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