home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / tk3.3b1 / tkScale.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-02  |  44.9 KB  |  1,462 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.  * Copyright (c) 1990-1993 The Regents of the University of California.
  10.  * All rights reserved.
  11.  *
  12.  * Permission is hereby granted, without written agreement and without
  13.  * license or royalty fees, to use, copy, modify, and distribute this
  14.  * software and its documentation for any purpose, provided that the
  15.  * above copyright notice and the following two paragraphs appear in
  16.  * all copies of this software.
  17.  * 
  18.  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  19.  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  20.  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
  21.  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  22.  *
  23.  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  24.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  25.  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  26.  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
  27.  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  28.  */
  29.  
  30. #ifndef lint
  31. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkScale.c,v 1.38 93/07/02 08:45:11 ouster Exp $ SPRITE (Berkeley)";
  32. #endif
  33.  
  34. #include "tkConfig.h"
  35. #include "default.h"
  36. #include "tkInt.h"
  37.  
  38. /*
  39.  * A data structure of the following type is kept for each scale
  40.  * widget managed by this file:
  41.  */
  42.  
  43. typedef struct {
  44.     Tk_Window tkwin;        /* Window that embodies the scale.  NULL
  45.                  * means that the window has been destroyed
  46.                  * but the data structures haven't yet been
  47.                  * cleaned up.*/
  48.     Display *display;        /* Display containing widget.  Used, among
  49.                  * other things, so that resources can be
  50.                  * freed even after tkwin has gone away. */
  51.     Tcl_Interp *interp;        /* Interpreter associated with scale. */
  52.     Tk_Uid orientUid;        /* Orientation for window ("vertical" or
  53.                  * "horizontal"). */
  54.     int vertical;        /* Non-zero means vertical orientation,
  55.                  * zero means horizontal. */
  56.     int value;            /* Current value of scale. */
  57.     int fromValue;        /* Value corresponding to left or top of
  58.                  * scale. */
  59.     int toValue;        /* Value corresponding to right or bottom
  60.                  * of scale. */
  61.     int tickInterval;        /* Distance between tick marks;  0 means
  62.                  * don't display any tick marks. */
  63.     char *command;        /* Command prefix to use when invoking Tcl
  64.                  * commands because the scale value changed.
  65.                  * NULL means don't invoke commands.
  66.                  * Malloc'ed. */
  67.     int commandLength;        /* Number of non-NULL bytes in command. */
  68.     char *label;        /* Label to display above or to right of
  69.                  * scale;  NULL means don't display a
  70.                  * label.  Malloc'ed. */
  71.     int labelLength;        /* Number of non-NULL chars. in label. */
  72.     Tk_Uid state;        /* Normal or disabled.  Value cannot be
  73.                  * changed when scale is disabled. */
  74.  
  75.     /*
  76.      * Information used when displaying widget:
  77.      */
  78.  
  79.     int borderWidth;        /* Width of 3-D border around window. */
  80.     Tk_3DBorder bgBorder;    /* Used for drawing background. */
  81.     Tk_3DBorder sliderBorder;    /* Used for drawing slider in normal mode. */
  82.     Tk_3DBorder activeBorder;    /* Used for drawing slider when active (i.e.
  83.                  * when mouse is in window). */
  84.     XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  85.     XColor *textColorPtr;    /* Color for drawing text. */
  86.     GC textGC;            /* GC for drawing text in normal mode. */
  87.     int width;            /* Desired narrow dimension of scale,
  88.                  * in pixels. */
  89.     int length;            /* Desired long dimension of scale,
  90.                  * in pixels. */
  91.     int relief;            /* Indicates whether window as a whole is
  92.                  * raised, sunken, or flat. */
  93.     int offset;            /* Zero if relief is TK_RELIEF_FLAT,
  94.                  * borderWidth otherwise.   Indicates how
  95.                  * much interior stuff must be offset from
  96.                  * outside edges to leave room for border. */
  97.     int sliderLength;        /* Length of slider, measured in pixels along
  98.                  * long dimension of scale. */
  99.     int showValue;        /* Non-zero means to display the scale value
  100.                  * below or to the left of the slider;  zero
  101.                  * means don't display the value. */
  102.     int tickPixels;        /* Number of pixels required for widest tick
  103.                  * mark.  0 means don't display ticks.*/
  104.     int valuePixels;        /* Number of pixels required for value text. */
  105.     int labelPixels;        /* Number of pixels required for label.   0
  106.                  * means don't display label. */
  107.  
  108.     /*
  109.      * Miscellaneous information:
  110.      */
  111.  
  112.     Cursor cursor;        /* Current cursor for window, or None. */
  113.     int flags;            /* Various flags;  see below for
  114.                  * definitions. */
  115. } Scale;
  116.  
  117. /*
  118.  * Flag bits for scales:
  119.  *
  120.  * REDRAW_SLIDER -        1 means slider (and numerical readout) need
  121.  *                to be redrawn.
  122.  * REDRAW_OTHER -        1 means other stuff besides slider and value
  123.  *                need to be redrawn.
  124.  * REDRAW_ALL -            1 means the entire widget needs to be redrawn.
  125.  * ACTIVE -            1 means the widget is active (the mouse is
  126.  *                in its window).
  127.  * BUTTON_PRESSED -        1 means a button press is in progress, so
  128.  *                slider should appear depressed and should be
  129.  *                draggable.
  130.  * INVOKE_COMMAND -        1 means the scale's command needs to be
  131.  *                invoked during the next redisplay (the
  132.  *                value of the scale has changed since the
  133.  *                last time the command was invoked).
  134.  */
  135.  
  136. #define REDRAW_SLIDER        1
  137. #define REDRAW_OTHER        2
  138. #define REDRAW_ALL        3
  139. #define ACTIVE            4
  140. #define BUTTON_PRESSED        8
  141. #define INVOKE_COMMAND        16
  142.  
  143. /*
  144.  * Space to leave between scale area and text.
  145.  */
  146.  
  147. #define SPACING 2
  148.  
  149. /*
  150.  * Information used for argv parsing.
  151.  */
  152.  
  153.  
  154. static Tk_ConfigSpec configSpecs[] = {
  155.     {TK_CONFIG_BORDER, "-activeforeground", "activeForeground", "Background",
  156.     DEF_SCALE_ACTIVE_FG_COLOR, Tk_Offset(Scale, activeBorder),
  157.     TK_CONFIG_COLOR_ONLY},
  158.     {TK_CONFIG_BORDER, "-activeforeground", "activeForeground", "Background",
  159.     DEF_SCALE_ACTIVE_FG_MONO, Tk_Offset(Scale, activeBorder),
  160.     TK_CONFIG_MONO_ONLY},
  161.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  162.     DEF_SCALE_BG_COLOR, Tk_Offset(Scale, bgBorder),
  163.     TK_CONFIG_COLOR_ONLY},
  164.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  165.     DEF_SCALE_BG_MONO, Tk_Offset(Scale, bgBorder),
  166.     TK_CONFIG_MONO_ONLY},
  167.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  168.     (char *) NULL, 0, 0},
  169.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  170.     (char *) NULL, 0, 0},
  171.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  172.     DEF_SCALE_BORDER_WIDTH, Tk_Offset(Scale, borderWidth), 0},
  173.     {TK_CONFIG_STRING, "-command", "command", "Command",
  174.     DEF_SCALE_COMMAND, Tk_Offset(Scale, command), TK_CONFIG_NULL_OK},
  175.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  176.     DEF_SCALE_CURSOR, Tk_Offset(Scale, cursor), TK_CONFIG_NULL_OK},
  177.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  178.     (char *) NULL, 0, 0},
  179.     {TK_CONFIG_FONT, "-font", "font", "Font",
  180.     DEF_SCALE_FONT, Tk_Offset(Scale, fontPtr),
  181.     0},
  182.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  183.     DEF_SCALE_FG_COLOR, Tk_Offset(Scale, textColorPtr),
  184.     TK_CONFIG_COLOR_ONLY},
  185.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  186.     DEF_SCALE_FG_MONO, Tk_Offset(Scale, textColorPtr),
  187.     TK_CONFIG_MONO_ONLY},
  188.     {TK_CONFIG_INT, "-from", "from", "From",
  189.     DEF_SCALE_FROM, Tk_Offset(Scale, fromValue), 0},
  190.     {TK_CONFIG_STRING, "-label", "label", "Label",
  191.     DEF_SCALE_LABEL, Tk_Offset(Scale, label), TK_CONFIG_NULL_OK},
  192.     {TK_CONFIG_PIXELS, "-length", "length", "Length",
  193.     DEF_SCALE_LENGTH, Tk_Offset(Scale, length), 0},
  194.     {TK_CONFIG_UID, "-orient", "orient", "Orient",
  195.     DEF_SCALE_ORIENT, Tk_Offset(Scale, orientUid), 0},
  196.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  197.     DEF_SCALE_RELIEF, Tk_Offset(Scale, relief), 0},
  198.     {TK_CONFIG_BOOLEAN, "-showvalue", "showValue", "ShowValue",
  199.     DEF_SCALE_SHOW_VALUE, Tk_Offset(Scale, showValue), 0},
  200.     {TK_CONFIG_BORDER, "-sliderforeground", "sliderForeground", "Background",
  201.     DEF_SCALE_SLIDER_FG_COLOR, Tk_Offset(Scale, sliderBorder),
  202.     TK_CONFIG_COLOR_ONLY},
  203.     {TK_CONFIG_BORDER, "-sliderforeground", "sliderForeground", "Background",
  204.     DEF_SCALE_SLIDER_FG_MONO, Tk_Offset(Scale, sliderBorder), 
  205.     TK_CONFIG_MONO_ONLY},
  206.     {TK_CONFIG_PIXELS, "-sliderlength", "sliderLength", "SliderLength",
  207.     DEF_SCALE_SLIDER_LENGTH, Tk_Offset(Scale, sliderLength), 0},
  208.     {TK_CONFIG_UID, "-state", "state", "State",
  209.     DEF_SCALE_STATE, Tk_Offset(Scale, state), 0},
  210.     {TK_CONFIG_INT, "-tickinterval", "tickInterval", "TickInterval",
  211.     DEF_SCALE_TICK_INTERVAL, Tk_Offset(Scale, tickInterval), 0},
  212.     {TK_CONFIG_INT, "-to", "to", "To",
  213.     DEF_SCALE_TO, Tk_Offset(Scale, toValue), 0},
  214.     {TK_CONFIG_PIXELS, "-width", "width", "Width",
  215.     DEF_SCALE_WIDTH, Tk_Offset(Scale, width), 0},
  216.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  217.     (char *) NULL, 0, 0}
  218. };
  219.  
  220. /*
  221.  * Forward declarations for procedures defined later in this file:
  222.  */
  223.  
  224. static void        ComputeScaleGeometry _ANSI_ARGS_((Scale *scalePtr));
  225. static int        ConfigureScale _ANSI_ARGS_((Tcl_Interp *interp,
  226.                 Scale *scalePtr, int argc, char **argv,
  227.                 int flags));
  228. static void        DestroyScale _ANSI_ARGS_((ClientData clientData));
  229. static void        DisplayHorizontalScale _ANSI_ARGS_((
  230.                 ClientData clientData));
  231. static void        DisplayHorizontalValue _ANSI_ARGS_((Scale *scalePtr,
  232.                 int value, int bottom));
  233. static void        DisplayVerticalScale _ANSI_ARGS_((
  234.                 ClientData clientData));
  235. static void        DisplayVerticalValue _ANSI_ARGS_((Scale *scalePtr,
  236.                 int value, int rightEdge));
  237. static void        EventuallyRedrawScale _ANSI_ARGS_((Scale *scalePtr,
  238.                 int what));
  239. static int        PixelToValue _ANSI_ARGS_((Scale *scalePtr, int x,
  240.                 int y));
  241. static void        ScaleEventProc _ANSI_ARGS_((ClientData clientData,
  242.                 XEvent *eventPtr));
  243. static void        ScaleMouseProc _ANSI_ARGS_((ClientData clientData,
  244.                 XEvent *eventPtr));
  245. static int        ScaleWidgetCmd _ANSI_ARGS_((ClientData clientData,
  246.                 Tcl_Interp *interp, int argc, char **argv));
  247. static void        SetScaleValue _ANSI_ARGS_((Scale *scalePtr,
  248.                 int value));
  249. static int        ValueToPixel _ANSI_ARGS_((Scale *scalePtr, int value));
  250.  
  251. /*
  252.  *--------------------------------------------------------------
  253.  *
  254.  * Tk_ScaleCmd --
  255.  *
  256.  *    This procedure is invoked to process the "scale" Tcl
  257.  *    command.  See the user documentation for details on what
  258.  *    it does.
  259.  *
  260.  * Results:
  261.  *    A standard Tcl result.
  262.  *
  263.  * Side effects:
  264.  *    See the user documentation.
  265.  *
  266.  *--------------------------------------------------------------
  267.  */
  268.  
  269. int
  270. Tk_ScaleCmd(clientData, interp, argc, argv)
  271.     ClientData clientData;        /* Main window associated with
  272.                  * interpreter. */
  273.     Tcl_Interp *interp;        /* Current interpreter. */
  274.     int argc;            /* Number of arguments. */
  275.     char **argv;        /* Argument strings. */
  276. {
  277.     Tk_Window tkwin = (Tk_Window) clientData;
  278.     register Scale *scalePtr;
  279.     Tk_Window new;
  280.  
  281.     if (argc < 2) {
  282.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  283.         argv[0], " pathName ?options?\"", (char *) NULL);
  284.     return TCL_ERROR;
  285.     }
  286.  
  287.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  288.     if (new == NULL) {
  289.     return TCL_ERROR;
  290.     }
  291.  
  292.     /*
  293.      * Initialize fields that won't be initialized by ConfigureScale,
  294.      * or which ConfigureScale expects to have reasonable values
  295.      * (e.g. resource pointers).
  296.      */
  297.  
  298.     scalePtr = (Scale *) ckalloc(sizeof(Scale));
  299.     scalePtr->tkwin = new;
  300.     scalePtr->display = Tk_Display(new);
  301.     scalePtr->interp = interp;
  302.     scalePtr->value = 0;
  303.     scalePtr->state = tkNormalUid;
  304.     scalePtr->textGC = None;
  305.     scalePtr->flags = 0;
  306.  
  307.     Tk_SetClass(scalePtr->tkwin, "Scale");
  308.     Tk_CreateEventHandler(scalePtr->tkwin, ExposureMask|StructureNotifyMask,
  309.         ScaleEventProc, (ClientData) scalePtr);
  310.     Tk_CreateEventHandler(scalePtr->tkwin, EnterWindowMask|LeaveWindowMask
  311.         |PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
  312.         ScaleMouseProc, (ClientData) scalePtr);
  313.     Tcl_CreateCommand(interp, Tk_PathName(scalePtr->tkwin), ScaleWidgetCmd,
  314.         (ClientData) scalePtr, (void (*)()) NULL);
  315.     if (ConfigureScale(interp, scalePtr, argc-2, argv+2, 0) != TCL_OK) {
  316.     goto error;
  317.     }
  318.  
  319.     interp->result = Tk_PathName(scalePtr->tkwin);
  320.     return TCL_OK;
  321.  
  322.     error:
  323.     Tk_DestroyWindow(scalePtr->tkwin);
  324.     return TCL_ERROR;
  325. }
  326.  
  327. /*
  328.  *--------------------------------------------------------------
  329.  *
  330.  * ScaleWidgetCmd --
  331.  *
  332.  *    This procedure is invoked to process the Tcl command
  333.  *    that corresponds to a widget managed by this module.
  334.  *    See the user documentation for details on what it does.
  335.  *
  336.  * Results:
  337.  *    A standard Tcl result.
  338.  *
  339.  * Side effects:
  340.  *    See the user documentation.
  341.  *
  342.  *--------------------------------------------------------------
  343.  */
  344.  
  345. static int
  346. ScaleWidgetCmd(clientData, interp, argc, argv)
  347.     ClientData clientData;        /* Information about scale
  348.                      * widget. */
  349.     Tcl_Interp *interp;            /* Current interpreter. */
  350.     int argc;                /* Number of arguments. */
  351.     char **argv;            /* Argument strings. */
  352. {
  353.     register Scale *scalePtr = (Scale *) clientData;
  354.     int result = TCL_OK;
  355.     int length;
  356.     char c;
  357.  
  358.     if (argc < 2) {
  359.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  360.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  361.     return TCL_ERROR;
  362.     }
  363.     Tk_Preserve((ClientData) scalePtr);
  364.     c = argv[1][0];
  365.     length = strlen(argv[1]);
  366.     if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  367.     if (argc == 2) {
  368.         result = Tk_ConfigureInfo(interp, scalePtr->tkwin, configSpecs,
  369.             (char *) scalePtr, (char *) NULL, 0);
  370.     } else if (argc == 3) {
  371.         result = Tk_ConfigureInfo(interp, scalePtr->tkwin, configSpecs,
  372.             (char *) scalePtr, argv[2], 0);
  373.     } else {
  374.         result = ConfigureScale(interp, scalePtr, argc-2, argv+2,
  375.             TK_CONFIG_ARGV_ONLY);
  376.     }
  377.     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
  378.     if (argc != 2) {
  379.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  380.             argv[0], " get\"", (char *) NULL);
  381.         goto error;
  382.     }
  383.     sprintf(interp->result, "%d", scalePtr->value);
  384.     } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
  385.     int value;
  386.  
  387.     if (argc != 3) {
  388.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  389.             argv[0], " set value\"", (char *) NULL);
  390.         goto error;
  391.     }
  392.     if (Tcl_GetInt(interp, argv[2], &value) != TCL_OK) {
  393.         goto error;
  394.     }
  395.     if (scalePtr->state == tkNormalUid) {
  396.         if ((value < scalePtr->fromValue)
  397.             ^ (scalePtr->toValue < scalePtr->fromValue)) {
  398.         value = scalePtr->fromValue;
  399.         }
  400.         if ((value > scalePtr->toValue)
  401.             ^ (scalePtr->toValue < scalePtr->fromValue)) {
  402.         value = scalePtr->toValue;
  403.         }
  404.         SetScaleValue(scalePtr, value);
  405.     }
  406.     } else {
  407.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  408.         "\":  must be configure, get, or set", (char *) NULL);
  409.     goto error;
  410.     }
  411.     Tk_Release((ClientData) scalePtr);
  412.     return result;
  413.  
  414.     error:
  415.     Tk_Release((ClientData) scalePtr);
  416.     return TCL_ERROR;
  417. }
  418.  
  419. /*
  420.  *----------------------------------------------------------------------
  421.  *
  422.  * DestroyScale --
  423.  *
  424.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  425.  *    to clean up the internal structure of a button at a safe time
  426.  *    (when no-one is using it anymore).
  427.  *
  428.  * Results:
  429.  *    None.
  430.  *
  431.  * Side effects:
  432.  *    Everything associated with the scale is freed up.
  433.  *
  434.  *----------------------------------------------------------------------
  435.  */
  436.  
  437. static void
  438. DestroyScale(clientData)
  439.     ClientData clientData;    /* Info about scale widget. */
  440. {
  441.     register Scale *scalePtr = (Scale *) clientData;
  442.  
  443.     /*
  444.      * Free up all the stuff that requires special handling, then
  445.      * let Tk_FreeOptions handle all the standard option-related
  446.      * stuff.
  447.      */
  448.  
  449.     if (scalePtr->textGC != None) {
  450.     Tk_FreeGC(scalePtr->display, scalePtr->textGC);
  451.     }
  452.     Tk_FreeOptions(configSpecs, (char *) scalePtr, scalePtr->display, 0);
  453.     ckfree((char *) scalePtr);
  454. }
  455.  
  456. /*
  457.  *----------------------------------------------------------------------
  458.  *
  459.  * ConfigureScale --
  460.  *
  461.  *    This procedure is called to process an argv/argc list, plus
  462.  *    the Tk option database, in order to configure (or
  463.  *    reconfigure) a scale widget.
  464.  *
  465.  * Results:
  466.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  467.  *    returned, then interp->result contains an error message.
  468.  *
  469.  * Side effects:
  470.  *    Configuration information, such as colors, border width,
  471.  *    etc. get set for scalePtr;  old resources get freed,
  472.  *    if there were any.
  473.  *
  474.  *----------------------------------------------------------------------
  475.  */
  476.  
  477. static int
  478. ConfigureScale(interp, scalePtr, argc, argv, flags)
  479.     Tcl_Interp *interp;        /* Used for error reporting. */
  480.     register Scale *scalePtr;    /* Information about widget;  may or may
  481.                  * not already have values for some fields. */
  482.     int argc;            /* Number of valid entries in argv. */
  483.     char **argv;        /* Arguments. */
  484.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  485. {
  486.     XGCValues gcValues;
  487.     GC newGC;
  488.     int length;
  489.  
  490.     if (Tk_ConfigureWidget(interp, scalePtr->tkwin, configSpecs,
  491.         argc, argv, (char *) scalePtr, flags) != TCL_OK) {
  492.     return TCL_ERROR;
  493.     }
  494.  
  495.     /*
  496.      * A few options need special processing, such as parsing the
  497.      * orientation or setting the background from a 3-D border.
  498.      */
  499.  
  500.     length = strlen(scalePtr->orientUid);
  501.     if (strncmp(scalePtr->orientUid, "vertical", length) == 0) {
  502.     scalePtr->vertical = 1;
  503.     } else if (strncmp(scalePtr->orientUid, "horizontal", length) == 0) {
  504.     scalePtr->vertical = 0;
  505.     } else {
  506.     Tcl_AppendResult(interp, "bad orientation \"", scalePtr->orientUid,
  507.         "\": must be vertical or horizontal", (char *) NULL);
  508.     return TCL_ERROR;
  509.     }
  510.  
  511.     if ((scalePtr->state != tkNormalUid)
  512.         && (scalePtr->state != tkDisabledUid)) {
  513.     Tcl_AppendResult(interp, "bad state value \"", scalePtr->state,
  514.         "\":  must be normal or disabled", (char *) NULL);
  515.     scalePtr->state = tkNormalUid;
  516.     return TCL_ERROR;
  517.     }
  518.  
  519.     /*
  520.      * Make sure that the tick interval has the right sign so that
  521.      * addition moves from fromValue to toValue.
  522.      */
  523.  
  524.     if ((scalePtr->tickInterval < 0)
  525.         ^ ((scalePtr->toValue - scalePtr->fromValue) <  0)) {
  526.     scalePtr->tickInterval = -scalePtr->tickInterval;
  527.     }
  528.  
  529.     /*
  530.      * Set the scale value to itself;  all this does is to make sure
  531.      * that the scale's value is within the new acceptable range for
  532.      * the scale.
  533.      */
  534.  
  535.     SetScaleValue(scalePtr, scalePtr->value);
  536.  
  537.     if (scalePtr->command != NULL) {
  538.     scalePtr->commandLength = strlen(scalePtr->command);
  539.     } else {
  540.     scalePtr->commandLength = 0;
  541.     }
  542.  
  543.     if (scalePtr->label != NULL) {
  544.     scalePtr->labelLength = strlen(scalePtr->label);
  545.     } else {
  546.     scalePtr->labelLength = 0;
  547.     }
  548.  
  549.     Tk_SetBackgroundFromBorder(scalePtr->tkwin, scalePtr->bgBorder);
  550.  
  551.     gcValues.font = scalePtr->fontPtr->fid;
  552.     gcValues.foreground = scalePtr->textColorPtr->pixel;
  553.     newGC = Tk_GetGC(scalePtr->tkwin, GCForeground|GCFont, &gcValues);
  554.     if (scalePtr->textGC != None) {
  555.     Tk_FreeGC(scalePtr->display, scalePtr->textGC);
  556.     }
  557.     scalePtr->textGC = newGC;
  558.  
  559.     if (scalePtr->relief != TK_RELIEF_FLAT) {
  560.     scalePtr->offset = scalePtr->borderWidth;
  561.     } else {
  562.     scalePtr->offset = 0;
  563.     }
  564.  
  565.     /*
  566.      * Recompute display-related information, and let the geometry
  567.      * manager know how much space is needed now.
  568.      */
  569.  
  570.     ComputeScaleGeometry(scalePtr);
  571.  
  572.     EventuallyRedrawScale(scalePtr, REDRAW_ALL);
  573.     return TCL_OK;
  574. }
  575.  
  576. /*
  577.  *----------------------------------------------------------------------
  578.  *
  579.  * ComputeScaleGeometry --
  580.  *
  581.  *    This procedure is called to compute various geometrical
  582.  *    information for a scale, such as where various things get
  583.  *    displayed.  It's called when the window is reconfigured.
  584.  *
  585.  * Results:
  586.  *    None.
  587.  *
  588.  * Side effects:
  589.  *    Display-related numbers get changed in *scrollPtr.  The
  590.  *    geometry manager gets told about the window's preferred size.
  591.  *
  592.  *----------------------------------------------------------------------
  593.  */
  594.  
  595. static void
  596. ComputeScaleGeometry(scalePtr)
  597.     register Scale *scalePtr;        /* Information about widget. */
  598. {
  599.     XCharStruct bbox;
  600.     char valueString[30];
  601.     int dummy, lineHeight;
  602.  
  603.     /*
  604.      * Horizontal scales are simpler than vertical ones because
  605.      * all sizes are the same (the height of a line of text);
  606.      * handle them first and then quit.
  607.      */
  608.  
  609.     if (!scalePtr->vertical) {
  610.     lineHeight = scalePtr->fontPtr->ascent + scalePtr->fontPtr->descent;
  611.     if (scalePtr->tickInterval != 0) {
  612.         scalePtr->tickPixels = lineHeight;
  613.     } else {
  614.         scalePtr->tickPixels = 0;
  615.     }
  616.     if (scalePtr->showValue) {
  617.         scalePtr->valuePixels = lineHeight + SPACING;
  618.     } else {
  619.         scalePtr->valuePixels = 0;
  620.     }
  621.     if (scalePtr->labelLength != 0) {
  622.         scalePtr->labelPixels = lineHeight;
  623.     } else {
  624.         scalePtr->labelPixels = 0;
  625.     }
  626.  
  627.     Tk_GeometryRequest(scalePtr->tkwin,
  628.         scalePtr->length + 2*scalePtr->offset,
  629.         scalePtr->tickPixels + scalePtr->valuePixels
  630.         + scalePtr->width + 2*scalePtr->borderWidth
  631.         + scalePtr->labelPixels + 2*scalePtr->offset);
  632.     Tk_SetInternalBorder(scalePtr->tkwin, scalePtr->borderWidth);
  633.     return;
  634.     }
  635.  
  636.     /*
  637.      * Vertical scale:  compute the amount of space needed for tick marks
  638.      * and current value by formatting strings for the two end points;
  639.      * use whichever length is longer.
  640.      */
  641.  
  642.     sprintf(valueString, "%d", scalePtr->fromValue);
  643.     XTextExtents(scalePtr->fontPtr, valueString, strlen(valueString),
  644.         &dummy, &dummy, &dummy, &bbox);
  645.     scalePtr->tickPixels = bbox.rbearing - bbox.lbearing;
  646.     sprintf(valueString, "%d", scalePtr->toValue);
  647.     XTextExtents(scalePtr->fontPtr, valueString, strlen(valueString),
  648.         &dummy, &dummy, &dummy, &bbox);
  649.     if (scalePtr->tickPixels < bbox.rbearing - bbox.lbearing) {
  650.     scalePtr->tickPixels = bbox.rbearing - bbox.lbearing;
  651.     }
  652.  
  653.     /*
  654.      * Pad the value with a bit of extra space for prettier printing.
  655.      */
  656.  
  657.     scalePtr->tickPixels += scalePtr->fontPtr->ascent/2;
  658.     scalePtr->valuePixels = scalePtr->tickPixels;
  659.     if (scalePtr->tickInterval == 0) {
  660.     scalePtr->tickPixels = 0;
  661.     }
  662.     if (!scalePtr->showValue) {
  663.     scalePtr->valuePixels = 0;
  664.     }
  665.  
  666.     if (scalePtr->labelLength == 0) {
  667.     scalePtr->labelPixels = 0;
  668.     } else {
  669.     XTextExtents(scalePtr->fontPtr, scalePtr->label,
  670.         scalePtr->labelLength, &dummy, &dummy, &dummy, &bbox);
  671.     scalePtr->labelPixels = bbox.rbearing - bbox.lbearing
  672.         + scalePtr->fontPtr->ascent;
  673.     }
  674.     Tk_GeometryRequest(scalePtr->tkwin, 4*scalePtr->borderWidth
  675.         + scalePtr->tickPixels + scalePtr->valuePixels + SPACING
  676.         + scalePtr->width + scalePtr->labelPixels,
  677.         scalePtr->length);
  678.     Tk_SetInternalBorder(scalePtr->tkwin, scalePtr->borderWidth);
  679. }
  680.  
  681. /*
  682.  *--------------------------------------------------------------
  683.  *
  684.  * DisplayVerticalScale --
  685.  *
  686.  *    This procedure redraws the contents of a vertical scale
  687.  *    window.  It is invoked as a do-when-idle handler, so it only
  688.  *    runs when there's nothing else for the application to do.
  689.  *
  690.  * Results:
  691.  *    None.
  692.  *
  693.  * Side effects:
  694.  *    Information appears on the screen.
  695.  *
  696.  *--------------------------------------------------------------
  697.  */
  698.  
  699. static void
  700. DisplayVerticalScale(clientData)
  701.     ClientData clientData;    /* Information about widget. */
  702. {
  703.     register Scale *scalePtr = (Scale *) clientData;
  704.     register Tk_Window tkwin = scalePtr->tkwin;
  705.     int tickRightEdge, valueRightEdge, labelLeftEdge, scaleLeftEdge;
  706.     int totalPixels, x, y, width, height, shadowWidth, tickValue;
  707.     int relief, result;
  708.     Tk_3DBorder sliderBorder;
  709.     char string[20];
  710.  
  711.     if ((scalePtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  712.     goto done;
  713.     }
  714.  
  715.     /*
  716.      * Invoke the scale's command if needed.
  717.      */
  718.  
  719.     if ((scalePtr->flags & INVOKE_COMMAND) && (scalePtr->command != NULL)) {
  720.     sprintf(string, " %d", scalePtr->value);
  721.     result = Tcl_VarEval(scalePtr->interp, scalePtr->command, string,
  722.         (char *) NULL);
  723.     if (result != TCL_OK) {
  724.         Tcl_AddErrorInfo(scalePtr->interp,
  725.             "\n    (command executed by scale)");
  726.         Tk_BackgroundError(scalePtr->interp);
  727.     }
  728.     }
  729.     scalePtr->flags &= ~INVOKE_COMMAND;
  730.  
  731.     /*
  732.      * Scanning from left to right across the window, the window
  733.      * will contain four columns:  ticks, value, scale, and label.
  734.      * Compute the x-coordinate for each of the columns.
  735.      */
  736.  
  737.     totalPixels = scalePtr->tickPixels + scalePtr->valuePixels
  738.         + 2*scalePtr->borderWidth + scalePtr->width
  739.         + 2*SPACING + scalePtr->labelPixels;
  740.     tickRightEdge = (Tk_Width(tkwin) - totalPixels)/2 + scalePtr->tickPixels;
  741.     valueRightEdge = tickRightEdge + scalePtr->valuePixels;
  742.     scaleLeftEdge = valueRightEdge + SPACING;
  743.     labelLeftEdge = scaleLeftEdge + 2*scalePtr->borderWidth
  744.         + scalePtr->width + scalePtr->fontPtr->ascent/2;
  745.  
  746.     /*
  747.      * Display the information from left to right across the window.
  748.      */
  749.  
  750.     if (scalePtr->flags & REDRAW_OTHER) {
  751.     XClearWindow(scalePtr->display, Tk_WindowId(tkwin));
  752.  
  753.     /*
  754.      * Display the tick marks.
  755.      */
  756.  
  757.     if (scalePtr->tickPixels != 0) {
  758.         for (tickValue = scalePtr->fromValue; ;
  759.             tickValue += scalePtr->tickInterval) {
  760.         if (scalePtr->toValue > scalePtr->fromValue) {
  761.             if (tickValue > scalePtr->toValue) {
  762.             break;
  763.             }
  764.         } else {
  765.             if (tickValue < scalePtr->toValue) {
  766.             break;
  767.             }
  768.         }
  769.         DisplayVerticalValue(scalePtr, tickValue, tickRightEdge);
  770.         }
  771.     }
  772.     }
  773.  
  774.     /*
  775.      * Display the value, if it is desired.  If not redisplaying the
  776.      * entire window, clear the area of the value to get rid of the
  777.      * old value displayed there.
  778.      */
  779.  
  780.     if (scalePtr->showValue) {
  781.     if (!(scalePtr->flags & REDRAW_OTHER)) {
  782.         XClearArea(scalePtr->display, Tk_WindowId(tkwin),
  783.             valueRightEdge-scalePtr->valuePixels, scalePtr->offset,
  784.             scalePtr->valuePixels,
  785.             Tk_Height(tkwin) - 2*scalePtr->offset, False);
  786.     }
  787.     DisplayVerticalValue(scalePtr, scalePtr->value, valueRightEdge);
  788.     }
  789.  
  790.     /*
  791.      * Display the scale and the slider.  If not redisplaying the
  792.      * entire window, must clear the trench area to erase the old
  793.      * slider, but don't need to redraw the border.
  794.      */
  795.  
  796.     if (scalePtr->flags & REDRAW_OTHER) {
  797.     Tk_Draw3DRectangle(scalePtr->display, Tk_WindowId(tkwin),
  798.         scalePtr->bgBorder, scaleLeftEdge, scalePtr->offset,
  799.         scalePtr->width + 2*scalePtr->borderWidth,
  800.         Tk_Height(tkwin) - 2*scalePtr->offset, scalePtr->borderWidth,
  801.         TK_RELIEF_SUNKEN);
  802.     } else {
  803.     XClearArea(scalePtr->display, Tk_WindowId(tkwin),
  804.         scaleLeftEdge + scalePtr->borderWidth,
  805.         scalePtr->offset + scalePtr->borderWidth,
  806.         scalePtr->width,
  807.         Tk_Height(tkwin) - 2*scalePtr->offset
  808.         - 2*scalePtr->borderWidth, False);
  809.     }
  810.     if (scalePtr->flags & ACTIVE) {
  811.     sliderBorder = scalePtr->activeBorder;
  812.     } else {
  813.     sliderBorder = scalePtr->sliderBorder;
  814.     }
  815.     width = scalePtr->width;
  816.     height = scalePtr->sliderLength/2;
  817.     x = scaleLeftEdge + scalePtr->borderWidth;
  818.     y = ValueToPixel(scalePtr, scalePtr->value) - height;
  819.     shadowWidth = scalePtr->borderWidth/2;
  820.     if (shadowWidth == 0) {
  821.     shadowWidth = 1;
  822.     }
  823.     relief = (scalePtr->flags & BUTTON_PRESSED) ? TK_RELIEF_SUNKEN
  824.         : TK_RELIEF_RAISED;
  825.     Tk_Draw3DRectangle(scalePtr->display, Tk_WindowId(tkwin), sliderBorder,
  826.         x, y, width, 2*height, shadowWidth, relief);
  827.     x += shadowWidth;
  828.     y += shadowWidth;
  829.     width -= 2*shadowWidth;
  830.     height -= shadowWidth;
  831.     Tk_Fill3DRectangle(scalePtr->display, Tk_WindowId(tkwin), sliderBorder,
  832.         x, y, width, height, shadowWidth, relief);
  833.     Tk_Fill3DRectangle(scalePtr->display, Tk_WindowId(tkwin), sliderBorder,
  834.         x, y+height, width, height, shadowWidth, relief);
  835.  
  836.     /*
  837.      * Draw the label to the right of the scale.
  838.      */
  839.  
  840.     if ((scalePtr->flags & REDRAW_OTHER) && (scalePtr->labelPixels != 0)) {
  841.     XDrawString(scalePtr->display, Tk_WindowId(scalePtr->tkwin),
  842.         scalePtr->textGC, labelLeftEdge,
  843.         scalePtr->offset + (3*scalePtr->fontPtr->ascent)/2,
  844.         scalePtr->label, scalePtr->labelLength);
  845.     }
  846.  
  847.     /*
  848.      * Draw the window border.
  849.      */
  850.  
  851.     if ((scalePtr->flags & REDRAW_OTHER)
  852.         && (scalePtr->relief != TK_RELIEF_FLAT)) {
  853.     Tk_Draw3DRectangle(scalePtr->display, Tk_WindowId(tkwin),
  854.         scalePtr->bgBorder, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  855.         scalePtr->borderWidth, scalePtr->relief);
  856.     }
  857.  
  858.     done:
  859.     scalePtr->flags &= ~REDRAW_ALL;
  860. }
  861.  
  862. /*
  863.  *----------------------------------------------------------------------
  864.  *
  865.  * DisplayVerticalValue --
  866.  *
  867.  *    This procedure is called to display values (scale readings)
  868.  *    for vertically-oriented scales.
  869.  *
  870.  * Results:
  871.  *    None.
  872.  *
  873.  * Side effects:
  874.  *    The numerical value corresponding to value is displayed with
  875.  *    its right edge at "rightEdge", and at a vertical position in
  876.  *    the scale that corresponds to "value".
  877.  *
  878.  *----------------------------------------------------------------------
  879.  */
  880.  
  881. static void
  882. DisplayVerticalValue(scalePtr, value, rightEdge)
  883.     register Scale *scalePtr;    /* Information about widget in which to
  884.                  * display value. */
  885.     int value;            /* Y-coordinate of number to display,
  886.                  * specified in application coords, not
  887.                  * in pixels (we'll compute pixels). */
  888.     int rightEdge;        /* X-coordinate of right edge of text,
  889.                  * specified in pixels. */
  890. {
  891.     register Tk_Window tkwin = scalePtr->tkwin;
  892.     int y, dummy, length;
  893.     char valueString[30];
  894.     XCharStruct bbox;
  895.  
  896.     y = ValueToPixel(scalePtr, value) + scalePtr->fontPtr->ascent/2;
  897.     sprintf(valueString, "%d", value);
  898.     length = strlen(valueString);
  899.     XTextExtents(scalePtr->fontPtr, valueString, length,
  900.         &dummy, &dummy, &dummy, &bbox);
  901.  
  902.     /*
  903.      * Adjust the y-coordinate if necessary to keep the text entirely
  904.      * inside the window.
  905.      */
  906.  
  907.     if ((y - bbox.ascent) < scalePtr->offset) {
  908.     y = scalePtr->offset + bbox.ascent;
  909.     }
  910.     if ((y + bbox.descent) > (Tk_Height(tkwin) - scalePtr->offset)) {
  911.     y = Tk_Height(tkwin) - scalePtr->offset - bbox.descent;
  912.     }
  913.     XDrawString(scalePtr->display, Tk_WindowId(tkwin),
  914.         scalePtr->textGC, rightEdge - bbox.rbearing,
  915.         y, valueString, length);
  916. }
  917.  
  918. /*
  919.  *--------------------------------------------------------------
  920.  *
  921.  * DisplayHorizontalScale --
  922.  *
  923.  *    This procedure redraws the contents of a horizontal scale
  924.  *    window.  It is invoked as a do-when-idle handler, so it only
  925.  *    runs when there's nothing else for the application to do.
  926.  *
  927.  * Results:
  928.  *    None.
  929.  *
  930.  * Side effects:
  931.  *    Information appears on the screen.
  932.  *
  933.  *--------------------------------------------------------------
  934.  */
  935.  
  936. static void
  937. DisplayHorizontalScale(clientData)
  938.     ClientData clientData;    /* Information about widget. */
  939. {
  940.     register Scale *scalePtr = (Scale *) clientData;
  941.     register Tk_Window tkwin = scalePtr->tkwin;
  942.     int tickBottom, valueBottom, labelBottom, scaleBottom;
  943.     int totalPixels, x, y, width, height, shadowWidth, tickValue;
  944.     int relief, result;
  945.     Tk_3DBorder sliderBorder;
  946.     char string[20];
  947.  
  948.     if ((scalePtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  949.     goto done;
  950.     }
  951.  
  952.     /*
  953.      * Invoke the scale's command if needed.
  954.      */
  955.  
  956.     if ((scalePtr->flags & INVOKE_COMMAND) && (scalePtr->command != NULL)) {
  957.     sprintf(string, " %d", scalePtr->value);
  958.     result = Tcl_VarEval(scalePtr->interp, scalePtr->command, string,
  959.         (char *) NULL);
  960.     if (result != TCL_OK) {
  961.         Tcl_AddErrorInfo(scalePtr->interp,
  962.             "\n    (command executed by scale)");
  963.         Tk_BackgroundError(scalePtr->interp);
  964.     }
  965.     }
  966.     scalePtr->flags &= ~INVOKE_COMMAND;
  967.  
  968.     /*
  969.      * Scanning from bottom to top across the window, the window
  970.      * will contain four rows:  ticks, value, scale, and label.
  971.      * Compute the y-coordinate for each of the rows.
  972.      */
  973.  
  974.     totalPixels = scalePtr->tickPixels + scalePtr->valuePixels
  975.         + 2*scalePtr->borderWidth + scalePtr->width
  976.         + scalePtr->labelPixels;
  977.     tickBottom = (Tk_Height(tkwin) + totalPixels)/2 - 1;
  978.     valueBottom = tickBottom - scalePtr->tickPixels;
  979.     scaleBottom = valueBottom - scalePtr->valuePixels;
  980.     labelBottom = scaleBottom - 2*scalePtr->borderWidth - scalePtr->width;
  981.  
  982.     /*
  983.      * Display the information from bottom to top across the window.
  984.      */
  985.  
  986.     if (scalePtr->flags & REDRAW_OTHER) {
  987.     XClearWindow(scalePtr->display, Tk_WindowId(tkwin));
  988.  
  989.     /*
  990.      * Display the tick marks.
  991.      */
  992.  
  993.     if (scalePtr->tickPixels != 0) {
  994.         for (tickValue = scalePtr->fromValue; ;
  995.             tickValue += scalePtr->tickInterval) {
  996.         if (scalePtr->toValue > scalePtr->fromValue) {
  997.             if (tickValue > scalePtr->toValue) {
  998.             break;
  999.             }
  1000.         } else {
  1001.             if (tickValue < scalePtr->toValue) {
  1002.             break;
  1003.             }
  1004.         }
  1005.         DisplayHorizontalValue(scalePtr, tickValue, tickBottom);
  1006.         }
  1007.     }
  1008.     }
  1009.  
  1010.     /*
  1011.      * Display the value, if it is desired.  If not redisplaying the
  1012.      * entire window, clear the area of the value to get rid of the
  1013.      * old value displayed there.
  1014.      */
  1015.  
  1016.     if (scalePtr->showValue) {
  1017.     if (!(scalePtr->flags & REDRAW_OTHER)) {
  1018.         XClearArea(scalePtr->display, Tk_WindowId(tkwin),
  1019.             scalePtr->offset, scaleBottom + 1,
  1020.             Tk_Width(tkwin) - 2*scalePtr->offset,
  1021.             valueBottom - scaleBottom, False);
  1022.     }
  1023.     DisplayHorizontalValue(scalePtr, scalePtr->value, valueBottom);
  1024.     }
  1025.  
  1026.     /*
  1027.      * Display the scale and the slider.  If not redisplaying the
  1028.      * entire window, must clear the trench area to erase the old
  1029.      * slider, but don't need to redraw the border.
  1030.      */
  1031.  
  1032.     y = scaleBottom - 2*scalePtr->borderWidth - scalePtr->width + 1;
  1033.     if (scalePtr->flags & REDRAW_OTHER) {
  1034.     Tk_Draw3DRectangle(scalePtr->display, Tk_WindowId(tkwin),
  1035.         scalePtr->bgBorder, scalePtr->offset, y,
  1036.         Tk_Width(tkwin) - 2*scalePtr->offset,
  1037.         scalePtr->width + 2*scalePtr->borderWidth,
  1038.         scalePtr->borderWidth, TK_RELIEF_SUNKEN);
  1039.     } else {
  1040.     XClearArea(scalePtr->display, Tk_WindowId(tkwin),
  1041.         scalePtr->offset + scalePtr->borderWidth,
  1042.         y + scalePtr->borderWidth,
  1043.         Tk_Width(tkwin) - 2*scalePtr->offset - 2*scalePtr->borderWidth,
  1044.         scalePtr->width, False);
  1045.     }
  1046.     if (scalePtr->flags & ACTIVE) {
  1047.     sliderBorder = scalePtr->activeBorder;
  1048.     } else {
  1049.     sliderBorder = scalePtr->sliderBorder;
  1050.     }
  1051.     width = scalePtr->sliderLength/2;
  1052.     height = scalePtr->width;
  1053.     x = ValueToPixel(scalePtr, scalePtr->value) - width;
  1054.     y += scalePtr->borderWidth;
  1055.     shadowWidth = scalePtr->borderWidth/2;
  1056.     if (shadowWidth == 0) {
  1057.     shadowWidth = 1;
  1058.     }
  1059.     relief = (scalePtr->flags & BUTTON_PRESSED) ? TK_RELIEF_SUNKEN
  1060.         : TK_RELIEF_RAISED;
  1061.     Tk_Draw3DRectangle(scalePtr->display, Tk_WindowId(tkwin), sliderBorder,
  1062.         x, y, 2*width, height, shadowWidth, relief);
  1063.     x += shadowWidth;
  1064.     y += shadowWidth;
  1065.     width -= shadowWidth;
  1066.     height -= 2*shadowWidth;
  1067.     Tk_Fill3DRectangle(scalePtr->display, Tk_WindowId(tkwin), sliderBorder,
  1068.         x, y, width, height, shadowWidth, relief);
  1069.     Tk_Fill3DRectangle(scalePtr->display, Tk_WindowId(tkwin), sliderBorder,
  1070.         x+width, y, width, height, shadowWidth, relief);
  1071.  
  1072.     /*
  1073.      * Draw the label to the top of the scale.
  1074.      */
  1075.  
  1076.     if ((scalePtr->flags & REDRAW_OTHER) && (scalePtr->labelPixels != 0)) {
  1077.     XDrawString(scalePtr->display, Tk_WindowId(scalePtr->tkwin),
  1078.         scalePtr->textGC, scalePtr->offset + scalePtr->fontPtr->ascent/2,
  1079.         labelBottom - scalePtr->fontPtr->descent,
  1080.         scalePtr->label, scalePtr->labelLength);
  1081.     }
  1082.  
  1083.     /*
  1084.      * Draw the window border.
  1085.      */
  1086.  
  1087.     if ((scalePtr->flags & REDRAW_OTHER)
  1088.         && (scalePtr->relief != TK_RELIEF_FLAT)) {
  1089.     Tk_Draw3DRectangle(scalePtr->display, Tk_WindowId(tkwin),
  1090.         scalePtr->bgBorder, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  1091.         scalePtr->borderWidth, scalePtr->relief);
  1092.     }
  1093.  
  1094.     done:
  1095.     scalePtr->flags &= ~REDRAW_ALL;
  1096. }
  1097.  
  1098. /*
  1099.  *----------------------------------------------------------------------
  1100.  *
  1101.  * DisplayHorizontalValue --
  1102.  *
  1103.  *    This procedure is called to display values (scale readings)
  1104.  *    for horizontally-oriented scales.
  1105.  *
  1106.  * Results:
  1107.  *    None.
  1108.  *
  1109.  * Side effects:
  1110.  *    The numerical value corresponding to value is displayed with
  1111.  *    its bottom edge at "bottom", and at a horizontal position in
  1112.  *    the scale that corresponds to "value".
  1113.  *
  1114.  *----------------------------------------------------------------------
  1115.  */
  1116.  
  1117. static void
  1118. DisplayHorizontalValue(scalePtr, value, bottom)
  1119.     register Scale *scalePtr;    /* Information about widget in which to
  1120.                  * display value. */
  1121.     int value;            /* Y-coordinate of number to display,
  1122.                  * specified in application coords, not
  1123.                  * in pixels (we'll compute pixels). */
  1124.     int bottom;            /* Y-coordinate of bottom edge of text,
  1125.                  * specified in pixels. */
  1126. {
  1127.     register Tk_Window tkwin = scalePtr->tkwin;
  1128.     int x, y, dummy, length;
  1129.     char valueString[30];
  1130.     XCharStruct bbox;
  1131.  
  1132.     x = ValueToPixel(scalePtr, value);
  1133.     y = bottom - scalePtr->fontPtr->descent;
  1134.     sprintf(valueString, "%d", value);
  1135.     length = strlen(valueString);
  1136.     XTextExtents(scalePtr->fontPtr, valueString, length,
  1137.         &dummy, &dummy, &dummy, &bbox);
  1138.  
  1139.     /*
  1140.      * Adjust the x-coordinate if necessary to keep the text entirely
  1141.      * inside the window.
  1142.      */
  1143.  
  1144.     x -= (bbox.lbearing + bbox.rbearing)/2;
  1145.     if ((x - bbox.lbearing) < scalePtr->offset) {
  1146.     x = scalePtr->offset + bbox.lbearing;
  1147.     }
  1148.     if ((y + bbox.rbearing) > (Tk_Width(tkwin) - scalePtr->offset)) {
  1149.     x = Tk_Width(tkwin) - scalePtr->offset - bbox.rbearing;
  1150.     }
  1151.     XDrawString(scalePtr->display, Tk_WindowId(tkwin),
  1152.         scalePtr->textGC, x, y, valueString, length);
  1153. }
  1154.  
  1155. /*
  1156.  *----------------------------------------------------------------------
  1157.  *
  1158.  * PixelToValue --
  1159.  *
  1160.  *    Given a pixel within a scale window, return the scale
  1161.  *    reading corresponding to that pixel.
  1162.  *
  1163.  * Results:
  1164.  *    An integer scale reading.
  1165.  *
  1166.  * Side effects:
  1167.  *    None.
  1168.  *
  1169.  *----------------------------------------------------------------------
  1170.  */
  1171.  
  1172. static int
  1173. PixelToValue(scalePtr, x, y)
  1174.     register Scale *scalePtr;        /* Information about widget. */
  1175.     int x, y;                /* Coordinates of point within
  1176.                      * window. */
  1177. {
  1178.     int value, pixelRange;
  1179.     double dtmp;
  1180.  
  1181.     if (scalePtr->vertical) {
  1182.     pixelRange = Tk_Height(scalePtr->tkwin) - scalePtr->sliderLength
  1183.         - 2*scalePtr->offset - 2*scalePtr->borderWidth;
  1184.     value = y;
  1185.     } else {
  1186.     pixelRange = Tk_Width(scalePtr->tkwin) - scalePtr->sliderLength
  1187.         - 2*scalePtr->offset - 2*scalePtr->borderWidth;
  1188.     value = x;
  1189.     }
  1190.  
  1191.     if (pixelRange <= 0) {
  1192.     /*
  1193.      * Not enough room for the slider to actually slide:  just return
  1194.      * the scale's current value.
  1195.      */
  1196.  
  1197.     return scalePtr->value;
  1198.     }
  1199.     value -= scalePtr->sliderLength/2 + scalePtr->offset
  1200.         + scalePtr->borderWidth;
  1201.     if (value < 0) {
  1202.     value = 0;
  1203.     }
  1204.     if (value > pixelRange) {
  1205.     value = pixelRange;
  1206.     }
  1207.  
  1208.     /*
  1209.      * The mathematics below is a bit tricky.  To avoid integer overflow
  1210.      * during the multiplication and division steps, do the arithmetic
  1211.      * using doubles.
  1212.      */
  1213.  
  1214.     if (scalePtr->toValue > scalePtr->fromValue) {
  1215.         dtmp = value * ((double) (scalePtr->toValue - scalePtr->fromValue));
  1216.     } else {
  1217.     dtmp = (pixelRange - value) *
  1218.         ((double) (scalePtr->fromValue - scalePtr->toValue));
  1219.     }
  1220.     dtmp = scalePtr->fromValue + dtmp/pixelRange;
  1221.     if (dtmp < 0) {
  1222.     value = dtmp - 0.5;
  1223.     } else {
  1224.     value = dtmp + 0.5;
  1225.     }
  1226.     return value;
  1227. }
  1228.  
  1229. /*
  1230.  *----------------------------------------------------------------------
  1231.  *
  1232.  * ValueToPixel --
  1233.  *
  1234.  *    Given a reading of the scale, return the x-coordinate or
  1235.  *    y-coordinate corresponding to that reading, depending on
  1236.  *    whether the scale is vertical or horizontal, respectively.
  1237.  *
  1238.  * Results:
  1239.  *    An integer value giving the pixel location corresponding
  1240.  *    to reading.  The value is restricted to lie within the
  1241.  *    defined range for the scale.
  1242.  *
  1243.  * Side effects:
  1244.  *    None.
  1245.  *
  1246.  *----------------------------------------------------------------------
  1247.  */
  1248.  
  1249. static int
  1250. ValueToPixel(scalePtr, value)
  1251.     register Scale *scalePtr;        /* Information about widget. */
  1252.     int value;                /* Reading of the widget. */
  1253. {
  1254.     int y, pixelRange, valueRange;
  1255.     double dtmp;
  1256.  
  1257.     valueRange = scalePtr->toValue - scalePtr->fromValue;
  1258.     pixelRange = (scalePtr->vertical ? Tk_Height(scalePtr->tkwin)
  1259.         : Tk_Width(scalePtr->tkwin)) - scalePtr->sliderLength
  1260.         - 2*scalePtr->offset - 2*scalePtr->borderWidth;
  1261.     if (valueRange == 0) {
  1262.     y = 0;
  1263.     } else {
  1264.     /*
  1265.      * Do arithmetic with doubles to avoid integer overflow if the
  1266.      * scale has a large range.
  1267.      */
  1268.  
  1269.     dtmp = (value - scalePtr->fromValue);
  1270.     dtmp = (dtmp * pixelRange)/valueRange;
  1271.     y = dtmp + 0.5;
  1272.     if (y < 0) {
  1273.         y = 0;
  1274.     } else if (y > pixelRange) {
  1275.         y = pixelRange;
  1276.     }
  1277.     }
  1278.     y += scalePtr->sliderLength/2 + scalePtr->offset + scalePtr->borderWidth;
  1279.     return y;
  1280. }
  1281.  
  1282. /*
  1283.  *--------------------------------------------------------------
  1284.  *
  1285.  * ScaleEventProc --
  1286.  *
  1287.  *    This procedure is invoked by the Tk dispatcher for various
  1288.  *    events on scales.
  1289.  *
  1290.  * Results:
  1291.  *    None.
  1292.  *
  1293.  * Side effects:
  1294.  *    When the window gets deleted, internal structures get
  1295.  *    cleaned up.  When it gets exposed, it is redisplayed.
  1296.  *
  1297.  *--------------------------------------------------------------
  1298.  */
  1299.  
  1300. static void
  1301. ScaleEventProc(clientData, eventPtr)
  1302.     ClientData clientData;    /* Information about window. */
  1303.     XEvent *eventPtr;        /* Information about event. */
  1304. {
  1305.     Scale *scalePtr = (Scale *) clientData;
  1306.  
  1307.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  1308.     EventuallyRedrawScale(scalePtr, REDRAW_ALL);
  1309.     } else if (eventPtr->type == DestroyNotify) {
  1310.     Tcl_DeleteCommand(scalePtr->interp, Tk_PathName(scalePtr->tkwin));
  1311.     scalePtr->tkwin = NULL;
  1312.     if (scalePtr->flags & REDRAW_ALL) {
  1313.         if (scalePtr->vertical) {
  1314.         Tk_CancelIdleCall(DisplayVerticalScale, (ClientData) scalePtr);
  1315.         } else {
  1316.         Tk_CancelIdleCall(DisplayHorizontalScale,
  1317.             (ClientData) scalePtr);
  1318.         }
  1319.     }
  1320.     Tk_EventuallyFree((ClientData) scalePtr, DestroyScale);
  1321.     } else if (eventPtr->type == ConfigureNotify) {
  1322.     ComputeScaleGeometry(scalePtr);
  1323.     }
  1324. }
  1325.  
  1326. /*
  1327.  *--------------------------------------------------------------
  1328.  *
  1329.  * ScaleMouseProc --
  1330.  *
  1331.  *    This procedure is called back by Tk in response to
  1332.  *    mouse events such as window entry, window exit, mouse
  1333.  *    motion, and button presses.
  1334.  *
  1335.  * Results:
  1336.  *    None.
  1337.  *
  1338.  * Side effects:
  1339.  *    This procedure implements the "feel" of the scale by
  1340.  *    issuing commands in response to button presses and mouse
  1341.  *    motion.
  1342.  *
  1343.  *--------------------------------------------------------------
  1344.  */
  1345.  
  1346. static void
  1347. ScaleMouseProc(clientData, eventPtr)
  1348.     ClientData clientData;        /* Information about window. */
  1349.     register XEvent *eventPtr;        /* Information about event. */
  1350. {
  1351.     register Scale *scalePtr = (Scale *) clientData;
  1352.  
  1353.     if (scalePtr->state != tkNormalUid) {
  1354.     return;
  1355.     }
  1356.  
  1357.     Tk_Preserve((ClientData) scalePtr);
  1358.     if (eventPtr->type == EnterNotify) {
  1359.     scalePtr->flags |= ACTIVE;
  1360.     EventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
  1361.     } else if (eventPtr->type == LeaveNotify) {
  1362.     scalePtr->flags &= ~ACTIVE;
  1363.     EventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
  1364.     } else if ((eventPtr->type == MotionNotify)
  1365.         && (scalePtr->flags & BUTTON_PRESSED)) {
  1366.     SetScaleValue(scalePtr,  PixelToValue(scalePtr,
  1367.         eventPtr->xmotion.x, eventPtr->xmotion.y));
  1368.     } else if ((eventPtr->type == ButtonPress)
  1369.         && (eventPtr->xbutton.button == Button1)
  1370.         && (eventPtr->xbutton.state == 0)) {
  1371.     scalePtr->flags |= BUTTON_PRESSED;
  1372.     SetScaleValue(scalePtr, PixelToValue(scalePtr,
  1373.         eventPtr->xbutton.x, eventPtr->xbutton.y));
  1374.     EventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
  1375.     } else if ((eventPtr->type == ButtonRelease)
  1376.         && (eventPtr->xbutton.button == Button1)
  1377.         && (scalePtr->flags & BUTTON_PRESSED)) {
  1378.     scalePtr->flags &= ~BUTTON_PRESSED;
  1379.     EventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
  1380.     }
  1381.     Tk_Release((ClientData) scalePtr);
  1382. }
  1383.  
  1384. /*
  1385.  *--------------------------------------------------------------
  1386.  *
  1387.  * SetScaleValue --
  1388.  *
  1389.  *    This procedure changes the value of a scale and invokes
  1390.  *    a Tcl command to reflect the current position of a scale
  1391.  *
  1392.  * Results:
  1393.  *    None.
  1394.  *
  1395.  * Side effects:
  1396.  *    A Tcl command is invoked, and an additional error-processing
  1397.  *    command may also be invoked.  The scale's slider is redrawn.
  1398.  *
  1399.  *--------------------------------------------------------------
  1400.  */
  1401.  
  1402. static void
  1403. SetScaleValue(scalePtr, value)
  1404.     register Scale *scalePtr;    /* Info about widget. */
  1405.     int value;            /* New value for scale.  Gets
  1406.                  * adjusted if it's off the scale. */
  1407. {
  1408.     if ((value < scalePtr->fromValue)
  1409.         ^ (scalePtr->toValue < scalePtr->fromValue)) {
  1410.     value = scalePtr->fromValue;
  1411.     }
  1412.     if ((value > scalePtr->toValue)
  1413.         ^ (scalePtr->toValue < scalePtr->fromValue)) {
  1414.     value = scalePtr->toValue;
  1415.     }
  1416.     if (value == scalePtr->value) {
  1417.     return;
  1418.     }
  1419.     scalePtr->value = value;
  1420.     scalePtr->flags |= INVOKE_COMMAND;
  1421.     EventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
  1422. }
  1423.  
  1424. /*
  1425.  *--------------------------------------------------------------
  1426.  *
  1427.  * EventuallyRedrawScale --
  1428.  *
  1429.  *    Arrange for part or all of a scale widget to redrawn at
  1430.  *    the next convenient time in the future.
  1431.  *
  1432.  * Results:
  1433.  *    None.
  1434.  *
  1435.  * Side effects:
  1436.  *    If "what" is REDRAW_SLIDER then just the slider and the
  1437.  *    value readout will be redrawn;  if "what" is REDRAW_ALL
  1438.  *    then the entire widget will be redrawn.
  1439.  *
  1440.  *--------------------------------------------------------------
  1441.  */
  1442.  
  1443. static void
  1444. EventuallyRedrawScale(scalePtr, what)
  1445.     register Scale *scalePtr;    /* Information about widget. */
  1446.     int what;            /* What to redraw:  REDRAW_SLIDER
  1447.                  * or REDRAW_ALL. */
  1448. {
  1449.     if ((what == 0) || (scalePtr->tkwin == NULL)
  1450.         || !Tk_IsMapped(scalePtr->tkwin)) {
  1451.     return;
  1452.     }
  1453.     if ((scalePtr->flags & REDRAW_ALL) == 0) {
  1454.     if (scalePtr->vertical) {
  1455.         Tk_DoWhenIdle(DisplayVerticalScale, (ClientData) scalePtr);
  1456.     } else {
  1457.         Tk_DoWhenIdle(DisplayHorizontalScale, (ClientData) scalePtr);
  1458.     }
  1459.     }
  1460.     scalePtr->flags |= what;
  1461. }
  1462.