home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / tcl / tkstep0.3b3 / tkstep0 / tkstep / tkScale.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-08  |  70.4 KB  |  2,225 lines

  1. /* 
  2.  * tkScale.c --
  3.  *
  4.  *    This module implements a scale widgets for the Tk toolkit.
  5.  *    A scale displays a slider that can be adjusted to change a
  6.  *    value;  it also displays numeric labels and a textual label,
  7.  *    if desired.
  8.  *    
  9.  *    The modifications to use floating-point values are based on
  10.  *    an implementation by Paul Mackerras.  The -variable option
  11.  *    is due to Henning Schulzrinne.  All of these are used with
  12.  *    permission.
  13.  *
  14.  * Copyright (c) 1990-1994 The Regents of the University of California.
  15.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  16.  *
  17.  * TkStep modifications (c) 1996 Andy Lo A Foe
  18.  *
  19.  * See the file "license.terms" for information on usage and redistribution
  20.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  21.  *
  22.  * SCCS: @(#) tkScale.c 1.80 96/03/21 13:11:55
  23.  */
  24.  
  25. #include "tkPort.h"
  26. #include "default.h"
  27. #include "tkInt.h"
  28. #include <math.h>
  29.  
  30. /*
  31.  * A data structure of the following type is kept for each scale
  32.  * widget managed by this file:
  33.  */
  34.  
  35. typedef struct {
  36.     Tk_Window tkwin;        /* Window that embodies the scale.  NULL
  37.                  * means that the window has been destroyed
  38.                  * but the data structures haven't yet been
  39.                  * cleaned up.*/
  40.     Display *display;        /* Display containing widget.  Used, among
  41.                  * other things, so that resources can be
  42.                  * freed even after tkwin has gone away. */
  43.     Tcl_Interp *interp;        /* Interpreter associated with scale. */
  44.     Tcl_Command widgetCmd;    /* Token for scale's widget command. */
  45.     Tk_Uid orientUid;        /* Orientation for window ("vertical" or
  46.                  * "horizontal"). */
  47.     int vertical;        /* Non-zero means vertical orientation,
  48.                  * zero means horizontal. */
  49.     int width;            /* Desired narrow dimension of scale,
  50.                  * in pixels. */
  51.     int length;            /* Desired long dimension of scale,
  52.                  * in pixels. */
  53.     double value;        /* Current value of scale. */
  54.     char *varName;        /* Name of variable (malloc'ed) or NULL.
  55.                  * If non-NULL, scale's value tracks
  56.                  * the contents of this variable and
  57.                  * vice versa. */
  58.     double fromValue;        /* Value corresponding to left or top of
  59.                  * scale. */
  60.     double toValue;        /* Value corresponding to right or bottom
  61.                  * of scale. */
  62.     double tickInterval;    /* Distance between tick marks;  0 means
  63.                  * don't display any tick marks. */
  64.     double resolution;        /* If > 0, all values are rounded to an
  65.                  * even multiple of this value. */
  66.     int digits;            /* Number of significant digits to print
  67.                  * in values.  0 means we get to choose the
  68.                  * number based on resolution and/or the
  69.                  * range of the scale. */
  70.     char format[10];        /* Sprintf conversion specifier computed from
  71.                  * digits and other information. */
  72.     double bigIncrement;    /* Amount to use for large increments to
  73.                  * scale value.  (0 means we pick a value). */
  74.     char *command;        /* Command prefix to use when invoking Tcl
  75.                  * commands because the scale value changed.
  76.                  * NULL means don't invoke commands.
  77.                  * Malloc'ed. */
  78.     int repeatDelay;        /* How long to wait before auto-repeating
  79.                  * on scrolling actions (in ms). */
  80.     int repeatInterval;        /* Interval between autorepeats (in ms). */
  81.     char *label;        /* Label to display above or to right of
  82.                  * scale;  NULL means don't display a
  83.                  * label.  Malloc'ed. */
  84.     int labelLength;        /* Number of non-NULL chars. in label. */
  85.     Tk_Uid state;        /* Normal or disabled.  Value cannot be
  86.                  * changed when scale is disabled. */
  87.  
  88.     /*
  89.      * Information used when displaying widget:
  90.      */
  91.  
  92.     int borderWidth;        /* Width of 3-D border around window. */
  93.     Tk_3DBorder bgBorder;    /* Used for drawing slider and other
  94.                  * background areas. */
  95.     Tk_3DBorder activeBorder;    /* For drawing the slider when active. */
  96.     int sliderRelief;        /* Is slider to be drawn raised, sunken, etc. */
  97.     XColor *troughColorPtr;    /* Color for drawing trough. */
  98.     XColor *darkPtr;
  99.     GC troughGC;        /* For drawing trough. */
  100.     GC copyGC;            /* Used for copying from pixmap onto screen. */
  101.     GC troughBackGC;
  102.     XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  103.     XColor *textColorPtr;    /* Color for drawing text. */
  104.     GC textGC;            /* GC for drawing text in normal mode. */
  105.     int relief;            /* Indicates whether window as a whole is
  106.                  * raised, sunken, or flat. */
  107.     int highlightWidth;        /* Width in pixels of highlight to draw
  108.                  * around widget when it has the focus.
  109.                  * <= 0 means don't draw a highlight. */
  110.     Pixmap stipple;
  111.     XColor *highlightBgColorPtr;
  112.                 /* Color for drawing traversal highlight
  113.                  * area when highlight is off. */
  114.     XColor *highlightColorPtr;    /* Color for drawing traversal highlight. */
  115.     int inset;            /* Total width of all borders, including
  116.                  * traversal highlight and 3-D border.
  117.                  * Indicates how much interior stuff must
  118.                  * be offset from outside edges to leave
  119.                  * room for borders. */
  120.     int sliderLength;        /* Length of slider, measured in pixels along
  121.                  * long dimension of scale. */
  122.     int showValue;        /* Non-zero means to display the scale value
  123.                  * below or to the left of the slider;  zero
  124.                  * means don't display the value. */
  125.  
  126.     /*
  127.      * Layout information for horizontal scales, assuming that window
  128.      * gets the size it requested:
  129.      */
  130.  
  131.     int horizLabelY;        /* Y-coord at which to draw label. */
  132.     int horizValueY;        /* Y-coord at which to draw value text. */
  133.     int horizTroughY;        /* Y-coord of top of slider trough. */
  134.     int horizTickY;        /* Y-coord at which to draw tick text. */
  135.     /*
  136.      * Layout information for vertical scales, assuming that window
  137.      * gets the size it requested:
  138.      */
  139.  
  140.     int vertTickRightX;        /* X-location of right side of tick-marks. */
  141.     int vertValueRightX;    /* X-location of right side of value string. */
  142.     int vertTroughX;        /* X-location of scale's slider trough. */
  143.     int vertLabelX;        /* X-location of origin of label. */
  144.  
  145.     /*
  146.      * Miscellaneous information:
  147.      */
  148.  
  149.     Tk_Cursor cursor;        /* Current cursor for window, or None. */
  150.     char *takeFocus;        /* Value of -takefocus option;  not used in
  151.                  * the C code, but used by keyboard traversal
  152.                  * scripts.  Malloc'ed, but may be NULL. */
  153.     int flags;            /* Various flags;  see below for
  154.                  * definitions. */
  155. } Scale;
  156.  
  157. /*
  158.  * Flag bits for scales:
  159.  *
  160.  * REDRAW_SLIDER -        1 means slider (and numerical readout) need
  161.  *                to be redrawn.
  162.  * REDRAW_OTHER -        1 means other stuff besides slider and value
  163.  *                need to be redrawn.
  164.  * REDRAW_ALL -            1 means the entire widget needs to be redrawn.
  165.  * ACTIVE -            1 means the widget is active (the mouse is
  166.  *                in its window).
  167.  * INVOKE_COMMAND -        1 means the scale's command needs to be
  168.  *                invoked during the next redisplay (the
  169.  *                value of the scale has changed since the
  170.  *                last time the command was invoked).
  171.  * SETTING_VAR -        1 means that the associated variable is
  172.  *                being set by us, so there's no need for
  173.  *                ScaleVarProc to do anything.
  174.  * NEVER_SET -            1 means that the scale's value has never
  175.  *                been set before (so must invoke -command and
  176.  *                set associated variable even if the value
  177.  *                doesn't appear to have changed).
  178.  * GOT_FOCUS -            1 means that the focus is currently in
  179.  *                this widget.
  180.  */
  181.  
  182. #define REDRAW_SLIDER        1
  183. #define REDRAW_OTHER        2
  184. #define REDRAW_ALL        3
  185. #define ACTIVE            4
  186. #define INVOKE_COMMAND        0x10
  187. #define SETTING_VAR        0x20
  188. #define NEVER_SET        0x40
  189. #define GOT_FOCUS        0x80
  190.  
  191. /*
  192.  * Symbolic values for the active parts of a slider.  These are
  193.  * the values that may be returned by the ScaleElement procedure.
  194.  */
  195.  
  196. #define OTHER        0
  197. #define TROUGH1        1
  198. #define SLIDER        2
  199. #define TROUGH2        3
  200.  
  201. /*
  202.  * Space to leave between scale area and text, and between text and
  203.  * edge of window.
  204.  */
  205.  
  206. #define SPACING 2
  207.  
  208. /*
  209.  * How many characters of space to provide when formatting the
  210.  * scale's value:
  211.  */
  212.  
  213. #define PRINT_CHARS 150
  214.  
  215.  
  216. /*
  217.  * If you want to make Tk ignore attribute settings that may be violating
  218.  * NEXTSTEP(tm) styles leave DISABLE_FORCE_STEP undef'ed, otherwise define it
  219.  * somewhere
  220.  */
  221. #ifndef DISABLE_FORCE_STEP
  222. #  define DISABLE_CHANGE    TK_CONFIG_DONT_CHANGE_DEFAULT
  223. #else
  224. #  define DISABLE_CHANGE    0
  225. #endif
  226.  
  227. /*
  228.  * Information used for argv parsing.
  229.  */
  230.  
  231. static Tk_ConfigSpec configSpecs[] = {
  232.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  233.     DEF_SCALE_ACTIVE_BG_COLOR, Tk_Offset(Scale, activeBorder),
  234.     TK_CONFIG_COLOR_ONLY | DISABLE_CHANGE},
  235.     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
  236.     DEF_SCALE_ACTIVE_BG_MONO, Tk_Offset(Scale, activeBorder),
  237.     TK_CONFIG_MONO_ONLY | DISABLE_CHANGE},
  238.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  239.     DEF_SCALE_BG_COLOR, Tk_Offset(Scale, bgBorder),
  240.     TK_CONFIG_COLOR_ONLY | DISABLE_CHANGE},
  241.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  242.     DEF_SCALE_BG_MONO, Tk_Offset(Scale, bgBorder),
  243.     TK_CONFIG_MONO_ONLY | DISABLE_CHANGE},
  244.     {TK_CONFIG_DOUBLE, "-bigincrement", "bigIncrement", "BigIncrement",
  245.     DEF_SCALE_BIG_INCREMENT, Tk_Offset(Scale, bigIncrement), 0},
  246.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  247.     (char *) NULL, 0, 0},
  248.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  249.     (char *) NULL, 0, 0},
  250.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  251.     DEF_SCALE_BORDER_WIDTH, Tk_Offset(Scale, borderWidth), DISABLE_CHANGE},
  252.     {TK_CONFIG_STRING, "-command", "command", "Command",
  253.     DEF_SCALE_COMMAND, Tk_Offset(Scale, command), TK_CONFIG_NULL_OK},
  254.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  255.     DEF_SCALE_CURSOR, Tk_Offset(Scale, cursor), TK_CONFIG_NULL_OK},
  256.     {TK_CONFIG_INT, "-digits", "digits", "Digits",
  257.     DEF_SCALE_DIGITS, Tk_Offset(Scale, digits), 0},
  258.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  259.     (char *) NULL, 0, 0},
  260.     {TK_CONFIG_FONT, "-font", "font", "Font",
  261.     DEF_SCALE_FONT, Tk_Offset(Scale, fontPtr),
  262.     0},
  263.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  264.     DEF_SCALE_FG_COLOR, Tk_Offset(Scale, textColorPtr),
  265.     TK_CONFIG_COLOR_ONLY | DISABLE_CHANGE},
  266.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  267.     DEF_SCALE_FG_MONO, Tk_Offset(Scale, textColorPtr),
  268.     TK_CONFIG_MONO_ONLY | DISABLE_CHANGE},
  269.     {TK_CONFIG_DOUBLE, "-from", "from", "From",
  270.     DEF_SCALE_FROM, Tk_Offset(Scale, fromValue), 0},
  271.     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
  272.     "HighlightBackground", DEF_SCALE_HIGHLIGHT_BG,
  273.     Tk_Offset(Scale, highlightBgColorPtr), DISABLE_CHANGE},
  274.     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
  275.     DEF_SCALE_HIGHLIGHT, Tk_Offset(Scale, highlightColorPtr), 0},
  276.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  277.     "HighlightThickness",
  278.     DEF_SCALE_HIGHLIGHT_WIDTH, Tk_Offset(Scale, highlightWidth), 
  279.     DISABLE_CHANGE},
  280.     {TK_CONFIG_STRING, "-label", "label", "Label",
  281.     DEF_SCALE_LABEL, Tk_Offset(Scale, label), TK_CONFIG_NULL_OK},
  282.     {TK_CONFIG_PIXELS, "-length", "length", "Length",
  283.     DEF_SCALE_LENGTH, Tk_Offset(Scale, length), 0},
  284.     {TK_CONFIG_UID, "-orient", "orient", "Orient",
  285.     DEF_SCALE_ORIENT, Tk_Offset(Scale, orientUid), 0},
  286.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  287.     DEF_SCALE_RELIEF, Tk_Offset(Scale, relief), DISABLE_CHANGE},
  288.     {TK_CONFIG_INT, "-repeatdelay", "repeatDelay", "RepeatDelay",
  289.     DEF_SCALE_REPEAT_DELAY, Tk_Offset(Scale, repeatDelay), 0},
  290.     {TK_CONFIG_INT, "-repeatinterval", "repeatInterval", "RepeatInterval",
  291.     DEF_SCALE_REPEAT_INTERVAL, Tk_Offset(Scale, repeatInterval), 0},
  292.     {TK_CONFIG_DOUBLE, "-resolution", "resolution", "Resolution",
  293.     DEF_SCALE_RESOLUTION, Tk_Offset(Scale, resolution), 0},
  294.     {TK_CONFIG_BOOLEAN, "-showvalue", "showValue", "ShowValue",
  295.     DEF_SCALE_SHOW_VALUE, Tk_Offset(Scale, showValue), 0},
  296.     {TK_CONFIG_PIXELS, "-sliderlength", "sliderLength", "SliderLength",
  297.     DEF_SCALE_SLIDER_LENGTH, Tk_Offset(Scale, sliderLength), 
  298.         DISABLE_CHANGE},
  299.     {TK_CONFIG_RELIEF, "-sliderrelief", "sliderRelief", "SliderRelief",
  300.     DEF_SCALE_SLIDER_RELIEF, Tk_Offset(Scale, sliderRelief),
  301.     TK_CONFIG_DONT_SET_DEFAULT| DISABLE_CHANGE},
  302.     {TK_CONFIG_UID, "-state", "state", "State",
  303.     DEF_SCALE_STATE, Tk_Offset(Scale, state), 0},
  304.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  305.     DEF_SCALE_TAKE_FOCUS, Tk_Offset(Scale, takeFocus),
  306.     TK_CONFIG_NULL_OK},
  307.     {TK_CONFIG_DOUBLE, "-tickinterval", "tickInterval", "TickInterval",
  308.     DEF_SCALE_TICK_INTERVAL, Tk_Offset(Scale, tickInterval), 0},
  309.     {TK_CONFIG_DOUBLE, "-to", "to", "To",
  310.     DEF_SCALE_TO, Tk_Offset(Scale, toValue), 0},
  311.     {TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
  312.     DEF_SCALE_TROUGH_COLOR, Tk_Offset(Scale, troughColorPtr),
  313.     TK_CONFIG_COLOR_ONLY | DISABLE_CHANGE},
  314.     {TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
  315.     DEF_SCALE_TROUGH_MONO, Tk_Offset(Scale, troughColorPtr),
  316.     TK_CONFIG_MONO_ONLY | DISABLE_CHANGE},
  317.     {TK_CONFIG_STRING, "-variable", "variable", "Variable",
  318.     DEF_SCALE_VARIABLE, Tk_Offset(Scale, varName), TK_CONFIG_NULL_OK},
  319.     {TK_CONFIG_PIXELS, "-width", "width", "Width",
  320.     DEF_SCALE_WIDTH, Tk_Offset(Scale, width), DISABLE_CHANGE},
  321.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  322.     (char *) NULL, 0, 0}
  323. };
  324.  
  325. /*
  326.  * Forward declarations for procedures defined later in this file:
  327.  */
  328.  
  329. static void        ComputeFormat _ANSI_ARGS_((Scale *scalePtr));
  330. static void        ComputeScaleGeometry _ANSI_ARGS_((Scale *scalePtr));
  331. static int        ConfigureScale _ANSI_ARGS_((Tcl_Interp *interp,
  332.                 Scale *scalePtr, int argc, char **argv,
  333.                 int flags));
  334. static void        DestroyScale _ANSI_ARGS_((char *memPtr));
  335. static void        DisplayScale _ANSI_ARGS_((ClientData clientData));
  336. static void        DisplayHorizontalScale _ANSI_ARGS_((Scale *scalePtr,
  337.                 Drawable drawable, XRectangle *drawnAreaPtr));
  338. static void        DisplayHorizontalValue _ANSI_ARGS_((Scale *scalePtr,
  339.                 Drawable drawable, double value, int top));
  340. static void        DisplayVerticalScale _ANSI_ARGS_((Scale *scalePtr,
  341.                 Drawable drawable, XRectangle *drawnAreaPtr));
  342. static void        DisplayVerticalValue _ANSI_ARGS_((Scale *scalePtr,
  343.                 Drawable drawable, double value, int rightEdge));
  344. static void        EventuallyRedrawScale _ANSI_ARGS_((Scale *scalePtr,
  345.                 int what));
  346. static double        PixelToValue _ANSI_ARGS_((Scale *scalePtr, int x,
  347.                 int y));
  348. static double        RoundToResolution _ANSI_ARGS_((Scale *scalePtr,
  349.                 double value));
  350. static void        ScaleCmdDeletedProc _ANSI_ARGS_((
  351.                 ClientData clientData));
  352. static int        ScaleElement _ANSI_ARGS_((Scale *scalePtr, int x,
  353.                 int y));
  354. static void        ScaleEventProc _ANSI_ARGS_((ClientData clientData,
  355.                 XEvent *eventPtr));
  356. static char *        ScaleVarProc _ANSI_ARGS_((ClientData clientData,
  357.                 Tcl_Interp *interp, char *name1, char *name2,
  358.                 int flags));
  359. static int        ScaleWidgetCmd _ANSI_ARGS_((ClientData clientData,
  360.                 Tcl_Interp *interp, int argc, char **argv));
  361. static void        SetScaleValue _ANSI_ARGS_((Scale *scalePtr,
  362.                 double value, int setVar, int invokeCommand));
  363. static int        ValueToPixel _ANSI_ARGS_((Scale *scalePtr, double value));
  364.  
  365. /*
  366.  *--------------------------------------------------------------
  367.  *
  368.  * Tk_ScaleCmd --
  369.  *
  370.  *    This procedure is invoked to process the "scale" Tcl
  371.  *    command.  See the user documentation for details on what
  372.  *    it does.
  373.  *
  374.  * Results:
  375.  *    A standard Tcl result.
  376.  *
  377.  * Side effects:
  378.  *    See the user documentation.
  379.  *
  380.  *--------------------------------------------------------------
  381.  */
  382.  
  383. int
  384. Tk_ScaleCmd(clientData, interp, argc, argv)
  385.     ClientData clientData;        /* Main window associated with
  386.                  * interpreter. */
  387.     Tcl_Interp *interp;        /* Current interpreter. */
  388.     int argc;            /* Number of arguments. */
  389.     char **argv;        /* Argument strings. */
  390. {
  391.     Tk_Window tkwin = (Tk_Window) clientData;
  392.     register Scale *scalePtr;
  393.     Tk_Window new;
  394.  
  395.     if (argc < 2) {
  396.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  397.         argv[0], " pathName ?options?\"", (char *) NULL);
  398.     return TCL_ERROR;
  399.     }
  400.  
  401.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  402.     if (new == NULL) {
  403.     return TCL_ERROR;
  404.     }
  405.  
  406.     /*
  407.      * Initialize fields that won't be initialized by ConfigureScale,
  408.      * or which ConfigureScale expects to have reasonable values
  409.      * (e.g. resource pointers).
  410.      */
  411.  
  412.     scalePtr = (Scale *) ckalloc(sizeof(Scale));
  413.     scalePtr->tkwin = new;
  414.     scalePtr->display = Tk_Display(new);
  415.     scalePtr->interp = interp;
  416.     scalePtr->widgetCmd = Tcl_CreateCommand(interp,
  417.         Tk_PathName(scalePtr->tkwin), ScaleWidgetCmd,
  418.         (ClientData) scalePtr, ScaleCmdDeletedProc);
  419.     scalePtr->orientUid = NULL;
  420.     scalePtr->vertical = 0;
  421.     scalePtr->width = 0;
  422.     scalePtr->length = 0;
  423.     scalePtr->value = 0;
  424.     scalePtr->varName = NULL;
  425.     scalePtr->fromValue = 0;
  426.     scalePtr->toValue = 0;
  427.     scalePtr->tickInterval = 0;
  428.     scalePtr->resolution = 1;
  429.     scalePtr->bigIncrement = 0.0;
  430.     scalePtr->command = NULL;
  431.     scalePtr->repeatDelay = 0;
  432.     scalePtr->repeatInterval = 0;
  433.     scalePtr->stipple = None;
  434.     scalePtr->darkPtr = NULL;
  435.     scalePtr->label = NULL;
  436.     scalePtr->labelLength = 0;
  437.     scalePtr->state = tkNormalUid;
  438.     scalePtr->borderWidth = 0;
  439.     scalePtr->bgBorder = NULL;
  440.     scalePtr->activeBorder = NULL;
  441.     scalePtr->sliderRelief = TK_RELIEF_RAISED;
  442.     scalePtr->troughColorPtr = NULL;
  443.     scalePtr->troughGC = None;
  444.     scalePtr->copyGC = None;
  445.     scalePtr->fontPtr = NULL;
  446.     scalePtr->textColorPtr = NULL;
  447.     scalePtr->troughBackGC = None;
  448.     scalePtr->textGC = None;
  449.     scalePtr->relief = TK_RELIEF_FLAT;
  450.     scalePtr->highlightWidth = 0;
  451.     scalePtr->highlightBgColorPtr = NULL;
  452.     scalePtr->highlightColorPtr = NULL;
  453.     scalePtr->inset = 0;
  454.     scalePtr->sliderLength = 0;
  455.     scalePtr->showValue = 0;
  456.     scalePtr->horizLabelY = 0;
  457.     scalePtr->horizValueY = 0;
  458.     scalePtr->horizTroughY = 0;
  459.     scalePtr->horizTickY = 0;
  460.     scalePtr->vertTickRightX = 0;
  461.     scalePtr->vertValueRightX = 0;
  462.     scalePtr->vertTroughX = 0;
  463.     scalePtr->vertLabelX = 0;
  464.     scalePtr->cursor = None;
  465.     scalePtr->takeFocus = NULL;
  466.     scalePtr->flags = NEVER_SET;
  467.  
  468.     Tk_SetClass(scalePtr->tkwin, "Scale");
  469.     Tk_CreateEventHandler(scalePtr->tkwin,
  470.         ExposureMask|StructureNotifyMask|FocusChangeMask,
  471.         ScaleEventProc, (ClientData) scalePtr);
  472.     if (ConfigureScale(interp, scalePtr, argc-2, argv+2, 0) != TCL_OK) {
  473.     goto error;
  474.     }
  475.  
  476.     interp->result = Tk_PathName(scalePtr->tkwin);
  477.     return TCL_OK;
  478.  
  479.     error:
  480.     Tk_DestroyWindow(scalePtr->tkwin);
  481.     return TCL_ERROR;
  482. }
  483.  
  484. /*
  485.  *--------------------------------------------------------------
  486.  *
  487.  * ScaleWidgetCmd --
  488.  *
  489.  *    This procedure is invoked to process the Tcl command
  490.  *    that corresponds to a widget managed by this module.
  491.  *    See the user documentation for details on what it does.
  492.  *
  493.  * Results:
  494.  *    A standard Tcl result.
  495.  *
  496.  * Side effects:
  497.  *    See the user documentation.
  498.  *
  499.  *--------------------------------------------------------------
  500.  */
  501.  
  502. static int
  503. ScaleWidgetCmd(clientData, interp, argc, argv)
  504.     ClientData clientData;        /* Information about scale
  505.                      * widget. */
  506.     Tcl_Interp *interp;            /* Current interpreter. */
  507.     int argc;                /* Number of arguments. */
  508.     char **argv;            /* Argument strings. */
  509. {
  510.     register Scale *scalePtr = (Scale *) clientData;
  511.     int result = TCL_OK;
  512.     size_t length;
  513.     int c;
  514.  
  515.     if (argc < 2) {
  516.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  517.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  518.     return TCL_ERROR;
  519.     }
  520.     Tcl_Preserve((ClientData) scalePtr);
  521.     c = argv[1][0];
  522.     length = strlen(argv[1]);
  523.     if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
  524.         && (length >= 2)) {
  525.     if (argc != 3) {
  526.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  527.             argv[0], " cget option\"",
  528.             (char *) NULL);
  529.         goto error;
  530.     }
  531.     result = Tk_ConfigureValue(interp, scalePtr->tkwin, configSpecs,
  532.         (char *) scalePtr, argv[2], 0);
  533.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  534.         && (length >= 3)) {
  535.     if (argc == 2) {
  536.         result = Tk_ConfigureInfo(interp, scalePtr->tkwin, configSpecs,
  537.             (char *) scalePtr, (char *) NULL, 0);
  538.     } else if (argc == 3) {
  539.         result = Tk_ConfigureInfo(interp, scalePtr->tkwin, configSpecs,
  540.             (char *) scalePtr, argv[2], 0);
  541.     } else {
  542.         result = ConfigureScale(interp, scalePtr, argc-2, argv+2,
  543.             TK_CONFIG_ARGV_ONLY);
  544.     }
  545.     } else if ((c == 'c') && (strncmp(argv[1], "coords", length) == 0)
  546.         && (length >= 3)) {
  547.     int x, y ;
  548.     double value;
  549.  
  550.     if ((argc != 2) && (argc != 3)) {
  551.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  552.             argv[0], " coords ?value?\"", (char *) NULL);
  553.         goto error;
  554.     }
  555.     if (argc == 3) {
  556.         if (Tcl_GetDouble(interp, argv[2], &value) != TCL_OK) {
  557.         goto error;
  558.         }
  559.     } else {
  560.         value = scalePtr->value;
  561.     }
  562.     if (scalePtr->vertical) {
  563.         x = scalePtr->vertTroughX + scalePtr->width/2
  564.             + scalePtr->borderWidth;
  565.         y = ValueToPixel(scalePtr, value);
  566.     } else {
  567.         x = ValueToPixel(scalePtr, value);
  568.         y = scalePtr->horizTroughY + scalePtr->width/2
  569.             + scalePtr->borderWidth;
  570.     }
  571.     sprintf(interp->result, "%d %d", x, y);
  572.     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
  573.     double value;
  574.     int x, y;
  575.  
  576.     if ((argc != 2) && (argc != 4)) {
  577.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  578.             argv[0], " get ?x y?\"", (char *) NULL);
  579.         goto error;
  580.     }
  581.     if (argc == 2) {
  582.         value = scalePtr->value;
  583.     } else {
  584.         if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
  585.             || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
  586.         goto error;
  587.         }
  588.         value = PixelToValue(scalePtr, x, y);
  589.     }
  590.     sprintf(interp->result, scalePtr->format, value);
  591.     } else if ((c == 'i') && (strncmp(argv[1], "identify", length) == 0)) {
  592.     int x, y, thing;
  593.  
  594.     if (argc != 4) {
  595.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  596.             argv[0], " identify x y\"", (char *) NULL);
  597.         goto error;
  598.     }
  599.     if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
  600.         || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
  601.         goto error;
  602.     }
  603.     thing = ScaleElement(scalePtr, x,y);
  604.     switch (thing) {
  605.         case TROUGH1:    interp->result = "trough1";    break;
  606.         case SLIDER:    interp->result = "slider";    break;
  607.         case TROUGH2:    interp->result = "trough2";    break;
  608.     }
  609.     } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
  610.     double value;
  611.  
  612.     if (argc != 3) {
  613.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  614.             argv[0], " set value\"", (char *) NULL);
  615.         goto error;
  616.     }
  617.     if (Tcl_GetDouble(interp, argv[2], &value) != TCL_OK) {
  618.         goto error;
  619.     }
  620.     if (scalePtr->state != tkDisabledUid) {
  621.         SetScaleValue(scalePtr, value, 1, 1);
  622.     }
  623.     } else {
  624.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  625.         "\": must be cget, configure, coords, get, identify, or set",
  626.         (char *) NULL);
  627.     goto error;
  628.     }
  629.     Tcl_Release((ClientData) scalePtr);
  630.     return result;
  631.  
  632.     error:
  633.     Tcl_Release((ClientData) scalePtr);
  634.     return TCL_ERROR;
  635. }
  636.  
  637. /*
  638.  *----------------------------------------------------------------------
  639.  *
  640.  * DestroyScale --
  641.  *
  642.  *    This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
  643.  *    to clean up the internal structure of a button at a safe time
  644.  *    (when no-one is using it anymore).
  645.  *
  646.  * Results:
  647.  *    None.
  648.  *
  649.  * Side effects:
  650.  *    Everything associated with the scale is freed up.
  651.  *
  652.  *----------------------------------------------------------------------
  653.  */
  654.  
  655. static void
  656. DestroyScale(memPtr)
  657.     char *memPtr;    /* Info about scale widget. */
  658. {
  659.     register Scale *scalePtr = (Scale *) memPtr;
  660.  
  661.     /*
  662.      * Free up all the stuff that requires special handling, then
  663.      * let Tk_FreeOptions handle all the standard option-related
  664.      * stuff.
  665.      */
  666.  
  667.     if (scalePtr->varName != NULL) {
  668.     Tcl_UntraceVar(scalePtr->interp, scalePtr->varName,
  669.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  670.         ScaleVarProc, (ClientData) scalePtr);
  671.     }
  672.     if (scalePtr->troughGC != None) {
  673.     Tk_FreeGC(scalePtr->display, scalePtr->troughGC);
  674.     }
  675.     if (scalePtr->copyGC != None) {
  676.     Tk_FreeGC(scalePtr->display, scalePtr->copyGC);
  677.     }
  678.     if (scalePtr->textGC != None) {
  679.     Tk_FreeGC(scalePtr->display, scalePtr->textGC);
  680.     }
  681.     if (scalePtr->darkPtr != NULL) {
  682.         Tk_FreeColor(scalePtr->darkPtr);
  683.     }
  684.     if (scalePtr->stipple != None) {
  685.        Tk_FreeBitmap(scalePtr->display, scalePtr->stipple);
  686.     }
  687.     if (scalePtr->troughBackGC != None) {
  688.         Tk_FreeGC(scalePtr->display, scalePtr->troughBackGC);
  689.     }
  690.     Tk_FreeOptions(configSpecs, (char *) scalePtr, scalePtr->display, 0);
  691.     ckfree((char *) scalePtr);
  692. }
  693.  
  694. /*
  695.  *----------------------------------------------------------------------
  696.  *
  697.  * ConfigureScale --
  698.  *
  699.  *    This procedure is called to process an argv/argc list, plus
  700.  *    the Tk option database, in order to configure (or
  701.  *    reconfigure) a scale widget.
  702.  *
  703.  * Results:
  704.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  705.  *    returned, then interp->result contains an error message.
  706.  *
  707.  * Side effects:
  708.  *    Configuration information, such as colors, border width,
  709.  *    etc. get set for scalePtr;  old resources get freed,
  710.  *    if there were any.
  711.  *
  712.  *----------------------------------------------------------------------
  713.  */
  714.  
  715. static int
  716. ConfigureScale(interp, scalePtr, argc, argv, flags)
  717.     Tcl_Interp *interp;        /* Used for error reporting. */
  718.     register Scale *scalePtr;    /* Information about widget;  may or may
  719.                  * not already have values for some fields. */
  720.     int argc;            /* Number of valid entries in argv. */
  721.     char **argv;        /* Arguments. */
  722.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  723. {
  724.     XGCValues gcValues;
  725.     GC newGC;
  726.     XColor newcolor;
  727.     size_t length;
  728.  
  729.     /*
  730.      * Eliminate any existing trace on a variable monitored by the scale.
  731.      */
  732.  
  733.     if (scalePtr->varName != NULL) {
  734.     Tcl_UntraceVar(interp, scalePtr->varName, 
  735.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  736.         ScaleVarProc, (ClientData) scalePtr);
  737.     }
  738.  
  739.  
  740.     if (Tk_StrictMotif(scalePtr->tkwin)) {
  741.     flags|=TK_CONFIG_CHECK_MY_FLAG;
  742.     }    
  743.     if (Tk_ConfigureWidget(interp, scalePtr->tkwin, configSpecs,
  744.         argc, argv, (char *) scalePtr, flags) != TCL_OK) {
  745.     return TCL_ERROR;
  746.     }
  747.  
  748.     /*
  749.      * If the scale is tied to the value of a variable, then set up
  750.      * a trace on the variable's value and set the scale's value from
  751.      * the value of the variable, if it exists.
  752.      */
  753.  
  754.     if (scalePtr->varName != NULL) {
  755.     char *stringValue, *end;
  756.     double value;
  757.  
  758.     stringValue = Tcl_GetVar(interp, scalePtr->varName, TCL_GLOBAL_ONLY);
  759.     if (stringValue != NULL) {
  760.         value = strtod(stringValue, &end);
  761.         if ((end != stringValue) && (*end == 0)) {
  762.         scalePtr->value = RoundToResolution(scalePtr, value);
  763.         }
  764.     }
  765.     Tcl_TraceVar(interp, scalePtr->varName,
  766.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  767.         ScaleVarProc, (ClientData) scalePtr);
  768.     }
  769.  
  770.     /*
  771.      * Several options need special processing, such as parsing the
  772.      * orientation and creating GCs.
  773.      */
  774.  
  775.     length = strlen(scalePtr->orientUid);
  776.     if (strncmp(scalePtr->orientUid, "vertical", length) == 0) {
  777.     scalePtr->vertical = 1;
  778.     } else if (strncmp(scalePtr->orientUid, "horizontal", length) == 0) {
  779.     scalePtr->vertical = 0;
  780.     } else {
  781.     Tcl_AppendResult(interp, "bad orientation \"", scalePtr->orientUid,
  782.         "\": must be vertical or horizontal", (char *) NULL);
  783.     return TCL_ERROR;
  784.     }
  785.  
  786.     scalePtr->fromValue = RoundToResolution(scalePtr, scalePtr->fromValue);
  787.     scalePtr->toValue = RoundToResolution(scalePtr, scalePtr->toValue);
  788.     scalePtr->tickInterval = RoundToResolution(scalePtr,
  789.         scalePtr->tickInterval);
  790.  
  791.     /*
  792.      * Make sure that the tick interval has the right sign so that
  793.      * addition moves from fromValue to toValue.
  794.      */
  795.  
  796.     if ((scalePtr->tickInterval < 0)
  797.         ^ ((scalePtr->toValue - scalePtr->fromValue) <  0)) {
  798.     scalePtr->tickInterval = -scalePtr->tickInterval;
  799.     }
  800.  
  801.     /*
  802.      * Set the scale value to itself;  all this does is to make sure
  803.      * that the scale's value is within the new acceptable range for
  804.      * the scale and reflect the value in the associated variable,
  805.      * if any.
  806.      */
  807.  
  808.     ComputeFormat(scalePtr);
  809.     SetScaleValue(scalePtr, scalePtr->value, 1, 1);
  810.  
  811.     if (scalePtr->label != NULL) {
  812.     scalePtr->labelLength = strlen(scalePtr->label);
  813.     } else {
  814.     scalePtr->labelLength = 0;
  815.     }
  816.  
  817.     if ((scalePtr->state != tkNormalUid)
  818.         && (scalePtr->state != tkDisabledUid)
  819.         && (scalePtr->state != tkActiveUid)) {
  820.     Tcl_AppendResult(interp, "bad state value \"", scalePtr->state,
  821.         "\": must be normal, active, or disabled", (char *) NULL);
  822.     scalePtr->state = tkNormalUid;
  823.     return TCL_ERROR;
  824.     }
  825.  
  826.  
  827.     Tk_SetBackgroundFromBorder(scalePtr->tkwin, scalePtr->bgBorder);
  828.     gcValues.background = scalePtr->troughColorPtr->pixel;
  829.     gcValues.foreground = scalePtr->troughColorPtr->pixel;
  830.     newGC = Tk_GetGC(scalePtr->tkwin, GCForeground, &gcValues);
  831.     if (scalePtr->troughGC != None) {
  832.     Tk_FreeGC(scalePtr->display, scalePtr->troughGC);
  833.     }
  834.     scalePtr->troughGC = newGC;
  835.  
  836.     if (scalePtr->copyGC == None) {
  837.     gcValues.graphics_exposures = False;
  838.     scalePtr->copyGC = Tk_GetGC(scalePtr->tkwin, GCGraphicsExposures,
  839.         &gcValues);
  840.     }
  841.     
  842.  
  843. /* Force a borderwidth of 1 */
  844.     scalePtr->borderWidth = 1;
  845.  
  846.     if (scalePtr->stipple==None) {
  847.        scalePtr->stipple = Tk_GetBitmap((Tcl_Interp *)NULL, scalePtr->tkwin,
  848.                                       Tk_GetUid("gray50"));
  849.        if (scalePtr->stipple == None) {
  850.           panic("ScrollBar couldn't allocate bitmap for trough");
  851.        }
  852.     }
  853.     
  854.     Tk_SetBackgroundFromBorder(scalePtr->tkwin, scalePtr->bgBorder);    
  855.     /* alloc color for trough base stipple */
  856.     newcolor.red = (60 * (int) scalePtr->troughColorPtr->red)/100;
  857.     newcolor.green =(60 * (int) scalePtr->troughColorPtr->green)/100;
  858.     newcolor.blue = (60 * (int) scalePtr->troughColorPtr->blue)/100;
  859.                     
  860.     if (scalePtr->darkPtr != NULL) {
  861.        Tk_FreeColor(scalePtr->darkPtr);
  862.     }    
  863.     scalePtr->darkPtr = Tk_GetColorByValue(scalePtr->tkwin,
  864.                              &newcolor);
  865.                            
  866.     gcValues.background = scalePtr->troughColorPtr->pixel;
  867.     gcValues.foreground = scalePtr->darkPtr->pixel;   
  868.     gcValues.stipple = scalePtr->stipple;
  869.     gcValues.fill_style = FillOpaqueStippled;
  870.                                                                 
  871.     newGC = Tk_GetGC(scalePtr->tkwin, GCForeground|GCBackground|GCStipple|
  872.                GCFillStyle, &gcValues);
  873.     if (scalePtr->troughBackGC != None) {
  874.        Tk_FreeGC(scalePtr->display, scalePtr->troughBackGC);
  875.     }
  876.     scalePtr->troughBackGC = newGC;    
  877.     
  878.     
  879.     
  880.     
  881.     
  882.     if (scalePtr->highlightWidth < 0) {
  883.     scalePtr->highlightWidth = 0;
  884.     }
  885.     gcValues.font = scalePtr->fontPtr->fid;
  886.     gcValues.foreground = scalePtr->textColorPtr->pixel;
  887.     newGC = Tk_GetGC(scalePtr->tkwin, GCForeground|GCFont, &gcValues);
  888.     if (scalePtr->textGC != None) {
  889.     Tk_FreeGC(scalePtr->display, scalePtr->textGC);
  890.     }
  891.     scalePtr->textGC = newGC;
  892.  
  893.     scalePtr->inset = scalePtr->highlightWidth + scalePtr->borderWidth;
  894.  
  895.     /*
  896.      * Recompute display-related information, and let the geometry
  897.      * manager know how much space is needed now.
  898.      */
  899.  
  900.     ComputeScaleGeometry(scalePtr);
  901.  
  902.     EventuallyRedrawScale(scalePtr, REDRAW_ALL);
  903.     return TCL_OK;
  904. }
  905.  
  906. /*
  907.  *----------------------------------------------------------------------
  908.  *
  909.  * ComputeFormat --
  910.  *
  911.  *    This procedure is invoked to recompute the "format" field
  912.  *    of a scale's widget record, which determines how the value
  913.  *    of the scale is converted to a string.
  914.  *
  915.  * Results:
  916.  *    None.
  917.  *
  918.  * Side effects:
  919.  *    The format field of scalePtr is modified.
  920.  *
  921.  *----------------------------------------------------------------------
  922.  */
  923.  
  924. static void
  925. ComputeFormat(scalePtr)
  926.     Scale *scalePtr;            /* Information about scale widget. */
  927. {
  928.     double maxValue, x;
  929.     int mostSigDigit, numDigits, leastSigDigit, afterDecimal;
  930.     int eDigits, fDigits;
  931.  
  932.     /*
  933.      * Compute the displacement from the decimal of the most significant
  934.      * digit required for any number in the scale's range.
  935.      */
  936.  
  937.     maxValue = fabs(scalePtr->fromValue);
  938.     x = fabs(scalePtr->toValue);
  939.     if (x > maxValue) {
  940.     maxValue = x;
  941.     }
  942.     if (maxValue == 0) {
  943.     maxValue = 1;
  944.     }
  945.     mostSigDigit = floor(log10(maxValue));
  946.  
  947.     /*
  948.      * If the number of significant digits wasn't specified explicitly,
  949.      * compute it. It's the difference between the most significant
  950.      * digit needed to represent any number on the scale and the
  951.      * most significant digit of the smallest difference between
  952.      * numbers on the scale.  In other words, display enough digits so
  953.      * that at least one digit will be different between any two adjacent
  954.      * positions of the scale.
  955.      */
  956.  
  957.     numDigits = scalePtr->digits;
  958.     if (numDigits <= 0) {
  959.     if  (scalePtr->resolution > 0) {
  960.         /*
  961.          * A resolution was specified for the scale, so just use it.
  962.          */
  963.  
  964.         leastSigDigit = floor(log10(scalePtr->resolution));
  965.     } else {
  966.         /*
  967.          * No resolution was specified, so compute the difference
  968.          * in value between adjacent pixels and use it for the least
  969.          * significant digit.
  970.          */
  971.  
  972.         x = fabs(scalePtr->fromValue - scalePtr->toValue);
  973.         if (scalePtr->length > 0) {
  974.         x /= scalePtr->length;
  975.         }
  976.         if (x > 0){
  977.         leastSigDigit = floor(log10(x));
  978.         } else {
  979.         leastSigDigit = 0;
  980.         }
  981.     }
  982.     numDigits = mostSigDigit - leastSigDigit + 1;
  983.     if (numDigits < 1) {
  984.         numDigits = 1;
  985.     }
  986.     }
  987.  
  988.     /*
  989.      * Compute the number of characters required using "e" format and
  990.      * "f" format, and then choose whichever one takes fewer characters.
  991.      */
  992.  
  993.     eDigits = numDigits + 4;
  994.     if (numDigits > 1) {
  995.     eDigits++;            /* Decimal point. */
  996.     }
  997.     afterDecimal = numDigits - mostSigDigit - 1;
  998.     if (afterDecimal < 0) {
  999.     afterDecimal = 0;
  1000.     }
  1001.     fDigits = (mostSigDigit >= 0) ? mostSigDigit + afterDecimal : afterDecimal;
  1002.     if (afterDecimal > 0) {
  1003.     fDigits++;            /* Decimal point. */
  1004.     }
  1005.     if (mostSigDigit < 0) {
  1006.     fDigits++;            /* Zero to left of decimal point. */
  1007.     }
  1008.     if (fDigits <= eDigits) {
  1009.     sprintf(scalePtr->format, "%%.%df", afterDecimal);
  1010.     } else {
  1011.     sprintf(scalePtr->format, "%%.%de", numDigits-1);
  1012.     }
  1013. }
  1014.  
  1015. /*
  1016.  *----------------------------------------------------------------------
  1017.  *
  1018.  * ComputeScaleGeometry --
  1019.  *
  1020.  *    This procedure is called to compute various geometrical
  1021.  *    information for a scale, such as where various things get
  1022.  *    displayed.  It's called when the window is reconfigured.
  1023.  *
  1024.  * Results:
  1025.  *    None.
  1026.  *
  1027.  * Side effects:
  1028.  *    Display-related numbers get changed in *scalePtr.  The
  1029.  *    geometry manager gets told about the window's preferred size.
  1030.  *
  1031.  *----------------------------------------------------------------------
  1032.  */
  1033.  
  1034. static void
  1035. ComputeScaleGeometry(scalePtr)
  1036.     register Scale *scalePtr;        /* Information about widget. */
  1037. {
  1038.     XCharStruct bbox;
  1039.     char valueString[PRINT_CHARS];
  1040.     int dummy, lineHeight, valuePixels, x, y, extraSpace;
  1041.  
  1042.     /*
  1043.      * Horizontal scales are simpler than vertical ones because
  1044.      * all sizes are the same (the height of a line of text);
  1045.      * handle them first and then quit.
  1046.      */
  1047.  
  1048.     if (!scalePtr->vertical) {
  1049.     lineHeight = scalePtr->fontPtr->ascent + scalePtr->fontPtr->descent;
  1050.     y = scalePtr->inset;
  1051.     extraSpace = 0;
  1052.     if (scalePtr->labelLength != 0) {
  1053.         scalePtr->horizLabelY = y + SPACING;
  1054.         y += lineHeight + SPACING;
  1055.         extraSpace = SPACING;
  1056.     }
  1057.     if (scalePtr->showValue) {
  1058.         scalePtr->horizValueY = y + SPACING;
  1059.         y += lineHeight + SPACING;
  1060.         extraSpace = SPACING;
  1061.     } else {
  1062.         scalePtr->horizValueY = y;
  1063.     }
  1064.     y += extraSpace;
  1065.     scalePtr->horizTroughY = y;
  1066.     y += scalePtr->width + 2*scalePtr->borderWidth;
  1067.     if (scalePtr->tickInterval != 0) {
  1068.         scalePtr->horizTickY = y + SPACING;
  1069.         y += lineHeight + 2*SPACING;
  1070.     }
  1071.     Tk_GeometryRequest(scalePtr->tkwin,
  1072.         scalePtr->length + 2*scalePtr->inset, y + scalePtr->inset);
  1073.     Tk_SetInternalBorder(scalePtr->tkwin, scalePtr->inset);
  1074.     return;
  1075.     }
  1076.  
  1077.     /*
  1078.      * Vertical scale:  compute the amount of space needed to display
  1079.      * the scales value by formatting strings for the two end points;
  1080.      * use whichever length is longer.
  1081.      */
  1082.  
  1083.     sprintf(valueString, scalePtr->format, scalePtr->fromValue);
  1084.     XTextExtents(scalePtr->fontPtr, valueString, (int) strlen(valueString),
  1085.         &dummy, &dummy, &dummy, &bbox);
  1086.     valuePixels = bbox.rbearing - bbox.lbearing;
  1087.     sprintf(valueString, scalePtr->format, scalePtr->toValue);
  1088.     XTextExtents(scalePtr->fontPtr, valueString, (int) strlen(valueString),
  1089.         &dummy, &dummy, &dummy, &bbox);
  1090.     if (valuePixels < bbox.rbearing - bbox.lbearing) {
  1091.     valuePixels = bbox.rbearing - bbox.lbearing;
  1092.     }
  1093.  
  1094.     /*
  1095.      * Assign x-locations to the elements of the scale, working from
  1096.      * left to right.
  1097.      */
  1098.  
  1099.     x = scalePtr->inset;
  1100.     if ((scalePtr->tickInterval != 0) && (scalePtr->showValue)) {
  1101.     scalePtr->vertTickRightX = x + SPACING + valuePixels;
  1102.     scalePtr->vertValueRightX = scalePtr->vertTickRightX + valuePixels
  1103.         + scalePtr->fontPtr->ascent/2;
  1104.     x = scalePtr->vertValueRightX + SPACING;
  1105.     } else if (scalePtr->tickInterval != 0) {
  1106.     scalePtr->vertTickRightX = x + SPACING + valuePixels;
  1107.     scalePtr->vertValueRightX = scalePtr->vertTickRightX;
  1108.     x = scalePtr->vertTickRightX + SPACING;
  1109.     } else if (scalePtr->showValue) {
  1110.     scalePtr->vertTickRightX = x;
  1111.     scalePtr->vertValueRightX = x + SPACING + valuePixels;
  1112.     x = scalePtr->vertValueRightX + SPACING;
  1113.     } else {
  1114.     scalePtr->vertTickRightX = x;
  1115.     scalePtr->vertValueRightX = x;
  1116.     }
  1117.     scalePtr->vertTroughX = x;
  1118.     x += 2*scalePtr->borderWidth + scalePtr->width;
  1119.     if (scalePtr->labelLength == 0) {
  1120.     scalePtr->vertLabelX = 0;
  1121.     } else {
  1122.     XTextExtents(scalePtr->fontPtr, scalePtr->label,
  1123.         scalePtr->labelLength, &dummy, &dummy, &dummy, &bbox);
  1124.     scalePtr->vertLabelX = x + scalePtr->fontPtr->ascent/2 - bbox.lbearing;
  1125.     x = scalePtr->vertLabelX + bbox.rbearing
  1126.         + scalePtr->fontPtr->ascent/2;
  1127.     }
  1128.     Tk_GeometryRequest(scalePtr->tkwin, x + scalePtr->inset,
  1129.         scalePtr->length + 2*scalePtr->inset);
  1130.     Tk_SetInternalBorder(scalePtr->tkwin, scalePtr->inset);
  1131. }
  1132.  
  1133. /*
  1134.  *--------------------------------------------------------------
  1135.  *
  1136.  * DisplayVerticalScale --
  1137.  *
  1138.  *    This procedure redraws the contents of a vertical scale
  1139.  *    window.  It is invoked as a do-when-idle handler, so it only
  1140.  *    runs when there's nothing else for the application to do.
  1141.  *
  1142.  * Results:
  1143.  *    There is no return value.  If only a part of the scale needs
  1144.  *    to be redrawn, then drawnAreaPtr is modified to reflect the
  1145.  *    area that was actually modified.
  1146.  *
  1147.  * Side effects:
  1148.  *    Information appears on the screen.
  1149.  *
  1150.  *--------------------------------------------------------------
  1151.  */
  1152.  
  1153. static void
  1154. DisplayVerticalScale(scalePtr, drawable, drawnAreaPtr)
  1155.     Scale *scalePtr;            /* Widget record for scale. */
  1156.     Drawable drawable;            /* Where to display scale (window
  1157.                      * or pixmap). */
  1158.     XRectangle *drawnAreaPtr;        /* Initally contains area of window;
  1159.                      * if only a part of the scale is
  1160.                      * redrawn, gets modified to reflect
  1161.                      * the part of the window that was
  1162.                      * redrawn. */
  1163. {
  1164.     Tk_Window tkwin = scalePtr->tkwin;
  1165.     int x, y, width, height, shadowWidth;
  1166.     double tickValue;
  1167.     Tk_3DBorder sliderBorder;
  1168.     GC darkGC, dark2GC, lightGC, light2GC;
  1169.  
  1170.  
  1171.     darkGC   = Tk_3DBorderGC(tkwin,scalePtr->bgBorder, TK_3D_DARK_GC);
  1172.     dark2GC  = Tk_3DBorderGC(tkwin,scalePtr->bgBorder, TK_3D_DARK2_GC);
  1173.     lightGC  = Tk_3DBorderGC(tkwin,scalePtr->bgBorder, TK_3D_LIGHT_GC);
  1174.     light2GC = Tk_3DBorderGC(tkwin,scalePtr->bgBorder, TK_3D_DARK_GC);
  1175.  
  1176.     /*
  1177.      * Display the information from left to right across the window.
  1178.      */
  1179.  
  1180.     if (!(scalePtr->flags & REDRAW_OTHER)) {
  1181.     drawnAreaPtr->x = scalePtr->vertTickRightX;
  1182.     drawnAreaPtr->y = scalePtr->inset;
  1183.     drawnAreaPtr->width = scalePtr->vertTroughX + scalePtr->width
  1184.         + 2*scalePtr->borderWidth - scalePtr->vertTickRightX;
  1185.     drawnAreaPtr->height -= 2*scalePtr->inset;
  1186.     }
  1187.     Tk_Fill3DRectangle(tkwin, drawable, scalePtr->bgBorder,
  1188.         drawnAreaPtr->x, drawnAreaPtr->y, drawnAreaPtr->width,
  1189.         drawnAreaPtr->height, 0, TK_RELIEF_FLAT);
  1190.     if (scalePtr->flags & REDRAW_OTHER) {
  1191.     /*
  1192.      * Display the tick marks.
  1193.      */
  1194.  
  1195.     if (scalePtr->tickInterval != 0) {
  1196.         for (tickValue = scalePtr->fromValue; ;
  1197.             tickValue += scalePtr->tickInterval) {
  1198.         /*
  1199.          * The RoundToResolution call gets rid of accumulated
  1200.          * round-off errors, if any.
  1201.          */
  1202.  
  1203.         tickValue = RoundToResolution(scalePtr, tickValue);
  1204.         if (scalePtr->toValue >= scalePtr->fromValue) {
  1205.             if (tickValue > scalePtr->toValue) {
  1206.             break;
  1207.             }
  1208.         } else {
  1209.             if (tickValue < scalePtr->toValue) {
  1210.             break;
  1211.             }
  1212.         }
  1213.         DisplayVerticalValue(scalePtr, drawable, tickValue,
  1214.             scalePtr->vertTickRightX);
  1215.         }
  1216.     }
  1217.     }
  1218.  
  1219.     /*
  1220.      * Display the value, if it is desired.
  1221.      */
  1222.  
  1223.     if (scalePtr->showValue) {
  1224.     DisplayVerticalValue(scalePtr, drawable, scalePtr->value,
  1225.         scalePtr->vertValueRightX);
  1226.     }
  1227.  
  1228.     /*
  1229.      * Display the trough and the slider.
  1230.      */
  1231.  
  1232.  
  1233.     XFillRectangle(scalePtr->display, drawable, scalePtr->troughBackGC,
  1234.         scalePtr->vertTroughX + scalePtr->borderWidth,
  1235.         scalePtr->inset + scalePtr->borderWidth,
  1236.         (unsigned) scalePtr->width,
  1237.         (unsigned) (Tk_Height(tkwin) - 2*scalePtr->inset
  1238.         - 2*scalePtr->borderWidth));
  1239.     Tk_Draw3DRectangle(tkwin, drawable,
  1240.         scalePtr->activeBorder, scalePtr->vertTroughX, scalePtr->inset,
  1241.         scalePtr->width + 2*scalePtr->borderWidth,
  1242.         Tk_Height(tkwin) - 2*scalePtr->inset, scalePtr->borderWidth,
  1243.         TK_RELIEF_SUNKEN);
  1244.     if (scalePtr->state == tkActiveUid) {
  1245.     sliderBorder = scalePtr->activeBorder;
  1246.     } else {
  1247.     sliderBorder = scalePtr->bgBorder;
  1248.     }
  1249.     width = scalePtr->width;
  1250.     height = scalePtr->sliderLength/2;
  1251.     x = scalePtr->vertTroughX + scalePtr->borderWidth;
  1252.     y = ValueToPixel(scalePtr, scalePtr->value) - height;
  1253.     /* We know borderwidth is 1 */
  1254.     shadowWidth = scalePtr->borderWidth+1;
  1255. /*
  1256.     if (shadowWidth == 0) {
  1257.     shadowWidth = 1;
  1258.     }
  1259.  
  1260.     Tk_Draw3DRectangle(tkwin, drawable, scalePtr->activeBorder, x, y, width,
  1261.         2*height, shadowWidth, TK_RELIEF_RAISED);
  1262.  
  1263.     x += shadowWidth;
  1264.     y += shadowWidth;
  1265.     width -= shadowWidth;
  1266.     height -= shadowWidth;
  1267. */    
  1268. /* Ugly! :-) */
  1269.     
  1270.     Tk_Fill3DRectangle(tkwin, drawable, scalePtr->bgBorder, x, y, width,
  1271.                        2*height, 2, TK_RELIEF_FLAT);
  1272.     /*
  1273.     XDrawLine(scalePtr->display, drawable, lightGC, x, y, x+width-shadowWidth, y);
  1274.     XDrawLine(scalePtr->display, drawable, lightGC, x, y, x, y+2*height-shadowWidth);
  1275.     XDrawLine(scalePtr->display, drawable, lightGC, x, y+height, x+width-shadowWidth, y+height);
  1276.  
  1277.     XDrawLine(scalePtr->display, drawable, dark2GC,  x+1, y+2*height-1, x+width-1, y+2*height-1);
  1278.     XDrawLine(scalePtr->display, drawable, dark2GC,  x+width-1, y+2*height-shadowWidth, x+width-1, y+1);
  1279.     XDrawLine(scalePtr->display, drawable, darkGC, x+width-1, y+height-1, x+1, y+height-1);
  1280.  
  1281.     XDrawLine(scalePtr->display, drawable, light2GC, x+1, y+height-2, x+width-shadowWidth, y+height-2);
  1282.     XDrawLine(scalePtr->display, drawable, light2GC, x+width-shadowWidth, y+height-2, x+width-shadowWidth, y+1);
  1283.     
  1284.     XDrawLine(scalePtr->display, drawable, light2GC, x+1, y+2*height-2, x+width-shadowWidth, y+2*height-2);
  1285.     XDrawLine(scalePtr->display, drawable, light2GC, x+width-shadowWidth, y+2*height-2, x+width-shadowWidth, y+height+1);
  1286.      * 
  1287.      * Re-uglyfication
  1288.     */
  1289.     XDrawLine(scalePtr->display, drawable, lightGC, x, y, x, y+2*height-shadowWidth);
  1290.     XDrawLine(scalePtr->display, drawable, lightGC, x+1, y, x+1, y+2*height-shadowWidth);
  1291.  
  1292.     XDrawLine(scalePtr->display, drawable, dark2GC,  x+width-1, y+2*height-shadowWidth, x+width-1, y+1);
  1293.     XDrawLine(scalePtr->display, drawable, darkGC,  x+width-2, y+2*height-shadowWidth, x+width-2, y+1);
  1294.     
  1295.     
  1296.     XDrawLine(scalePtr->display, drawable, lightGC, x+1, y, x+width-2, y);
  1297.  
  1298.     XDrawLine(scalePtr->display, drawable, darkGC, x+1, y+height-2, x+width-2, y+height-2);
  1299.     XDrawLine(scalePtr->display, drawable, lightGC, x+1, y+height-1, x+width-2, y+height-1);
  1300.     
  1301.     XDrawLine(scalePtr->display, drawable, darkGC, x, y+height*2-3, x+width-2, y+height*2-3);
  1302.     XDrawLine(scalePtr->display, drawable, dark2GC, x, y+height*2-2, x+width-1, y+height*2-2);
  1303.     XDrawLine(scalePtr->display, drawable, darkGC, x, y+height*2-1, x+width-1, y+height*2-1);
  1304.     
  1305.     
  1306.     /*
  1307.      * Draw the label to the right of the scale.
  1308.      */
  1309.  
  1310.     if ((scalePtr->flags & REDRAW_OTHER) && (scalePtr->labelLength != 0)) {
  1311.     XDrawString(scalePtr->display, drawable,
  1312.         scalePtr->textGC, scalePtr->vertLabelX,
  1313.         scalePtr->inset + (3*scalePtr->fontPtr->ascent)/2,
  1314.         scalePtr->label, scalePtr->labelLength);
  1315.     }
  1316. }
  1317.  
  1318. /*
  1319.  *----------------------------------------------------------------------
  1320.  *
  1321.  * DisplayVerticalValue --
  1322.  *
  1323.  *    This procedure is called to display values (scale readings)
  1324.  *    for vertically-oriented scales.
  1325.  *
  1326.  * Results:
  1327.  *    None.
  1328.  *
  1329.  * Side effects:
  1330.  *    The numerical value corresponding to value is displayed with
  1331.  *    its right edge at "rightEdge", and at a vertical position in
  1332.  *    the scale that corresponds to "value".
  1333.  *
  1334.  *----------------------------------------------------------------------
  1335.  */
  1336.  
  1337. static void
  1338. DisplayVerticalValue(scalePtr, drawable, value, rightEdge)
  1339.     register Scale *scalePtr;    /* Information about widget in which to
  1340.                  * display value. */
  1341.     Drawable drawable;        /* Pixmap or window in which to draw
  1342.                  * the value. */
  1343.     double value;        /* Y-coordinate of number to display,
  1344.                  * specified in application coords, not
  1345.                  * in pixels (we'll compute pixels). */
  1346.     int rightEdge;        /* X-coordinate of right edge of text,
  1347.                  * specified in pixels. */
  1348. {
  1349.     register Tk_Window tkwin = scalePtr->tkwin;
  1350.     int y, dummy, length;
  1351.     char valueString[PRINT_CHARS];
  1352.     XCharStruct bbox;
  1353.  
  1354.     y = ValueToPixel(scalePtr, value) + scalePtr->fontPtr->ascent/2;
  1355.     sprintf(valueString, scalePtr->format, value);
  1356.     length = strlen(valueString);
  1357.     XTextExtents(scalePtr->fontPtr, valueString, length,
  1358.         &dummy, &dummy, &dummy, &bbox);
  1359.  
  1360.     /*
  1361.      * Adjust the y-coordinate if necessary to keep the text entirely
  1362.      * inside the window.
  1363.      */
  1364.  
  1365.     if ((y - bbox.ascent) < (scalePtr->inset + SPACING)) {
  1366.     y = scalePtr->inset + SPACING + bbox.ascent;
  1367.     }
  1368.     if ((y + bbox.descent) > (Tk_Height(tkwin) - scalePtr->inset - SPACING)) {
  1369.     y = Tk_Height(tkwin) - scalePtr->inset - SPACING - bbox.descent;
  1370.     }
  1371.     XDrawString(scalePtr->display, drawable, scalePtr->textGC,
  1372.         rightEdge - bbox.rbearing, y, valueString, length);
  1373. }
  1374.  
  1375. /*
  1376.  *--------------------------------------------------------------
  1377.  *
  1378.  * DisplayHorizontalScale --
  1379.  *
  1380.  *    This procedure redraws the contents of a horizontal scale
  1381.  *    window.  It is invoked as a do-when-idle handler, so it only
  1382.  *    runs when there's nothing else for the application to do.
  1383.  *
  1384.  * Results:
  1385.  *    There is no return value.  If only a part of the scale needs
  1386.  *    to be redrawn, then drawnAreaPtr is modified to reflect the
  1387.  *    area that was actually modified.
  1388.  *
  1389.  * Side effects:
  1390.  *    Information appears on the screen.
  1391.  *
  1392.  *--------------------------------------------------------------
  1393.  */
  1394.  
  1395. static void
  1396. DisplayHorizontalScale(scalePtr, drawable, drawnAreaPtr)
  1397.     Scale *scalePtr;            /* Widget record for scale. */
  1398.     Drawable drawable;            /* Where to display scale (window
  1399.                      * or pixmap). */
  1400.     XRectangle *drawnAreaPtr;        /* Initally contains area of window;
  1401.                      * if only a part of the scale is
  1402.                      * redrawn, gets modified to reflect
  1403.                      * the part of the window that was
  1404.                      * redrawn. */
  1405. {
  1406.     register Tk_Window tkwin = scalePtr->tkwin;
  1407.     int x, y, width, height, shadowWidth;
  1408.     double tickValue;
  1409.     Tk_3DBorder sliderBorder;
  1410.     GC lightGC, light2GC, darkGC, dark2GC;
  1411.     
  1412.     darkGC   = Tk_3DBorderGC(tkwin,scalePtr->bgBorder, TK_3D_DARK_GC);
  1413.     dark2GC  = Tk_3DBorderGC(tkwin,scalePtr->bgBorder, TK_3D_DARK2_GC);
  1414.     lightGC  = Tk_3DBorderGC(tkwin,scalePtr->bgBorder, TK_3D_LIGHT_GC);
  1415.     light2GC = Tk_3DBorderGC(tkwin,scalePtr->bgBorder, TK_3D_DARK_GC);
  1416.  
  1417.     /*
  1418.      * Display the information from bottom to top across the window.
  1419.      */
  1420.  
  1421.     if (!(scalePtr->flags & REDRAW_OTHER)) {
  1422.     drawnAreaPtr->x = scalePtr->inset;
  1423.     drawnAreaPtr->y = scalePtr->horizValueY;
  1424.     drawnAreaPtr->width -= 2*scalePtr->inset;
  1425.     drawnAreaPtr->height = scalePtr->horizTroughY + scalePtr->width
  1426.         + 2*scalePtr->borderWidth - scalePtr->horizValueY;
  1427.     }
  1428.     Tk_Fill3DRectangle(tkwin, drawable, scalePtr->bgBorder,
  1429.         drawnAreaPtr->x, drawnAreaPtr->y, drawnAreaPtr->width,
  1430.         drawnAreaPtr->height, 0, TK_RELIEF_FLAT);
  1431.     if (scalePtr->flags & REDRAW_OTHER) {
  1432.     /*
  1433.      * Display the tick marks.
  1434.      */
  1435.  
  1436.     if (scalePtr->tickInterval != 0) {
  1437.         for (tickValue = scalePtr->fromValue; ;
  1438.             tickValue += scalePtr->tickInterval) {
  1439.         /*
  1440.          * The RoundToResolution call gets rid of accumulated
  1441.          * round-off errors, if any.
  1442.          */
  1443.  
  1444.         tickValue = RoundToResolution(scalePtr, tickValue);
  1445.         if (scalePtr->toValue >= scalePtr->fromValue) {
  1446.             if (tickValue > scalePtr->toValue) {
  1447.             break;
  1448.             }
  1449.         } else {
  1450.             if (tickValue < scalePtr->toValue) {
  1451.             break;
  1452.             }
  1453.         }
  1454.         DisplayHorizontalValue(scalePtr, drawable, tickValue,
  1455.             scalePtr->horizTickY);
  1456.         }
  1457.     }
  1458.     }
  1459.  
  1460.     /*
  1461.      * Display the value, if it is desired.
  1462.      */
  1463.  
  1464.     if (scalePtr->showValue) {
  1465.     DisplayHorizontalValue(scalePtr, drawable, scalePtr->value,
  1466.         scalePtr->horizValueY);
  1467.     }
  1468.  
  1469.     /*
  1470.      * Display the trough and the slider.
  1471.      */
  1472.  
  1473.     y = scalePtr->horizTroughY;
  1474.     Tk_Draw3DRectangle(tkwin, drawable,
  1475.         scalePtr->bgBorder, scalePtr->inset, y,
  1476.         Tk_Width(tkwin) - 2*scalePtr->inset,
  1477.         scalePtr->width + 2*scalePtr->borderWidth,
  1478.         scalePtr->borderWidth, TK_RELIEF_SUNKEN);
  1479.     XFillRectangle(scalePtr->display, drawable, scalePtr->troughBackGC,
  1480.         scalePtr->inset + scalePtr->borderWidth,
  1481.         y + scalePtr->borderWidth,
  1482.         (unsigned) (Tk_Width(tkwin) - 2*scalePtr->inset
  1483.         - 2*scalePtr->borderWidth),
  1484.         (unsigned) scalePtr->width);
  1485.     if (scalePtr->state == tkActiveUid) {
  1486.     sliderBorder = scalePtr->activeBorder;
  1487.     } else {
  1488.     sliderBorder = scalePtr->bgBorder;
  1489.     }
  1490.     width = scalePtr->sliderLength/2;
  1491.     height = scalePtr->width;
  1492.     x = ValueToPixel(scalePtr, scalePtr->value) - width;
  1493.     y += scalePtr->borderWidth;
  1494.     /* We know scalePtr->borderWidth is 1 */
  1495.     shadowWidth = scalePtr->borderWidth+1;
  1496. /*
  1497.     if (shadowWidth == 0) {
  1498.     shadowWidth = 1;
  1499.     }
  1500.     Tk_Draw3DRectangle(tkwin, drawable, scalePtr->activeBorder,
  1501.         x, y, 2*width, height, shadowWidth, TK_RELIEF_RAISED);
  1502.     x += shadowWidth;
  1503.     y += shadowWidth;
  1504.     width -= shadowWidth;
  1505.     height -= 2*shadowWidth;
  1506.     Tk_Fill3DRectangle(tkwin, drawable, scalePtr->bgBorder, x, y, width, height,
  1507.         shadowWidth, TK_RELIEF_RAISED);
  1508.     Tk_Fill3DRectangle(tkwin, drawable, scalePtr->bgBorder, x+width, y,
  1509.         width, height, shadowWidth, TK_RELIEF_RAISED);
  1510.     XDrawLine(scalePtr->display, drawable, lightGC, x+width, y, x+width, y+height-2);
  1511. */
  1512.     Tk_Fill3DRectangle(tkwin, drawable, scalePtr->bgBorder, x, y, 2*width,
  1513.                        height, 2, TK_RELIEF_FLAT);
  1514.     /*
  1515.     XDrawLine(scalePtr->display, drawable, lightGC, x, y, x+2*width-2, y);
  1516.     XDrawLine(scalePtr->display, drawable, lightGC, x, y, x, y+height-2);
  1517.     XDrawLine(scalePtr->display, drawable, lightGC, x+width, y, x+width, y+height-2);
  1518.  
  1519.     XDrawLine(scalePtr->display, drawable, dark2GC,  x+1, y+height-1, x+2*width-1, y+height-1);
  1520.     XDrawLine(scalePtr->display, drawable, dark2GC,  x+2*width-1, y+height-1, x+2*width-1, y+1);
  1521.     XDrawLine(scalePtr->display, drawable, darkGC, x+width-1, y+height-1, x+width-1, y+1);
  1522.  
  1523.     XDrawLine(scalePtr->display, drawable, light2GC, x+1, y+height-2, x+width-shadowWidth, y+height-2);
  1524.     XDrawLine(scalePtr->display, drawable, light2GC, x+width-shadowWidth, y+height-2, x+width-2, y+1);
  1525.  
  1526.     XDrawLine(scalePtr->display, drawable, light2GC, x+width+1, y+height-2, x+2*width-2, y+height-2);
  1527.     XDrawLine(scalePtr->display, drawable, light2GC, x+2*width-2, y+height-2, x+2*width-2, y+1);
  1528.  
  1529.     */
  1530.     
  1531.     XDrawLine(scalePtr->display, drawable, lightGC, x, y, x+2*width-2, y);
  1532.  
  1533.     XDrawLine(scalePtr->display, drawable, darkGC,  x, y+height-2, x+2*width-2, y+height-2);
  1534.     XDrawLine(scalePtr->display, drawable, dark2GC,  x, y+height-1, x+2*width-2, y+height-1);
  1535.     
  1536.     
  1537.     XDrawLine(scalePtr->display, drawable, lightGC, x, y, x, y+height-2);
  1538.     XDrawLine(scalePtr->display, drawable, lightGC, x+1, y, x+1, y+height-3);
  1539.     
  1540.     XDrawLine(scalePtr->display, drawable, darkGC, x+width-2, y+1, x+width-2, y+height-3);
  1541.     XDrawLine(scalePtr->display, drawable, lightGC, x+width-1, y, x+width-1, y+height-2);
  1542.     
  1543.     XDrawLine(scalePtr->display, drawable, darkGC, x+2*width-3, y, x+2*width-3, y+height-2);
  1544.     XDrawLine(scalePtr->display, drawable, dark2GC, x+2*width-2, y, x+2*width-2, y+height-1);
  1545.     XDrawLine(scalePtr->display, drawable, darkGC, x+2*width-1, y, x+2*width-1, y+height-1);
  1546.  
  1547.     
  1548.     /*
  1549.      * Draw the label at the top of the scale.
  1550.      */
  1551.  
  1552.     if ((scalePtr->flags & REDRAW_OTHER) && (scalePtr->labelLength != 0)) {
  1553.     XDrawString(scalePtr->display, drawable,
  1554.         scalePtr->textGC, scalePtr->inset + scalePtr->fontPtr->ascent/2,
  1555.         scalePtr->horizLabelY + scalePtr->fontPtr->ascent,
  1556.         scalePtr->label, scalePtr->labelLength);
  1557.     }
  1558. }
  1559.  
  1560. /*
  1561.  *----------------------------------------------------------------------
  1562.  *
  1563.  * DisplayHorizontalValue --
  1564.  *
  1565.  *    This procedure is called to display values (scale readings)
  1566.  *    for horizontally-oriented scales.
  1567.  *
  1568.  * Results:
  1569.  *    None.
  1570.  *
  1571.  * Side effects:
  1572.  *    The numerical value corresponding to value is displayed with
  1573.  *    its bottom edge at "bottom", and at a horizontal position in
  1574.  *    the scale that corresponds to "value".
  1575.  *
  1576.  *----------------------------------------------------------------------
  1577.  */
  1578.  
  1579. static void
  1580. DisplayHorizontalValue(scalePtr, drawable, value, top)
  1581.     register Scale *scalePtr;    /* Information about widget in which to
  1582.                  * display value. */
  1583.     Drawable drawable;        /* Pixmap or window in which to draw
  1584.                  * the value. */
  1585.     double value;        /* X-coordinate of number to display,
  1586.                  * specified in application coords, not
  1587.                  * in pixels (we'll compute pixels). */
  1588.     int top;            /* Y-coordinate of top edge of text,
  1589.                  * specified in pixels. */
  1590. {
  1591.     register Tk_Window tkwin = scalePtr->tkwin;
  1592.     int x, y, dummy, length;
  1593.     char valueString[PRINT_CHARS];
  1594.     XCharStruct bbox;
  1595.  
  1596.     x = ValueToPixel(scalePtr, value);
  1597.     y = top + scalePtr->fontPtr->ascent;
  1598.     sprintf(valueString, scalePtr->format, value);
  1599.     length = strlen(valueString);
  1600.     XTextExtents(scalePtr->fontPtr, valueString, length,
  1601.         &dummy, &dummy, &dummy, &bbox);
  1602.  
  1603.     /*
  1604.      * Adjust the x-coordinate if necessary to keep the text entirely
  1605.      * inside the window.
  1606.      */
  1607.  
  1608.     x -= (bbox.rbearing - bbox.lbearing)/2;
  1609.     if ((x + bbox.lbearing) < (scalePtr->inset + SPACING)) {
  1610.     x = scalePtr->inset + SPACING - bbox.lbearing;
  1611.     }
  1612.     if ((x + bbox.rbearing) > (Tk_Width(tkwin) - scalePtr->inset)) {
  1613.     x = Tk_Width(tkwin) - scalePtr->inset - SPACING - bbox.rbearing;
  1614.     }
  1615.     XDrawString(scalePtr->display, drawable, scalePtr->textGC, x, y,
  1616.         valueString, length);
  1617. }
  1618.  
  1619. /*
  1620.  *----------------------------------------------------------------------
  1621.  *
  1622.  * DisplayScale --
  1623.  *
  1624.  *    This procedure is invoked as an idle handler to redisplay
  1625.  *    the contents of a scale widget.
  1626.  *
  1627.  * Results:
  1628.  *    None.
  1629.  *
  1630.  * Side effects:
  1631.  *    The scale gets redisplayed.
  1632.  *
  1633.  *----------------------------------------------------------------------
  1634.  */
  1635.  
  1636. static void
  1637. DisplayScale(clientData)
  1638.     ClientData clientData;    /* Widget record for scale. */
  1639. {
  1640.     Scale *scalePtr = (Scale *) clientData;
  1641.     Tk_Window tkwin = scalePtr->tkwin;
  1642.     Tcl_Interp *interp = scalePtr->interp;
  1643.     Pixmap pixmap;
  1644.     int result;
  1645.     char string[PRINT_CHARS];
  1646.     XRectangle drawnArea;
  1647.  
  1648.     if ((scalePtr->tkwin == NULL) || !Tk_IsMapped(scalePtr->tkwin)) {
  1649.     goto done;
  1650.     }
  1651.  
  1652.     /*
  1653.      * Invoke the scale's command if needed.
  1654.      */
  1655.  
  1656.     Tcl_Preserve((ClientData) scalePtr);
  1657.     Tcl_Preserve((ClientData) interp);
  1658.     if ((scalePtr->flags & INVOKE_COMMAND) && (scalePtr->command != NULL)) {
  1659.     sprintf(string, scalePtr->format, scalePtr->value);
  1660.     result = Tcl_VarEval(interp, scalePtr->command,    " ", string,
  1661.                              (char *) NULL);
  1662.     if (result != TCL_OK) {
  1663.         Tcl_AddErrorInfo(interp, "\n    (command executed by scale)");
  1664.         Tcl_BackgroundError(interp);
  1665.     }
  1666.     }
  1667.     Tcl_Release((ClientData) interp);
  1668.     scalePtr->flags &= ~INVOKE_COMMAND;
  1669.     if (scalePtr->tkwin == NULL) {
  1670.     Tcl_Release((ClientData) scalePtr);
  1671.     return;
  1672.     }
  1673.     Tcl_Release((ClientData) scalePtr);
  1674.  
  1675.     /*
  1676.      * In order to avoid screen flashes, this procedure redraws
  1677.      * the scale in a pixmap, then copies the pixmap to the
  1678.      * screen in a single operation.  This means that there's no
  1679.      * point in time where the on-sreen image has been cleared.
  1680.      */
  1681.  
  1682.     pixmap = Tk_GetPixmap(scalePtr->display, Tk_WindowId(tkwin),
  1683.         Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  1684.     drawnArea.x = 0;
  1685.     drawnArea.y = 0;
  1686.     drawnArea.width = Tk_Width(tkwin);
  1687.     drawnArea.height = Tk_Height(tkwin);
  1688.  
  1689.     /*
  1690.      * Much of the redisplay is done totally differently for
  1691.      * horizontal and vertical scales.  Handle the part that's
  1692.      * different.
  1693.      */
  1694.  
  1695.     if (scalePtr->vertical) {
  1696.     DisplayVerticalScale(scalePtr, pixmap, &drawnArea);
  1697.     } else {
  1698.     DisplayHorizontalScale(scalePtr, pixmap, &drawnArea);
  1699.     }
  1700.  
  1701.     /*
  1702.      * Now handle the part of redisplay that is the same for
  1703.      * horizontal and vertical scales:  border and traversal
  1704.      * highlight.
  1705.      */
  1706.  
  1707.     if (scalePtr->flags & REDRAW_OTHER) {
  1708.     if (scalePtr->relief != TK_RELIEF_FLAT) {
  1709.         Tk_Draw3DRectangle(tkwin, pixmap, scalePtr->bgBorder,
  1710.             scalePtr->highlightWidth, scalePtr->highlightWidth,
  1711.             Tk_Width(tkwin) - 2*scalePtr->highlightWidth,
  1712.             Tk_Height(tkwin) - 2*scalePtr->highlightWidth,
  1713.             scalePtr->borderWidth, scalePtr->relief);
  1714.     }
  1715.     if (scalePtr->highlightWidth != 0) {
  1716.         GC gc;
  1717.     
  1718.         if (scalePtr->flags & GOT_FOCUS) {
  1719.         gc = Tk_GCForColor(scalePtr->highlightColorPtr, pixmap);
  1720.         } else {
  1721.         gc = Tk_GCForColor(scalePtr->highlightBgColorPtr, pixmap);
  1722.         }
  1723.         Tk_DrawFocusHighlight(tkwin, gc, scalePtr->highlightWidth, pixmap);
  1724.     }
  1725.     }
  1726.  
  1727.     /*
  1728.      * Copy the information from the off-screen pixmap onto the screen,
  1729.      * then delete the pixmap.
  1730.      */
  1731.  
  1732.     XCopyArea(scalePtr->display, pixmap, Tk_WindowId(tkwin),
  1733.         scalePtr->copyGC, drawnArea.x, drawnArea.y, drawnArea.width,
  1734.         drawnArea.height, drawnArea.x, drawnArea.y);
  1735.     Tk_FreePixmap(scalePtr->display, pixmap);
  1736.  
  1737.     done:
  1738.     scalePtr->flags &= ~REDRAW_ALL;
  1739. }
  1740.  
  1741. /*
  1742.  *----------------------------------------------------------------------
  1743.  *
  1744.  * ScaleElement --
  1745.  *
  1746.  *    Determine which part of a scale widget lies under a given
  1747.  *    point.
  1748.  *
  1749.  * Results:
  1750.  *    The return value is either TROUGH1, SLIDER, TROUGH2, or
  1751.  *    OTHER, depending on which of the scale's active elements
  1752.  *    (if any) is under the point at (x,y).
  1753.  *
  1754.  * Side effects:
  1755.  *    None.
  1756.  *
  1757.  *----------------------------------------------------------------------
  1758.  */
  1759.  
  1760. static int
  1761. ScaleElement(scalePtr, x, y)
  1762.     Scale *scalePtr;        /* Widget record for scale. */
  1763.     int x, y;            /* Coordinates within scalePtr's window. */
  1764. {
  1765.     int sliderFirst;
  1766.  
  1767.     if (scalePtr->vertical) {
  1768.     if ((x < scalePtr->vertTroughX)
  1769.         || (x >= (scalePtr->vertTroughX + 2*scalePtr->borderWidth +
  1770.         scalePtr->width))) {
  1771.         return OTHER;
  1772.     }
  1773.     if ((y < scalePtr->inset)
  1774.         || (y >= (Tk_Height(scalePtr->tkwin) - scalePtr->inset))) {
  1775.         return OTHER;
  1776.     }
  1777.     sliderFirst = ValueToPixel(scalePtr, scalePtr->value)
  1778.         - scalePtr->sliderLength/2;
  1779.     if (y < sliderFirst) {
  1780.         return TROUGH1;
  1781.     }
  1782.     if (y < (sliderFirst+scalePtr->sliderLength)) {
  1783.         return SLIDER;
  1784.     }
  1785.     return TROUGH2;
  1786.     }
  1787.  
  1788.     if ((y < scalePtr->horizTroughY)
  1789.         || (y >= (scalePtr->horizTroughY + 2*scalePtr->borderWidth +
  1790.         scalePtr->width))) {
  1791.     return OTHER;
  1792.     }
  1793.     if ((x < scalePtr->inset)
  1794.         || (x >= (Tk_Width(scalePtr->tkwin) - scalePtr->inset))) {
  1795.     return OTHER;
  1796.     }
  1797.     sliderFirst = ValueToPixel(scalePtr, scalePtr->value)
  1798.         - scalePtr->sliderLength/2;
  1799.     if (x < sliderFirst) {
  1800.     return TROUGH1;
  1801.     }
  1802.     if (x < (sliderFirst+scalePtr->sliderLength)) {
  1803.     return SLIDER;
  1804.     }
  1805.     return TROUGH2;
  1806. }
  1807.  
  1808. /*
  1809.  *----------------------------------------------------------------------
  1810.  *
  1811.  * PixelToValue --
  1812.  *
  1813.  *    Given a pixel within a scale window, return the scale
  1814.  *    reading corresponding to that pixel.
  1815.  *
  1816.  * Results:
  1817.  *    A double-precision scale reading.  If the value is outside
  1818.  *    the legal range for the scale then it's rounded to the nearest
  1819.  *    end of the scale.
  1820.  *
  1821.  * Side effects:
  1822.  *    None.
  1823.  *
  1824.  *----------------------------------------------------------------------
  1825.  */
  1826.  
  1827. static double
  1828. PixelToValue(scalePtr, x, y)
  1829.     register Scale *scalePtr;        /* Information about widget. */
  1830.     int x, y;                /* Coordinates of point within
  1831.                      * window. */
  1832. {
  1833.     double value, pixelRange;
  1834.  
  1835.     if (scalePtr->vertical) {
  1836.     pixelRange = Tk_Height(scalePtr->tkwin) - scalePtr->sliderLength
  1837.         - 2*scalePtr->inset - 2*scalePtr->borderWidth;
  1838.     value = y;
  1839.     } else {
  1840.     pixelRange = Tk_Width(scalePtr->tkwin) - scalePtr->sliderLength
  1841.         - 2*scalePtr->inset - 2*scalePtr->borderWidth;
  1842.     value = x;
  1843.     }
  1844.  
  1845.     if (pixelRange <= 0) {
  1846.     /*
  1847.      * Not enough room for the slider to actually slide:  just return
  1848.      * the scale's current value.
  1849.      */
  1850.  
  1851.     return scalePtr->value;
  1852.     }
  1853.     value -= scalePtr->sliderLength/2 + scalePtr->inset
  1854.         + scalePtr->borderWidth;
  1855.     value /= pixelRange;
  1856.     if (value < 0) {
  1857.     value = 0;
  1858.     }
  1859.     if (value > 1) {
  1860.     value = 1;
  1861.     }
  1862.     value = scalePtr->fromValue +
  1863.         value * (scalePtr->toValue - scalePtr->fromValue);
  1864.     return RoundToResolution(scalePtr, value);
  1865. }
  1866.  
  1867. /*
  1868.  *----------------------------------------------------------------------
  1869.  *
  1870.  * ValueToPixel --
  1871.  *
  1872.  *    Given a reading of the scale, return the x-coordinate or
  1873.  *    y-coordinate corresponding to that reading, depending on
  1874.  *    whether the scale is vertical or horizontal, respectively.
  1875.  *
  1876.  * Results:
  1877.  *    An integer value giving the pixel location corresponding
  1878.  *    to reading.  The value is restricted to lie within the
  1879.  *    defined range for the scale.
  1880.  *
  1881.  * Side effects:
  1882.  *    None.
  1883.  *
  1884.  *----------------------------------------------------------------------
  1885.  */
  1886.  
  1887. static int
  1888. ValueToPixel(scalePtr, value)
  1889.     register Scale *scalePtr;        /* Information about widget. */
  1890.     double value;            /* Reading of the widget. */
  1891. {
  1892.     int y, pixelRange;
  1893.     double valueRange;
  1894.  
  1895.     valueRange = scalePtr->toValue - scalePtr->fromValue;
  1896.     pixelRange = (scalePtr->vertical ? Tk_Height(scalePtr->tkwin)
  1897.         : Tk_Width(scalePtr->tkwin)) - scalePtr->sliderLength
  1898.         - 2*scalePtr->inset - 2*scalePtr->borderWidth;
  1899.     if (valueRange == 0) {
  1900.     y = 0;
  1901.     } else {
  1902.     y = (int) ((value - scalePtr->fromValue) * pixelRange
  1903.           / valueRange + 0.5);
  1904.     if (y < 0) {
  1905.         y = 0;
  1906.     } else if (y > pixelRange) {
  1907.         y = pixelRange;
  1908.     }
  1909.     }
  1910.     y += scalePtr->sliderLength/2 + scalePtr->inset + scalePtr->borderWidth;
  1911.     return y;
  1912. }
  1913.  
  1914. /*
  1915.  *--------------------------------------------------------------
  1916.  *
  1917.  * ScaleEventProc --
  1918.  *
  1919.  *    This procedure is invoked by the Tk dispatcher for various
  1920.  *    events on scales.
  1921.  *
  1922.  * Results:
  1923.  *    None.
  1924.  *
  1925.  * Side effects:
  1926.  *    When the window gets deleted, internal structures get
  1927.  *    cleaned up.  When it gets exposed, it is redisplayed.
  1928.  *
  1929.  *--------------------------------------------------------------
  1930.  */
  1931.  
  1932. static void
  1933. ScaleEventProc(clientData, eventPtr)
  1934.     ClientData clientData;    /* Information about window. */
  1935.     XEvent *eventPtr;        /* Information about event. */
  1936. {
  1937.     Scale *scalePtr = (Scale *) clientData;
  1938.  
  1939.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  1940.     EventuallyRedrawScale(scalePtr, REDRAW_ALL);
  1941.     } else if (eventPtr->type == DestroyNotify) {
  1942.     if (scalePtr->tkwin != NULL) {
  1943.         scalePtr->tkwin = NULL;
  1944.         Tcl_DeleteCommand(scalePtr->interp,
  1945.             Tcl_GetCommandName(scalePtr->interp, scalePtr->widgetCmd));
  1946.     }
  1947.     if (scalePtr->flags & REDRAW_ALL) {
  1948.         Tcl_CancelIdleCall(DisplayScale, (ClientData) scalePtr);
  1949.     }
  1950.     Tcl_EventuallyFree((ClientData) scalePtr, DestroyScale);
  1951.     } else if (eventPtr->type == ConfigureNotify) {
  1952.     ComputeScaleGeometry(scalePtr);
  1953.     EventuallyRedrawScale(scalePtr, REDRAW_ALL);
  1954.     } else if (eventPtr->type == FocusIn) {
  1955.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1956.         scalePtr->flags |= GOT_FOCUS;
  1957.         if (scalePtr->highlightWidth > 0) {
  1958.         EventuallyRedrawScale(scalePtr, REDRAW_ALL);
  1959.         }
  1960.     }
  1961.     } else if (eventPtr->type == FocusOut) {
  1962.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1963.         scalePtr->flags &= ~GOT_FOCUS;
  1964.         if (scalePtr->highlightWidth > 0) {
  1965.         EventuallyRedrawScale(scalePtr, REDRAW_ALL);
  1966.         }
  1967.     }
  1968.     }
  1969. }
  1970.  
  1971. /*
  1972.  *----------------------------------------------------------------------
  1973.  *
  1974.  * ScaleCmdDeletedProc --
  1975.  *
  1976.  *    This procedure is invoked when a widget command is deleted.  If
  1977.  *    the widget isn't already in the process of being destroyed,
  1978.  *    this command destroys it.
  1979.  *
  1980.  * Results:
  1981.  *    None.
  1982.  *
  1983.  * Side effects:
  1984.  *    The widget is destroyed.
  1985.  *
  1986.  *----------------------------------------------------------------------
  1987.  */
  1988.  
  1989. static void
  1990. ScaleCmdDeletedProc(clientData)
  1991.     ClientData clientData;    /* Pointer to widget record for widget. */
  1992. {
  1993.     Scale *scalePtr = (Scale *) clientData;
  1994.     Tk_Window tkwin = scalePtr->tkwin;
  1995.  
  1996.     /*
  1997.      * This procedure could be invoked either because the window was
  1998.      * destroyed and the command was then deleted (in which case tkwin
  1999.      * is NULL) or because the command was deleted, and then this procedure
  2000.      * destroys the widget.
  2001.      */
  2002.  
  2003.     if (tkwin != NULL) {
  2004.     scalePtr->tkwin = NULL;
  2005.     Tk_DestroyWindow(tkwin);
  2006.     }
  2007. }
  2008.  
  2009. /*
  2010.  *--------------------------------------------------------------
  2011.  *
  2012.  * SetScaleValue --
  2013.  *
  2014.  *    This procedure changes the value of a scale and invokes
  2015.  *    a Tcl command to reflect the current position of a scale
  2016.  *
  2017.  * Results:
  2018.  *    None.
  2019.  *
  2020.  * Side effects:
  2021.  *    A Tcl command is invoked, and an additional error-processing
  2022.  *    command may also be invoked.  The scale's slider is redrawn.
  2023.  *
  2024.  *--------------------------------------------------------------
  2025.  */
  2026.  
  2027. static void
  2028. SetScaleValue(scalePtr, value, setVar, invokeCommand)
  2029.     register Scale *scalePtr;    /* Info about widget. */
  2030.     double value;        /* New value for scale.  Gets adjusted
  2031.                  * if it's off the scale. */
  2032.     int setVar;            /* Non-zero means reflect new value through
  2033.                  * to associated variable, if any. */
  2034.     int invokeCommand;        /* Non-zero means invoked -command option
  2035.                  * to notify of new value, 0 means don't. */
  2036. {
  2037.     char string[PRINT_CHARS];
  2038.  
  2039.     value = RoundToResolution(scalePtr, value);
  2040.     if ((value < scalePtr->fromValue)
  2041.         ^ (scalePtr->toValue < scalePtr->fromValue)) {
  2042.     value = scalePtr->fromValue;
  2043.     }
  2044.     if ((value > scalePtr->toValue)
  2045.         ^ (scalePtr->toValue < scalePtr->fromValue)) {
  2046.     value = scalePtr->toValue;
  2047.     }
  2048.     if (scalePtr->flags & NEVER_SET) {
  2049.     scalePtr->flags &= ~NEVER_SET;
  2050.     } else if (scalePtr->value == value) {
  2051.     return;
  2052.     }
  2053.     scalePtr->value = value;
  2054.     if (invokeCommand) {
  2055.     scalePtr->flags |= INVOKE_COMMAND;
  2056.     }
  2057.     EventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
  2058.  
  2059.     if (setVar && (scalePtr->varName != NULL)) {
  2060.     sprintf(string, scalePtr->format, scalePtr->value);
  2061.     scalePtr->flags |= SETTING_VAR;
  2062.     Tcl_SetVar(scalePtr->interp, scalePtr->varName, string,
  2063.            TCL_GLOBAL_ONLY);
  2064.     scalePtr->flags &= ~SETTING_VAR;
  2065.     }
  2066. }
  2067.  
  2068. /*
  2069.  *--------------------------------------------------------------
  2070.  *
  2071.  * EventuallyRedrawScale --
  2072.  *
  2073.  *    Arrange for part or all of a scale widget to redrawn at
  2074.  *    the next convenient time in the future.
  2075.  *
  2076.  * Results:
  2077.  *    None.
  2078.  *
  2079.  * Side effects:
  2080.  *    If "what" is REDRAW_SLIDER then just the slider and the
  2081.  *    value readout will be redrawn;  if "what" is REDRAW_ALL
  2082.  *    then the entire widget will be redrawn.
  2083.  *
  2084.  *--------------------------------------------------------------
  2085.  */
  2086.  
  2087. static void
  2088. EventuallyRedrawScale(scalePtr, what)
  2089.     register Scale *scalePtr;    /* Information about widget. */
  2090.     int what;            /* What to redraw:  REDRAW_SLIDER
  2091.                  * or REDRAW_ALL. */
  2092. {
  2093.     if ((what == 0) || (scalePtr->tkwin == NULL)
  2094.         || !Tk_IsMapped(scalePtr->tkwin)) {
  2095.     return;
  2096.     }
  2097.     if ((scalePtr->flags & REDRAW_ALL) == 0) {
  2098.     Tcl_DoWhenIdle(DisplayScale, (ClientData) scalePtr);
  2099.     }
  2100.     scalePtr->flags |= what;
  2101. }
  2102.  
  2103. /*
  2104.  *--------------------------------------------------------------
  2105.  *
  2106.  * RoundToResolution --
  2107.  *
  2108.  *    Round a given floating-point value to the nearest multiple
  2109.  *    of the scale's resolution.
  2110.  *
  2111.  * Results:
  2112.  *    The return value is the rounded result.
  2113.  *
  2114.  * Side effects:
  2115.  *    None.
  2116.  *
  2117.  *--------------------------------------------------------------
  2118.  */
  2119.  
  2120. static double
  2121. RoundToResolution(scalePtr, value)
  2122.     Scale *scalePtr;        /* Information about scale widget. */
  2123.     double value;        /* Value to round. */
  2124. {
  2125.     double rem, new;
  2126.  
  2127.     if (scalePtr->resolution <= 0) {
  2128.     return value;
  2129.     }
  2130.     new = scalePtr->resolution * floor(value/scalePtr->resolution);
  2131.     rem = value - new;
  2132.     if (rem < 0) {
  2133.     if (rem <= -scalePtr->resolution/2) {
  2134.         new -= scalePtr->resolution;
  2135.     }
  2136.     } else {
  2137.     if (rem >= scalePtr->resolution/2) {
  2138.         new += scalePtr->resolution;
  2139.     }
  2140.     }
  2141.     return new;
  2142. }
  2143.  
  2144. /*
  2145.  *----------------------------------------------------------------------
  2146.  *
  2147.  * ScaleVarProc --
  2148.  *
  2149.  *    This procedure is invoked by Tcl whenever someone modifies a
  2150.  *    variable associated with a scale widget.
  2151.  *
  2152.  * Results:
  2153.  *    NULL is always returned.
  2154.  *
  2155.  * Side effects:
  2156.  *    The value displayed in the scale will change to match the
  2157.  *    variable's new value.  If the variable has a bogus value then
  2158.  *    it is reset to the value of the scale.
  2159.  *
  2160.  *----------------------------------------------------------------------
  2161.  */
  2162.  
  2163.     /* ARGSUSED */
  2164. static char *
  2165. ScaleVarProc(clientData, interp, name1, name2, flags)
  2166.     ClientData clientData;    /* Information about button. */
  2167.     Tcl_Interp *interp;        /* Interpreter containing variable. */
  2168.     char *name1;        /* Name of variable. */
  2169.     char *name2;        /* Second part of variable name. */
  2170.     int flags;            /* Information about what happened. */
  2171. {
  2172.     register Scale *scalePtr = (Scale *) clientData;
  2173.     char *stringValue, *end, *result;
  2174.     double value;
  2175.  
  2176.     /*
  2177.      * If the variable is unset, then immediately recreate it unless
  2178.      * the whole interpreter is going away.
  2179.      */
  2180.  
  2181.     if (flags & TCL_TRACE_UNSETS) {
  2182.     if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  2183.         Tcl_TraceVar(interp, scalePtr->varName,
  2184.             TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  2185.             ScaleVarProc, clientData);
  2186.         scalePtr->flags |= NEVER_SET;
  2187.         SetScaleValue(scalePtr, scalePtr->value, 1, 0);
  2188.     }
  2189.     return (char *) NULL;
  2190.     }
  2191.  
  2192.     /*
  2193.      * If we came here because we updated the variable (in SetScaleValue),
  2194.      * then ignore the trace.  Otherwise update the scale with the value
  2195.      * of the variable.
  2196.      */
  2197.  
  2198.     if (scalePtr->flags & SETTING_VAR) {
  2199.     return (char *) NULL;
  2200.     }
  2201.     result = NULL;
  2202.     stringValue = Tcl_GetVar(interp, scalePtr->varName, TCL_GLOBAL_ONLY);
  2203.     if (stringValue != NULL) {
  2204.     value = strtod(stringValue, &end);
  2205.     if ((end == stringValue) || (*end != 0)) {
  2206.         result = "can't assign non-numeric value to scale variable";
  2207.     } else {
  2208.         scalePtr->value = RoundToResolution(scalePtr, value);
  2209.     }
  2210.  
  2211.     /*
  2212.      * This code is a bit tricky because it sets the scale's value before
  2213.      * calling SetScaleValue.  This way, SetScaleValue won't bother to
  2214.      * set the variable again or to invoke the -command.  However, it
  2215.      * also won't redisplay the scale, so we have to ask for that
  2216.      * explicitly.
  2217.      */
  2218.  
  2219.     SetScaleValue(scalePtr, scalePtr->value, 1, 0);
  2220.     EventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
  2221.     }
  2222.  
  2223.     return result;
  2224. }
  2225.