/*
 * Copyright (c) 1996 Michael A. Perzl
 * All rights reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without fee,
 * 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 Michael A. Perzl not
 * be used in advertising or publicity pertaining to distribution of
 * the software without specific, written prior permission.
 * Michael A. Perzl makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is" without any
 * express or implied warranties, including, without limitation, the
 * implied warranties of merchantability and fitness for a particular
 * purpose.
 *
 * Michael A. Perzl DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS.  IN NO EVENT SHALL Michael A. Perzl BE LIABLE FOR ANY
 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */



/* include standard header files */

#include <stdio.h>


/* include X and Motif header files */

#include <X11/Xos.h>
#include <Xm/XmP.h>

#if (XmVersion >= 1002)
#include <Xm/DrawP.h>
#endif

#include "ProgressMeterP.h"



static XtResource resources[] = {

  { XmNvalue, XmCValue, XmRInt, sizeof (int),
    XtOffset (XmProgressMeterWidget, progressMeter.value),
    XmRImmediate, (XtPointer) 50 },

  { XmNminimum, XmCValue, XmRInt, sizeof (int),
    XtOffset (XmProgressMeterWidget, progressMeter.minimum),
    XmRImmediate, (XtPointer) 0 },

  { XmNmaximum, XmCValue, XmRInt, sizeof (int),
    XtOffset (XmProgressMeterWidget, progressMeter.maximum),
    XmRImmediate, (XtPointer) 100 },

  { XmNboxMode, XmCBoxMode,
    XmRBoolean, sizeof (Boolean),
    XtOffset (XmProgressMeterWidget, progressMeter.boxMode),
    XmRImmediate, (XtPointer) FALSE },

  { XmNnumBoxes, XmCValue,
    XmRInt, sizeof (int),
    XtOffset (XmProgressMeterWidget, progressMeter.numBoxes),
    XmRImmediate, (XtPointer) 10 },

  { XmNorientation, XmCOrientation,
    XmROrientation, sizeof (unsigned char),
    XtOffset (XmProgressMeterWidget, progressMeter.orientation),
    XmRImmediate, (XtPointer) XmHORIZONTAL },

  { XmNshowBoxSeparators, XmCShowBoxSeparators,
    XmRBoolean, sizeof (Boolean),
    XtOffset (XmProgressMeterWidget, progressMeter.showBoxSeparators),
    XmRImmediate, (XtPointer) TRUE },

  { XmNshowPercentage, XmCShowPercentage,
    XmRBoolean, sizeof (Boolean),
    XtOffset (XmProgressMeterWidget, progressMeter.showPercentage),
    XmRImmediate, (XtPointer) TRUE },

  { XmNshowTime, XmCShowTime,
    XmRBoolean, sizeof (Boolean),
    XtOffset (XmProgressMeterWidget, progressMeter.showTime),
    XmRImmediate, (XtPointer) FALSE },

  { XmNshowTimeUnit, XmCShowTimeUnit,
    XmRBoolean, sizeof (Boolean),
    XtOffset (XmProgressMeterWidget, progressMeter.showTimeUnit),
    XmRImmediate, (XtPointer) TRUE },

  { XmNfontList, XmCFontList,
    XmRFontList, sizeof (XmFontList),
    XtOffset (XmProgressMeterWidget, progressMeter.fontList),
    XmRString, (XtPointer) "Fixed" }

};



/* if Motif version is >= 1.2 we can use the new international */
/* Motif fontlist default used to set up XmStrings             */

#if (XmVersion >= 1002)

XmStringCharSet XmCHARSET = (XmStringCharSet) XmFONTLIST_DEFAULT_TAG;

#else

XmStringCharSet XmCHARSET = (XmStringCharSet) XmSTRING_DEFAULT_CHARSET;

#endif



#define MAX_BOX_POS 200

#define MIN_BOX_LENGTH 5

#define TIME_STRING_OFFSET 3



#ifdef _NO_PROTO
static void getGCs (widget)
  Widget widget;
#else
static void getGCs (Widget widget)
#endif
{
  XmProgressMeterWidget w = (XmProgressMeterWidget) widget;

  XGCValues values;
  XtGCMask  valueMask;

  XmFontContext  fontContext;
  XmFontType     fontType;
  XtPointer      pointer;
  XFontSet      *fontSet;


/* set the value mask */

  valueMask = GCForeground | GCBackground;


/* set the values for the foreground and background color */

  values.foreground = w->primitive.foreground;
  values.background = w->core.background_pixel;


/* get the GC for the drawing operations */

  w->progressMeter.gc = XtGetGC (widget, valueMask, &values);


/* set the value mask */

  valueMask = GCFunction | GCForeground | GCBackground | GCFont;


/* set the values for the function, foreground and background color */

  values.function   = GXxor;
  values.background = w->core.background_pixel;
  values.foreground = w->primitive.foreground ^ values.background;


/* set the value for the font */

  (void) XmFontListInitFontContext (&fontContext,
                                    w->progressMeter.fontList);

  pointer = XmFontListEntryGetFont (XmFontListNextEntry (fontContext),
                                    &fontType);

  if (fontType == XmFONT_IS_FONT)
    values.font = ((XFontStruct *) pointer)->fid;
  else
  if (fontType == XmFONT_IS_FONTSET)
  {
    fontSet = (XFontSet *) pointer;

    values.font = XLoadFont (XtDisplay (widget),
                             XBaseFontNameListOfFontSet (*fontSet));
  }


/* get the GC for the text operations */

  w->progressMeter.invgc = XtGetGC (widget, valueMask, &values);


/* free the font context stuff */

  XmFontListFreeFontContext (fontContext);
}



#ifdef _NO_PROTO
static void CalcBoxPositions (widget)
  Widget widget;
#else
static void CalcBoxPositions (Widget widget)
#endif
{
  int       k;
  Dimension sht;
  double    boxLength;

  XmProgressMeterWidget w = (XmProgressMeterWidget) widget;


  sht = w->primitive.shadow_thickness;

  if (w->progressMeter.orientation == XmHORIZONTAL)
  {
    w->progressMeter.boxPos[0] = sht;

    boxLength = (widget->core.width -
                         2.0 * (sht + w->progressMeter.numBoxes - 1))
                  / w->progressMeter.numBoxes;

    for (k = 1;  k <= w->progressMeter.numBoxes;  k++)
      w->progressMeter.boxPos[k] = 
              w->progressMeter.boxPos[0] + (int) (k * (2 + boxLength));
  }
  else   /* XmVERTICAL */
  {
    w->progressMeter.boxPos[0] = widget->core.height;

    boxLength = (widget->core.height -
                         2.0 * (sht + w->progressMeter.numBoxes - 1))
                  / w->progressMeter.numBoxes;

    for (k = 1;  k <= w->progressMeter.numBoxes;  k++)
      w->progressMeter.boxPos[k] =
              w->progressMeter.boxPos[0] - (int) (k * (2 + boxLength));
  }
}



#ifdef _NO_PROTO
static void CheckNumBoxes (w)
  XmProgressMeterWidget w;
#else
static void CheckNumBoxes (XmProgressMeterWidget w)
#endif
{
  int  dim, nBox;
  char textBuf[80];


/* set a reasonable 'numBoxes' value */

  if (w->progressMeter.orientation == XmHORIZONTAL)
    dim = w->core.width;
  else
    dim = w->core.height;

  nBox = w->progressMeter.numBoxes;

  if (nBox > MAX_BOX_POS) nBox = MAX_BOX_POS;

  if (dim < nBox * MIN_BOX_LENGTH + (nBox - 1) * 2)
    nBox = dim / (MIN_BOX_LENGTH + 2);

  if (nBox < 2) nBox = 2;

  if (nBox != w->progressMeter.numBoxes)
  {
    sprintf (textBuf, "ProgressMeter: numBoxes set to %d", nBox);
    XtWarning (textBuf);
    w->progressMeter.numBoxes = nBox;
  }
}



#ifdef _NO_PROTO
static void Initialize (req, widget, args, numArgs)
  Widget    req,
            widget;
  ArgList   args;
  Cardinal *numArgs;
#else
static void Initialize (Widget req, Widget widget,
                        ArgList args, Cardinal *numArgs)
#endif
{
  XmProgressMeterWidget w = (XmProgressMeterWidget) widget;


/* make sure the window size is not zero.
 * The Core Initialize() method doesn't do this. */

  if (widget->core.width == 0) widget->core.width = 100;

  if (widget->core.height == 0) widget->core.height = 50;


/* make sure the min and max dial settings are valid */

  if (w->progressMeter.minimum >= w->progressMeter.maximum)
  {
    XtWarning ("ProgressMeter: Maximum must be greater than Minimum");
    w->progressMeter.minimum = w->progressMeter.maximum - 1;
  }

  if (w->progressMeter.value > w->progressMeter.maximum)
  {
    XtWarning ("ProgressMeter: Value exceeds the Maximum");
    w->progressMeter.value = w->progressMeter.maximum;
  }

  if (w->progressMeter.value < w->progressMeter.minimum)
  {
    XtWarning ("ProgressMeter: Value is less than Minimum");
    w->progressMeter.value = w->progressMeter.minimum;
  }


/* set a reasonable 'numBoxes' value */

  CheckNumBoxes (w);


/* create a graphics context used to draw the bar & font */

  getGCs (widget);


/* allocate the memory for the box positions */

  w->progressMeter.boxPos = (Dimension *) XtMalloc (MAX_BOX_POS);


/* calculate the box positions if boxMode == TRUE */

  if (w->progressMeter.boxMode) CalcBoxPositions (widget);


/* remember the widget creation time */

  time (&w->progressMeter.startTime);
}



#ifdef _NO_PROTO
static void Destroy (widget)
  Widget widget;
#else
static void Destroy (Widget widget)
#endif
{
  XmProgressMeterWidget w = (XmProgressMeterWidget) widget;


  XtReleaseGC (widget, w->progressMeter.gc);
  XtReleaseGC (widget, w->progressMeter.invgc);

  XtFree ((char *) w->progressMeter.boxPos);
}



#ifdef _NO_PROTO
static void Resize (widget)
  Widget widget;
#else
static void Resize (Widget widget)
#endif
{

}



#ifdef _NO_PROTO
static void DrawPercentage (w, ratio)
  XmProgressMeterWidget w;
  double                ratio;
#else
static void DrawPercentage (XmProgressMeterWidget w, double ratio)
#endif
{
  XmString  valStr;
  Dimension width, height;
  int       xP, yP;
  char      textBuf[10];


/* format the percentage string */

  sprintf (textBuf, "%3.1f%%", ratio * 100.0);


/* create a Motif string */

  valStr = XmStringCreateLtoR (textBuf, XmCHARSET);


/* calculate the X and Y position for the output string */

  width  = XmStringWidth  (w->progressMeter.fontList, valStr);
  height = XmStringHeight (w->progressMeter.fontList, valStr);

  xP = (w->core.width  - width)  / 2;
  yP = (w->core.height - height) / 2;


/* display the output string */

  XmStringDraw (XtDisplay (w), XtWindow (w),
                w->progressMeter.fontList, valStr,
                w->progressMeter.invgc,
                xP, yP, width,
                XmALIGNMENT_BEGINNING,
                XmSTRING_DIRECTION_L_TO_R, NULL);


/* free the Motif string */

  XmStringFree (valStr);
}



#ifdef _NO_PROTO
static void DrawTime (leftFlag, w, diffTime, ratio)
  Boolean               leftFlag;
  XmProgressMeterWidget w;
  time_t                diffTime;
  double                ratio;
#else
static void DrawTime (Boolean leftFlag, XmProgressMeterWidget w,
                      time_t diffTime, double ratio)
#endif
{
  XmString  valStr;
  Dimension sht, width, height;
  int       k, xP, yP;
  char      textBuf[20];


/* we have to calculate the remaining time */

  if (! leftFlag)
  {
    diffTime *= (1.0 - ratio) / ratio;

    if ((diffTime == 0) && (ratio != 1.0))
      diffTime = 1;
  }


/* format the left time string */

  if (! w->progressMeter.showTimeUnit)
  {
    sprintf (textBuf, "%d:%2d", diffTime / 60, diffTime % 60);

    k = strlen (textBuf);
    if (textBuf[k - 2] == ' ') textBuf[k - 2] = '0';
  }
  else
  if (diffTime > 59)
  {
    sprintf (textBuf, "%d:%2d min", diffTime / 60, diffTime % 60);

    k = strlen (textBuf);
    if (textBuf[k - 6] == ' ') textBuf[k - 6] = '0';
  }
  else
    sprintf (textBuf, "%d sec", diffTime);


  sht = w->primitive.shadow_thickness;


/* create a Motif string */

  valStr = XmStringCreateLtoR (textBuf, XmCHARSET);


/* calculate the X and Y position for the output string */

  width  = XmStringWidth  (w->progressMeter.fontList, valStr);
  height = XmStringHeight (w->progressMeter.fontList, valStr);

  if (leftFlag)
    if (w->progressMeter.orientation == XmHORIZONTAL)
    {
      xP = sht + TIME_STRING_OFFSET;
      yP = (w->core.height - height) / 2;
    }
    else    /* XmVERTICAL */
    {
      xP = (w->core.width - width) / 2;
      yP = w->core.height - height - sht - TIME_STRING_OFFSET;
    }
  else
    if (w->progressMeter.orientation == XmHORIZONTAL)
    {
      xP = w->core.width - width - sht - TIME_STRING_OFFSET;
      yP = (w->core.height - height) / 2;
    }
    else    /* XmVERTICAL */
    {
      xP = (w->core.width - width) / 2;
      yP = sht + TIME_STRING_OFFSET;
    }


/* display the output string */

  XmStringDraw (XtDisplay (w), XtWindow (w),
                w->progressMeter.fontList, valStr,
                w->progressMeter.invgc,
                xP, yP, w->core.width,
                XmALIGNMENT_BEGINNING,
                XmSTRING_DIRECTION_L_TO_R, NULL);


/* free the Motif string */

  XmStringFree (valStr);
}



#ifdef _NO_PROTO
static void DrawSlider (w, expose, oldValue)
  Widget  w;
  Boolean expose;
  int     oldValue;
#else
static void DrawSlider (XmProgressMeterWidget w, Boolean expose,
                        int oldValue)
#endif
{
  Dimension  size, sht, oldSize,
            *boxPos;
  double     ratio, oldRatio;
  int        nBox, nBoxOld, k;
  time_t     diffTime;


  if (! XtIsRealized ((Widget) w))
    return;

  sht = w->primitive.shadow_thickness;

  boxPos = w->progressMeter.boxPos;

  ratio = (double)
      ((double) (w->progressMeter.value - w->progressMeter.minimum)) / 
      ((double) (w->progressMeter.maximum - w->progressMeter.minimum));

  oldRatio = (double)
      ((double) (oldValue - w->progressMeter.minimum)) / 
      ((double) (w->progressMeter.maximum - w->progressMeter.minimum));


/* do this to avoid race conditions with erasing old stuff ... */

  if ((ratio == 0.0) || (oldRatio == 0.0)) expose = TRUE;


/* erase the old percentage if necessary */

  if (w->progressMeter.showPercentage
        && (! w->progressMeter.boxMode) && (! expose))
    DrawPercentage (w, oldRatio);


/* erase the old time if necessary */

  if (w->progressMeter.showTime
        && (! w->progressMeter.boxMode) && (! expose))
  {
    diffTime = w->progressMeter.lastTime - w->progressMeter.startTime;


    if ((ratio != 0.0) && (ratio != 1.0))
    {
/* draw the left time string */

      DrawTime (TRUE, w, diffTime, oldRatio);


/* draw the right time string */

      DrawTime (FALSE, w, diffTime, oldRatio);
    }
  }


/* draw the "slider" */

  if (w->progressMeter.boxMode)
  {
    nBox    = (int) (ratio    * w->progressMeter.numBoxes);
    nBoxOld = (int) (oldRatio * w->progressMeter.numBoxes);

    switch (w->progressMeter.orientation)
    {
      case XmHORIZONTAL:
          if (expose)
          {
            if (w->progressMeter.showBoxSeparators)
              for (k = 1;  k < w->progressMeter.numBoxes;  k++)
              {
                XDrawLine (XtDisplay (w), XtWindow (w),
                           w->primitive.top_shadow_GC,
                           boxPos[k] - 2, sht,
                           boxPos[k] - 2, w->core.height - sht);
  
                XDrawLine (XtDisplay (w), XtWindow (w),
                           w->primitive.bottom_shadow_GC,
                           boxPos[k] - 1, sht,
                           boxPos[k] - 1, w->core.height - sht);
              }

            for (k = 0;  k < nBox;  k++)
              XFillRectangle (XtDisplay (w), XtWindow (w),
                              w->progressMeter.gc,
                              boxPos[k], sht,
                              boxPos[k+1] - boxPos[k] - 2,
                              w->core.height - 2 * sht);

            if (w->core.height - 2 * sht > 0)
              for (k = nBox;  k < w->progressMeter.numBoxes;  k++)
                XClearArea (XtDisplay (w), XtWindow (w),
                            boxPos[k], sht,
                            boxPos[k+1] - boxPos[k] - 2,
                            w->core.height - 2 * sht,
                            FALSE);
          }
          else
          if (nBox > nBoxOld)
            for (k = nBoxOld;  k < nBox;  k++)
              XFillRectangle (XtDisplay (w), XtWindow (w),
                              w->progressMeter.gc,
                              boxPos[k], sht,
                              boxPos[k+1] - boxPos[k] - 2,
                              w->core.height - 2 * sht);
          else
          if (nBoxOld > nBox)
            if (w->core.height - 2 * sht > 0)
              for (k = nBoxOld;  k >= nBox;  k--)
                XClearArea (XtDisplay (w), XtWindow (w),
                            boxPos[k], sht,
                            boxPos[k+1] - boxPos[k] - 2,
                            w->core.height - 2 * sht,
                            FALSE);

          break;

      case XmVERTICAL:
          if (expose)
          {
            if (w->progressMeter.showBoxSeparators)
              for (k = 1;  k < w->progressMeter.numBoxes;  k++)
              {
                XDrawLine (XtDisplay (w), XtWindow (w),
                           w->primitive.top_shadow_GC,
                           sht, boxPos[k] - 2,
                           w->core.width - sht, boxPos[k] - 2);
   
                XDrawLine (XtDisplay (w), XtWindow (w),
                           w->primitive.bottom_shadow_GC,
                           sht, boxPos[k] - 1,
                           w->core.width - sht, boxPos[k] - 1);
              }

            if (w->core.width - 2 * sht > 0)
              for (k = nBox;  k < w->progressMeter.numBoxes;  k++)
                XClearArea (XtDisplay (w), XtWindow (w),
                            sht, boxPos[k+1],
                            w->core.width - 2 * sht,
                            boxPos[k] - boxPos[k+1] - 2,
                            FALSE);

            for (k = 0;  k < nBox;  k++)
              XFillRectangle (XtDisplay (w), XtWindow (w),
                              w->progressMeter.gc,
                              sht, boxPos[k+1],
                              w->core.width - 2 * sht,
                              boxPos[k] - boxPos[k+1] - 2);
          }
          else
          if (nBox > nBoxOld)
            for (k = nBoxOld;  k < nBox;  k++)
              XFillRectangle (XtDisplay (w), XtWindow (w),
                              w->progressMeter.gc,
                              sht, boxPos[k+1],
                              w->core.width - 2 * sht,
                              boxPos[k] - boxPos[k+1] - 2);
          else
          if (nBoxOld > nBox)
            if (w->core.width - 2 * sht)
              for (k = nBoxOld;  k >= nBox;  k--)
                XClearArea (XtDisplay (w), XtWindow (w),
                            sht, boxPos[k+1],
                            w->core.width - 2 * sht,
                            boxPos[k] - boxPos[k+1] - 2,
                            FALSE);

          break;

      default: XtWarning ("XmProgressMeter: No orientation defined !");
               return;
    }
  }

  else    /* boxMode == FALSE */

    switch (w->progressMeter.orientation) 
    {
      case XmHORIZONTAL:
          size    = (w->core.width - 2 * sht) * ratio;
          oldSize = (w->core.width - 2 * sht) * oldRatio;

          if ((! expose) && (size == oldSize)) break;

          if (expose)
          {
            XFillRectangle (XtDisplay (w), XtWindow (w),
                            w->progressMeter.gc,
                            sht, sht,
                            size, w->core.height - 2 * sht);

            if ((w->core.width - 2 * sht - size > 0) &&
                (w->core.height - 2 * sht > 0))
              XClearArea (XtDisplay (w), XtWindow (w),
                          sht + size, sht,
                          w->core.width - 2 * sht - size,
                          w->core.height - 2 * sht,
                          FALSE);
          }
          else   /* no expose event, only do one action */
          if (size > oldSize)
            XFillRectangle (XtDisplay (w), XtWindow (w),
                            w->progressMeter.gc,
                            sht + oldSize, sht,
                            size - oldSize, w->core.height - 2 * sht);
          else
            if ((oldSize - size > 0) && (w->core.height - 2 * sht > 0))
              XClearArea (XtDisplay (w), XtWindow (w),
                          sht + size, sht,
                          oldSize - size, w->core.height - 2 * sht,
                          FALSE);
  		    
          break;
  
      case XmVERTICAL:
          size    = (w->core.height - 2 * sht) * ratio;
          oldSize = (w->core.height - 2 * sht) * oldRatio;

          if ((! expose) && (size == oldSize)) break;

          if (expose)
          {
            if ((w->core.width - 2 * sht > 0) &&
                (w->core.height - 2 * sht - size > 0))
              XClearArea (XtDisplay (w), XtWindow (w),
                          sht, sht,
                          w->core.width - 2 * sht,
                          w->core.height - 2 * sht - size,
                          FALSE);

            XFillRectangle (XtDisplay (w), XtWindow (w),
                            w->progressMeter.gc,
                            sht, w->core.height - sht - size,
                            w->core.width - 2 * sht, size);
          }
          else    /* no expose event, only one action */
          if (size > oldSize)
            XFillRectangle (XtDisplay (w), XtWindow (w),
                            w->progressMeter.gc,
                            sht, w->core.height - sht - size,
                            w->core.width - 2 * sht, size - oldSize);
          else
            if ((w->core.width - 2 * sht > 0) && (oldSize - size > 0))
              XClearArea (XtDisplay (w), XtWindow (w),
                          sht, w->core.height - sht - oldSize,
                          w->core.width - 2 * sht, oldSize - size,
                          FALSE);

          break;
  
      default: XtWarning ("XmProgressMeter: No orientation defined !");
               return;
    }


/* display the percentage if necessary */

  if (w->progressMeter.showPercentage && (! w->progressMeter.boxMode))
    DrawPercentage (w, ratio);


/* display the time if necessary */

  if (w->progressMeter.showTime && (! w->progressMeter.boxMode))
  {
    time (&w->progressMeter.lastTime);

    diffTime = w->progressMeter.lastTime - w->progressMeter.startTime;


    if ((ratio != 0.0) && (ratio != 1.0))
    {
/* draw the left time string */

      DrawTime (TRUE, w, diffTime, ratio);


/* draw the right time string */

      DrawTime (FALSE, w, diffTime, ratio);
    }
  }
}



#ifdef _NO_PROTO
static void ExposeProc (widget, event, r)
  Widget  widget;
  XEvent *event;
  Region  r;
#else
static void ExposeProc (Widget widget, XEvent *event, Region r)
#endif
{
  XmProgressMeterWidget w = (XmProgressMeterWidget) widget;


#if (XmVersion >= 1002)
  _XmDrawShadows (XtDisplay (widget), XtWindow (widget),
                  w->primitive.top_shadow_GC,
                  w->primitive.bottom_shadow_GC,
                  0, 0, widget->core.width, widget->core.height,
                  w->primitive.shadow_thickness, XmSHADOW_IN);
#else
  _XmDrawShadow (XtDisplay (widget), XtWindow (widget),
                 w->primitive.top_shadow_GC,
                 w->primitive.bottom_shadow_GC,
                 w->primitive.shadow_thickness,
                 0, 0, widget->core.width, widget->core.height);
#endif

  DrawSlider (w, TRUE, 0);
}



#ifdef _NO_PROTO
static Boolean SetValues (cur, req, widget, args, numArgs)
  Widget    cur,
            req,
            widget;
  ArgList   args;
  Cardinal *numArgs;
#else
static Boolean SetValues (Widget cur, Widget req, Widget widget,
                          ArgList args, Cardinal *numArgs)
#endif
{
  XmProgressMeterWidget current = (XmProgressMeterWidget) cur;
  XmProgressMeterWidget request = (XmProgressMeterWidget) req;
  XmProgressMeterWidget w = (XmProgressMeterWidget) widget;

  Boolean redraw = FALSE;


/* make sure the progress meter values are reasonable */

  if (w->progressMeter.minimum > w->progressMeter.maximum)
  {
    XtWarning ("ProgressMeter: Minimum must be less than Maximum");
    w->progressMeter.minimum = 0;
    w->progressMeter.maximum = 100;
  }

  if (w->progressMeter.value > w->progressMeter.maximum)
  {
    XtWarning ("ProgressMeter: Value is greater than the Maximum");
    w->progressMeter.value = w->progressMeter.maximum;
  }

  if (w->progressMeter.value < w->progressMeter.minimum)
  {
    XtWarning ("ProgressMeter: Value is less than the Minimum");
    w->progressMeter.value = w->progressMeter.minimum;
  }


/* if foreground or background color or the used font have changed,
 * generate the GCs */

  if ((w->primitive.foreground != current->primitive.foreground) ||
      (w->core.background_pixel != current->core.background_pixel) ||
      (w->progressMeter.fontList != current->progressMeter.fontList))
  {
    XtReleaseGC ((Widget) current, current->progressMeter.gc);
    XtReleaseGC ((Widget) current, current->progressMeter.invgc);

    getGCs ((Widget) w);

    redraw = TRUE;
  }


/* if the progress meter value has changed ... */

  if (w->progressMeter.value != current->progressMeter.value)
    redraw = TRUE;


/* if the progress meter style has changed ... */

  if (w->progressMeter.boxMode != current->progressMeter.boxMode)
    redraw = TRUE;

  if ((w->progressMeter.boxMode && (! current->progressMeter.boxMode))
      || (w->progressMeter.numBoxes != current->progressMeter.numBoxes))
    CheckNumBoxes (w);


/* if the display of the progress has changed ... */

  if ((w->progressMeter.showPercentage !=
                              current->progressMeter.showPercentage) ||
      (w->progressMeter.showTime != current->progressMeter.showTime) ||
      (w->progressMeter.showTimeUnit !=
                                current->progressMeter.showTimeUnit))
    redraw = TRUE;


/* return if we have to redraw the progress meter ... */

  return (redraw);
}



XmProgressMeterClassRec xmProgressMeterClassRec = {
                                       /* core fields            */
{
  (WidgetClass) &xmPrimitiveClassRec,  /* superclass             */
  "XmProgressMeter",                   /* class_name             */
  sizeof (XmProgressMeterRec),         /* widget_size            */
  NULL,                                /* class_initialize       */
  NULL,                                /* class_part_initialize  */
  FALSE,                               /* class_inited           */
  Initialize,                          /* initialize             */
  NULL,                                /* initialize_hook        */
  XtInheritRealize,                    /* realize                */
  NULL,                                /* actions                */
  0,                                   /* num_actions            */
  resources,                           /* resources              */
  XtNumber (resources),                /* num_resources          */
  NULLQUARK,                           /* xrm_class              */
  TRUE,                                /* compress_motion        */
  XtExposeCompressMaximal,             /* compress_exposure      */
  TRUE,                                /* compress_enterleave    */
  TRUE,                                /* visible_interest       */
  Destroy,                             /* destroy                */
  Resize,                              /* resize                 */
  ExposeProc,                          /* expose                 */
  SetValues,                           /* set_values             */
  NULL,                                /* set_values_hook        */
  XtInheritSetValuesAlmost,            /* set_values_almost      */
  NULL,                                /* get_values_hook        */
  NULL,                                /* accept_focus           */
  XtVersion,                           /* version                */
  NULL,                                /* callback_private       */
  NULL,                                /* tm_table               */
  NULL,                                /* query_geometry         */
  NULL,                                /* display_accelerator    */
  NULL                                 /* extension              */
},

                                       /* primitive_class fields */
{
  NULL,                                /* border_highlight       */
  NULL,                                /* border_unhighlight     */
  NULL,                                /* translations           */
  NULL,                                /* arm_and_activate       */
  NULL,                                /* syn_resources          */
  0,                                   /* num_syn_resources      */
  NULL                                 /* extension              */
},

                                       /* progressMeter fields   */
{
  0                                    /* ignore                 */
}

};

WidgetClass xmProgressMeterWidgetClass = (WidgetClass) &xmProgressMeterClassRec;



#ifdef _NO_PROTO
Widget XmCreateProgressMeter (parent, name, args, numArgs)
  Widget   parent;
  String   name;
  ArgList  args;
  Cardinal numArgs;
#else
Widget XmCreateProgressMeter (Widget parent, String name,
                              ArgList args, Cardinal numArgs)
#endif
{
  return (XtCreateWidget (name, xmProgressMeterWidgetClass,
                          parent, args, numArgs));
}



#ifdef _NO_PROTO
void XmProgressMeterSetValue (widget, value)
  Widget widget;
  int    value;
#else
void XmProgressMeterSetValue (Widget widget, int value)
#endif
{
  int     oldValue;
  double  ratio, oldRatio;
  char    ratioStr[10], oldRatioStr[10];
  Boolean triggerExpose;


  XmProgressMeterWidget w = (XmProgressMeterWidget) widget;


  if (! XtIsRealized (widget)) return;

  if (! XtIsSubclass (widget, xmProgressMeterWidgetClass))
    return;

  oldValue = w->progressMeter.value;

  if (value > w->progressMeter.maximum)
    w->progressMeter.value = w->progressMeter.maximum;
  else
    if (value < w->progressMeter.minimum)
      w->progressMeter.value = w->progressMeter.minimum;
  else
    w->progressMeter.value = value;

  if (w->progressMeter.value == oldValue) return;


  triggerExpose = FALSE;

/* we have to generate an expose event in case the string length
 * gets smaller (eg. from '10.0%' to '9.9%') */

  ratio = (double)
      ((double) (w->progressMeter.value - w->progressMeter.minimum)) / 
      ((double) (w->progressMeter.maximum - w->progressMeter.minimum));

  oldRatio = (double)
      ((double) (oldValue - w->progressMeter.minimum)) / 
      ((double) (w->progressMeter.maximum - w->progressMeter.minimum));

  sprintf (ratioStr,    "%3.1f%%", ratio    * 100.0);
  sprintf (oldRatioStr, "%3.1f%%", oldRatio * 100.0);

  
  if (strlen (ratioStr) != strlen (oldRatioStr))
    triggerExpose = TRUE;


  DrawSlider (w, triggerExpose, oldValue);
}



#ifdef _NO_PROTO
int XmProgressMeterGetValue (widget)
  Widget widget;
#else
int XmProgressMeterGetValue (Widget widget)
#endif
{	
  XmProgressMeterWidget w = (XmProgressMeterWidget) widget;


  if (! XtIsRealized (widget)) return (-1);

  if (! XtIsSubclass (widget, xmProgressMeterWidgetClass))
    return (-1);

  return (w->progressMeter.value);
}

