tkScale.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:42k
源码类别:

通讯编程

开发平台:

Visual C++

  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-1997 Sun Microsystems, Inc.
  16.  * Copyright (c) 1998-2000 by Scriptics Corporation.
  17.  *
  18.  * See the file "license.terms" for information on usage and redistribution
  19.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  20.  *
  21.  * RCS: @(#) $Id: tkScale.c,v 1.17.2.1 2006/06/14 22:15:22 dgp Exp $
  22.  */
  23. #include "tkPort.h"
  24. #include "default.h"
  25. #include "tkInt.h"
  26. #include "tclMath.h"
  27. #include "tkScale.h"
  28. /*
  29.  * The following table defines the legal values for the -orient option.
  30.  * It is used together with the "enum orient" declaration in tkScale.h.
  31.  */
  32. static char *orientStrings[] = {
  33.     "horizontal", "vertical", (char *) NULL
  34. };
  35. /*
  36.  * The following table defines the legal values for the -state option.
  37.  * It is used together with the "enum state" declaration in tkScale.h.
  38.  */
  39. static char *stateStrings[] = {
  40.     "active", "disabled", "normal", (char *) NULL
  41. };
  42. static Tk_OptionSpec optionSpecs[] = {
  43.     {TK_OPTION_BORDER, "-activebackground", "activeBackground", "Foreground",
  44. DEF_SCALE_ACTIVE_BG_COLOR, -1, Tk_Offset(TkScale, activeBorder),
  45. 0, (ClientData) DEF_SCALE_ACTIVE_BG_MONO, 0},
  46.     {TK_OPTION_BORDER, "-background", "background", "Background",
  47. DEF_SCALE_BG_COLOR, -1, Tk_Offset(TkScale, bgBorder),
  48. 0, (ClientData) DEF_SCALE_BG_MONO, 0},
  49.     {TK_OPTION_DOUBLE, "-bigincrement", "bigIncrement", "BigIncrement",
  50.         DEF_SCALE_BIG_INCREMENT, -1, Tk_Offset(TkScale, bigIncrement), 
  51.         0, 0, 0},
  52.     {TK_OPTION_SYNONYM, "-bd", (char *) NULL, (char *) NULL,
  53. (char *) NULL, 0, -1, 0, (ClientData) "-borderwidth", 0},
  54.     {TK_OPTION_SYNONYM, "-bg", (char *) NULL, (char *) NULL,
  55. (char *) NULL, 0, -1, 0, (ClientData) "-background", 0},
  56.     {TK_OPTION_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  57. DEF_SCALE_BORDER_WIDTH, -1, Tk_Offset(TkScale, borderWidth), 
  58.         0, 0, 0},
  59.     {TK_OPTION_STRING, "-command", "command", "Command",
  60. DEF_SCALE_COMMAND, -1, Tk_Offset(TkScale, command),
  61. TK_OPTION_NULL_OK, 0, 0},
  62.     {TK_OPTION_CURSOR, "-cursor", "cursor", "Cursor",
  63. DEF_SCALE_CURSOR, -1, Tk_Offset(TkScale, cursor),
  64. TK_OPTION_NULL_OK, 0, 0},
  65.     {TK_OPTION_INT, "-digits", "digits", "Digits", 
  66. DEF_SCALE_DIGITS, -1, Tk_Offset(TkScale, digits), 
  67.         0, 0, 0},
  68.     {TK_OPTION_SYNONYM, "-fg", "foreground", (char *) NULL,
  69. (char *) NULL, 0, -1, 0, (ClientData) "-foreground", 0},
  70.     {TK_OPTION_FONT, "-font", "font", "Font",
  71. DEF_SCALE_FONT, -1, Tk_Offset(TkScale, tkfont), 0, 0, 0},
  72.     {TK_OPTION_COLOR, "-foreground", "foreground", "Foreground",
  73. DEF_SCALE_FG_COLOR, -1, Tk_Offset(TkScale, textColorPtr), 0, 
  74.         (ClientData) DEF_SCALE_FG_MONO, 0},
  75.     {TK_OPTION_DOUBLE, "-from", "from", "From", DEF_SCALE_FROM, -1, 
  76.         Tk_Offset(TkScale, fromValue), 0, 0, 0},
  77.     {TK_OPTION_BORDER, "-highlightbackground", "highlightBackground",
  78. "HighlightBackground", DEF_SCALE_HIGHLIGHT_BG_COLOR,
  79. -1, Tk_Offset(TkScale, highlightBorder), 
  80.         0, (ClientData) DEF_SCALE_HIGHLIGHT_BG_MONO, 0},
  81.     {TK_OPTION_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
  82. DEF_SCALE_HIGHLIGHT, -1, Tk_Offset(TkScale, highlightColorPtr),
  83. 0, 0, 0},
  84.     {TK_OPTION_PIXELS, "-highlightthickness", "highlightThickness",
  85. "HighlightThickness", DEF_SCALE_HIGHLIGHT_WIDTH, -1, 
  86. Tk_Offset(TkScale, highlightWidth), 0, 0, 0},
  87.     {TK_OPTION_STRING, "-label", "label", "Label",
  88. DEF_SCALE_LABEL, -1, Tk_Offset(TkScale, label),
  89. TK_OPTION_NULL_OK, 0, 0},
  90.     {TK_OPTION_PIXELS, "-length", "length", "Length",
  91. DEF_SCALE_LENGTH, -1, Tk_Offset(TkScale, length), 0, 0, 0},
  92.     {TK_OPTION_STRING_TABLE, "-orient", "orient", "Orient",
  93.         DEF_SCALE_ORIENT, -1, Tk_Offset(TkScale, orient), 
  94.         0, (ClientData) orientStrings, 0},
  95.     {TK_OPTION_RELIEF, "-relief", "relief", "Relief",
  96. DEF_SCALE_RELIEF, -1, Tk_Offset(TkScale, relief), 0, 0, 0},
  97.     {TK_OPTION_INT, "-repeatdelay", "repeatDelay", "RepeatDelay",
  98.         DEF_SCALE_REPEAT_DELAY, -1, Tk_Offset(TkScale, repeatDelay),
  99.         0, 0, 0},
  100.     {TK_OPTION_INT, "-repeatinterval", "repeatInterval", "RepeatInterval",
  101.         DEF_SCALE_REPEAT_INTERVAL, -1, Tk_Offset(TkScale, repeatInterval),
  102.         0, 0, 0},
  103.     {TK_OPTION_DOUBLE, "-resolution", "resolution", "Resolution",
  104.         DEF_SCALE_RESOLUTION, -1, Tk_Offset(TkScale, resolution),
  105.         0, 0, 0},
  106.     {TK_OPTION_BOOLEAN, "-showvalue", "showValue", "ShowValue",
  107.         DEF_SCALE_SHOW_VALUE, -1, Tk_Offset(TkScale, showValue),
  108.         0, 0, 0},
  109.     {TK_OPTION_PIXELS, "-sliderlength", "sliderLength", "SliderLength",
  110.         DEF_SCALE_SLIDER_LENGTH, -1, Tk_Offset(TkScale, sliderLength),
  111.         0, 0, 0},
  112.     {TK_OPTION_RELIEF, "-sliderrelief", "sliderRelief", "SliderRelief",
  113. DEF_SCALE_SLIDER_RELIEF, -1, Tk_Offset(TkScale, sliderRelief), 
  114.         0, 0, 0},
  115.     {TK_OPTION_STRING_TABLE, "-state", "state", "State",
  116.         DEF_SCALE_STATE, -1, Tk_Offset(TkScale, state), 
  117.         0, (ClientData) stateStrings, 0},
  118.     {TK_OPTION_STRING, "-takefocus", "takeFocus", "TakeFocus",
  119. DEF_SCALE_TAKE_FOCUS, Tk_Offset(TkScale, takeFocusPtr), -1,
  120. TK_OPTION_NULL_OK, 0, 0},
  121.     {TK_OPTION_DOUBLE, "-tickinterval", "tickInterval", "TickInterval",
  122.         DEF_SCALE_TICK_INTERVAL, -1, Tk_Offset(TkScale, tickInterval),
  123.         0, 0, 0},
  124.     {TK_OPTION_DOUBLE, "-to", "to", "To",
  125.         DEF_SCALE_TO, -1, Tk_Offset(TkScale, toValue), 0, 0, 0},
  126.     {TK_OPTION_COLOR, "-troughcolor", "troughColor", "Background",
  127.         DEF_SCALE_TROUGH_COLOR, -1, Tk_Offset(TkScale, troughColorPtr),
  128.         0, (ClientData) DEF_SCALE_TROUGH_MONO, 0},
  129.     {TK_OPTION_STRING, "-variable", "variable", "Variable",
  130. DEF_SCALE_VARIABLE, Tk_Offset(TkScale, varNamePtr), -1,
  131. TK_OPTION_NULL_OK, 0, 0},
  132.     {TK_OPTION_PIXELS, "-width", "width", "Width",
  133. DEF_SCALE_WIDTH, -1, Tk_Offset(TkScale, width), 0, 0, 0},
  134.     {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
  135. (char *) NULL, 0, -1, 0, 0, 0}
  136. };
  137. /*
  138.  * The following tables define the scale widget commands and map the 
  139.  * indexes into the string tables into a single enumerated type used 
  140.  * to dispatch the scale widget command.
  141.  */
  142. static CONST char *commandNames[] = {
  143.     "cget", "configure", "coords", "get", "identify", "set", (char *) NULL
  144. };
  145. enum command {
  146.     COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_COORDS, COMMAND_GET,
  147.     COMMAND_IDENTIFY, COMMAND_SET
  148. };
  149. /*
  150.  * Forward declarations for procedures defined later in this file:
  151.  */
  152. static void ComputeFormat _ANSI_ARGS_((TkScale *scalePtr));
  153. static void ComputeScaleGeometry _ANSI_ARGS_((TkScale *scalePtr));
  154. static int ConfigureScale _ANSI_ARGS_((Tcl_Interp *interp,
  155.     TkScale *scalePtr, int objc,
  156.     Tcl_Obj *CONST objv[]));
  157. static void DestroyScale _ANSI_ARGS_((char *memPtr));
  158. static void ScaleCmdDeletedProc _ANSI_ARGS_((
  159.     ClientData clientData));
  160. static void ScaleEventProc _ANSI_ARGS_((ClientData clientData,
  161.     XEvent *eventPtr));
  162. static char * ScaleVarProc _ANSI_ARGS_((ClientData clientData,
  163.     Tcl_Interp *interp, CONST char *name1,
  164.     CONST char *name2, int flags));
  165. static int ScaleWidgetObjCmd _ANSI_ARGS_((ClientData clientData,
  166.     Tcl_Interp *interp, int objc, 
  167.     Tcl_Obj *CONST objv[]));
  168. static void ScaleWorldChanged _ANSI_ARGS_((
  169.     ClientData instanceData));
  170. static void ScaleSetVariable _ANSI_ARGS_((TkScale *scalePtr));
  171. /*
  172.  * The structure below defines scale class behavior by means of procedures
  173.  * that can be invoked from generic window code.
  174.  */
  175. static Tk_ClassProcs scaleClass = {
  176.     sizeof(Tk_ClassProcs), /* size */
  177.     ScaleWorldChanged, /* worldChangedProc */
  178. };
  179. /*
  180.  *--------------------------------------------------------------
  181.  *
  182.  * Tk_ScaleObjCmd --
  183.  *
  184.  * This procedure is invoked to process the "scale" Tcl
  185.  * command.  See the user documentation for details on what
  186.  * it does.
  187.  *
  188.  * Results:
  189.  * A standard Tcl result.
  190.  *
  191.  * Side effects:
  192.  * See the user documentation.
  193.  *
  194.  *--------------------------------------------------------------
  195.  */
  196. int
  197. Tk_ScaleObjCmd(clientData, interp, objc, objv)
  198.     ClientData clientData; /* NULL. */
  199.     Tcl_Interp *interp; /* Current interpreter. */
  200.     int objc; /* Number of arguments. */
  201.     Tcl_Obj *CONST objv[]; /* Argument values. */
  202. {
  203.     register TkScale *scalePtr;
  204.     Tk_OptionTable optionTable;
  205.     Tk_Window tkwin;
  206.     if (objc < 2) {
  207. Tcl_WrongNumArgs(interp, 1, objv, "pathName ?options?");
  208. return TCL_ERROR;
  209.     }
  210.     tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp),
  211.             Tcl_GetString(objv[1]), (char *) NULL);
  212.     if (tkwin == NULL) {
  213. return TCL_ERROR;
  214.     }
  215.     /*
  216.      * Create the option table for this widget class.  If it has already
  217.      * been created, the cached pointer will be returned.
  218.      */
  219.     optionTable = Tk_CreateOptionTable(interp, optionSpecs);
  220.     Tk_SetClass(tkwin, "Scale");
  221.     scalePtr = TkpCreateScale(tkwin);
  222.     /*
  223.      * Initialize fields that won't be initialized by ConfigureScale,
  224.      * or which ConfigureScale expects to have reasonable values
  225.      * (e.g. resource pointers).
  226.      */
  227.     scalePtr->tkwin = tkwin;
  228.     scalePtr->display = Tk_Display(tkwin);
  229.     scalePtr->interp = interp;
  230.     scalePtr->widgetCmd = Tcl_CreateObjCommand(interp,
  231.     Tk_PathName(scalePtr->tkwin), ScaleWidgetObjCmd,
  232.     (ClientData) scalePtr, ScaleCmdDeletedProc);
  233.     scalePtr->optionTable = optionTable;
  234.     scalePtr->orient = ORIENT_VERTICAL;
  235.     scalePtr->width = 0;
  236.     scalePtr->length = 0;
  237.     scalePtr->value = 0.0;
  238.     scalePtr->varNamePtr = NULL;
  239.     scalePtr->fromValue = 0.0;
  240.     scalePtr->toValue = 0.0;
  241.     scalePtr->tickInterval = 0.0;
  242.     scalePtr->resolution = 1.0;
  243.     scalePtr->digits = 0;
  244.     scalePtr->bigIncrement = 0.0;
  245.     scalePtr->command = NULL;
  246.     scalePtr->repeatDelay = 0;
  247.     scalePtr->repeatInterval = 0;
  248.     scalePtr->label = NULL;
  249.     scalePtr->labelLength = 0;
  250.     scalePtr->state = STATE_NORMAL;
  251.     scalePtr->borderWidth = 0;
  252.     scalePtr->bgBorder = NULL;
  253.     scalePtr->activeBorder = NULL;
  254.     scalePtr->sliderRelief = TK_RELIEF_RAISED;
  255.     scalePtr->troughColorPtr = NULL;
  256.     scalePtr->troughGC = None;
  257.     scalePtr->copyGC = None;
  258.     scalePtr->tkfont = NULL;
  259.     scalePtr->textColorPtr = NULL;
  260.     scalePtr->textGC = None;
  261.     scalePtr->relief = TK_RELIEF_FLAT;
  262.     scalePtr->highlightWidth = 0;
  263.     scalePtr->highlightBorder = NULL;
  264.     scalePtr->highlightColorPtr = NULL;
  265.     scalePtr->inset = 0;
  266.     scalePtr->sliderLength = 0;
  267.     scalePtr->showValue = 0;
  268.     scalePtr->horizLabelY = 0;
  269.     scalePtr->horizValueY = 0;
  270.     scalePtr->horizTroughY = 0;
  271.     scalePtr->horizTickY = 0;
  272.     scalePtr->vertTickRightX = 0;
  273.     scalePtr->vertValueRightX = 0;
  274.     scalePtr->vertTroughX = 0;
  275.     scalePtr->vertLabelX = 0;
  276.     scalePtr->fontHeight = 0;
  277.     scalePtr->cursor = None;
  278.     scalePtr->takeFocusPtr = NULL;
  279.     scalePtr->flags = NEVER_SET;
  280.     Tk_SetClassProcs(scalePtr->tkwin, &scaleClass, (ClientData) scalePtr);
  281.     Tk_CreateEventHandler(scalePtr->tkwin,
  282.     ExposureMask|StructureNotifyMask|FocusChangeMask,
  283.     ScaleEventProc, (ClientData) scalePtr);
  284.     if ((Tk_InitOptions(interp, (char *) scalePtr, optionTable, tkwin)
  285.     != TCL_OK) ||
  286.     (ConfigureScale(interp, scalePtr, objc - 2, objv + 2) != TCL_OK)) {
  287. Tk_DestroyWindow(scalePtr->tkwin);
  288. return TCL_ERROR;
  289.     }
  290.     Tcl_SetResult(interp, Tk_PathName(scalePtr->tkwin), TCL_STATIC);
  291.     return TCL_OK;
  292. }
  293. /*
  294.  *--------------------------------------------------------------
  295.  *
  296.  * ScaleWidgetObjCmd --
  297.  *
  298.  * This procedure is invoked to process the Tcl command
  299.  * that corresponds to a widget managed by this module.
  300.  * See the user documentation for details on what it does.
  301.  *
  302.  * Results:
  303.  * A standard Tcl result.
  304.  *
  305.  * Side effects:
  306.  * See the user documentation.
  307.  *
  308.  *--------------------------------------------------------------
  309.  */
  310. static int
  311. ScaleWidgetObjCmd(clientData, interp, objc, objv)
  312.     ClientData clientData; /* Information about scale
  313.  * widget. */
  314.     Tcl_Interp *interp; /* Current interpreter. */
  315.     int objc; /* Number of arguments. */
  316.     Tcl_Obj *CONST objv[]; /* Argument strings. */
  317. {
  318.     TkScale *scalePtr = (TkScale *) clientData;
  319.     Tcl_Obj *objPtr;
  320.     int index, result;
  321.     if (objc < 2) {
  322.         Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
  323. return TCL_ERROR;
  324.     }
  325.     result = Tcl_GetIndexFromObj(interp, objv[1], commandNames,
  326.             "option", 0, &index);
  327.     if (result != TCL_OK) {
  328. return result;
  329.     }
  330.     Tcl_Preserve((ClientData) scalePtr);
  331.     switch (index) {
  332.         case COMMAND_CGET: {
  333.        if (objc != 3) {
  334.         Tcl_WrongNumArgs(interp, 1, objv, "cget option");
  335. goto error;
  336.     }
  337.     objPtr = Tk_GetOptionValue(interp, (char *) scalePtr,
  338.     scalePtr->optionTable, objv[2], scalePtr->tkwin);
  339.     if (objPtr == NULL) {
  340.  goto error;
  341.     } else {
  342. Tcl_SetObjResult(interp, objPtr);
  343.     }
  344.     break;
  345. }
  346.         case COMMAND_CONFIGURE: {
  347.     if (objc <= 3) {
  348. objPtr = Tk_GetOptionInfo(interp, (char *) scalePtr,
  349. scalePtr->optionTable,
  350. (objc == 3) ? objv[2] : (Tcl_Obj *) NULL,
  351. scalePtr->tkwin);
  352. if (objPtr == NULL) {
  353.     goto error;
  354. } else {
  355.     Tcl_SetObjResult(interp, objPtr);
  356. }
  357.     } else {
  358. result = ConfigureScale(interp, scalePtr, objc-2, objv+2);
  359.     }
  360.     break;
  361. }
  362.         case COMMAND_COORDS: {
  363.     int x, y ;
  364.     double value;
  365.     char buf[TCL_INTEGER_SPACE * 2];
  366.     if ((objc != 2) && (objc != 3)) {
  367.         Tcl_WrongNumArgs(interp, 1, objv, "coords ?value?");
  368. goto error;
  369.     }
  370.     if (objc == 3) {
  371.         if (Tcl_GetDoubleFromObj(interp, objv[2], &value) 
  372.                         != TCL_OK) {
  373.     goto error;
  374. }
  375.     } else {
  376.         value = scalePtr->value;
  377.     }
  378.     if (scalePtr->orient == ORIENT_VERTICAL) {
  379.         x = scalePtr->vertTroughX + scalePtr->width/2
  380.         + scalePtr->borderWidth;
  381. y = TkScaleValueToPixel(scalePtr, value);
  382.     } else {
  383.         x = TkScaleValueToPixel(scalePtr, value);
  384. y = scalePtr->horizTroughY + scalePtr->width/2
  385.                         + scalePtr->borderWidth;
  386.     }
  387.     sprintf(buf, "%d %d", x, y);
  388.     Tcl_SetResult(interp, buf, TCL_VOLATILE);
  389.             break;
  390.         }
  391.         case COMMAND_GET: {
  392.     double value;
  393.     int x, y;
  394.     char buf[TCL_DOUBLE_SPACE];
  395.     if ((objc != 2) && (objc != 4)) {
  396.         Tcl_WrongNumArgs(interp, 1, objv, "get ?x y?");
  397. goto error;
  398.     }
  399.     if (objc == 2) {
  400.         value = scalePtr->value;
  401.     } else {
  402.         if ((Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK)
  403.         || (Tcl_GetIntFromObj(interp, objv[3], &y) 
  404.                         != TCL_OK)) {
  405.     goto error;
  406. }
  407. value = TkScalePixelToValue(scalePtr, x, y);
  408.     }
  409.     sprintf(buf, scalePtr->format, value);
  410.     Tcl_SetResult(interp, buf, TCL_VOLATILE);
  411.             break;
  412.         }
  413.         case COMMAND_IDENTIFY: {
  414.     int x, y, thing;
  415.     if (objc != 4) {
  416.         Tcl_WrongNumArgs(interp, 1, objv, "identify x y");
  417. goto error;
  418.     }
  419.     if ((Tcl_GetIntFromObj(interp, objv[2], &x) != TCL_OK)
  420.                     || (Tcl_GetIntFromObj(interp, objv[3], &y) != TCL_OK)) {
  421.         goto error;
  422.     }
  423.     thing = TkpScaleElement(scalePtr, x,y);
  424.     switch (thing) {
  425.         case TROUGH1:
  426.     Tcl_SetResult(interp, "trough1", TCL_STATIC);
  427.     break;
  428.         case SLIDER:
  429.     Tcl_SetResult(interp, "slider", TCL_STATIC);
  430.     break;
  431.         case TROUGH2:
  432.     Tcl_SetResult(interp, "trough2", TCL_STATIC);
  433.     break;
  434.     }
  435.             break;
  436.         }
  437.         case COMMAND_SET: {
  438.     double value;
  439.     if (objc != 3) {
  440.         Tcl_WrongNumArgs(interp, 1, objv, "set value");
  441. goto error;
  442.     }
  443.     if (Tcl_GetDoubleFromObj(interp, objv[2], &value) != TCL_OK) {
  444.         goto error;
  445.     }
  446.     if (scalePtr->state != STATE_DISABLED) {
  447.       TkScaleSetValue(scalePtr, value, 1, 1);
  448.     }
  449.     break;
  450.         } 
  451.     }
  452.     Tcl_Release((ClientData) scalePtr);
  453.     return result;
  454.     error:
  455.     Tcl_Release((ClientData) scalePtr);
  456.     return TCL_ERROR;
  457. }
  458. /*
  459.  *----------------------------------------------------------------------
  460.  *
  461.  * DestroyScale --
  462.  *
  463.  * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
  464.  * to clean up the internal structure of a button at a safe time
  465.  * (when no-one is using it anymore).
  466.  *
  467.  * Results:
  468.  * None.
  469.  *
  470.  * Side effects:
  471.  * Everything associated with the scale is freed up.
  472.  *
  473.  *----------------------------------------------------------------------
  474.  */
  475. static void
  476. DestroyScale(memPtr)
  477.     char *memPtr; /* Info about scale widget. */
  478. {
  479.     register TkScale *scalePtr = (TkScale *) memPtr;
  480.     scalePtr->flags |= SCALE_DELETED;
  481.     Tcl_DeleteCommandFromToken(scalePtr->interp, scalePtr->widgetCmd);
  482.     if (scalePtr->flags & REDRAW_PENDING) {
  483. Tcl_CancelIdleCall(TkpDisplayScale, (ClientData) scalePtr);
  484.     }
  485.     /*
  486.      * Free up all the stuff that requires special handling, then
  487.      * let Tk_FreeOptions handle all the standard option-related
  488.      * stuff.
  489.      */
  490.     if (scalePtr->varNamePtr != NULL) {
  491. Tcl_UntraceVar(scalePtr->interp, Tcl_GetString(scalePtr->varNamePtr),
  492. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  493. ScaleVarProc, (ClientData) scalePtr);
  494.     }
  495.     if (scalePtr->troughGC != None) {
  496. Tk_FreeGC(scalePtr->display, scalePtr->troughGC);
  497.     }
  498.     if (scalePtr->copyGC != None) {
  499. Tk_FreeGC(scalePtr->display, scalePtr->copyGC);
  500.     }
  501.     if (scalePtr->textGC != None) {
  502. Tk_FreeGC(scalePtr->display, scalePtr->textGC);
  503.     }
  504.     Tk_FreeConfigOptions((char *) scalePtr, scalePtr->optionTable,
  505.     scalePtr->tkwin);
  506.     scalePtr->tkwin = NULL;
  507.     TkpDestroyScale(scalePtr);
  508. }
  509. /*
  510.  *----------------------------------------------------------------------
  511.  *
  512.  * ConfigureScale --
  513.  *
  514.  * This procedure is called to process an argv/argc list, plus
  515.  * the Tk option database, in order to configure (or
  516.  * reconfigure) a scale widget.
  517.  *
  518.  * Results:
  519.  * The return value is a standard Tcl result.  If TCL_ERROR is
  520.  * returned, then the interp's result contains an error message.
  521.  *
  522.  * Side effects:
  523.  * Configuration information, such as colors, border width,
  524.  * etc. get set for scalePtr;  old resources get freed,
  525.  * if there were any.
  526.  *
  527.  *----------------------------------------------------------------------
  528.  */
  529. static int
  530. ConfigureScale(interp, scalePtr, objc, objv)
  531.     Tcl_Interp *interp; /* Used for error reporting. */
  532.     register TkScale *scalePtr; /* Information about widget;  may or may
  533.  * not already have values for some fields. */
  534.     int objc; /* Number of valid entries in objv. */
  535.     Tcl_Obj *CONST objv[]; /* Argument values. */
  536. {
  537.     Tk_SavedOptions savedOptions;
  538.     Tcl_Obj *errorResult = NULL;
  539.     int error;
  540.     double varValue;
  541.     /*
  542.      * Eliminate any existing trace on a variable monitored by the scale.
  543.      */
  544.     if (scalePtr->varNamePtr != NULL) {
  545. Tcl_UntraceVar(interp, Tcl_GetString(scalePtr->varNamePtr),
  546. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  547. ScaleVarProc, (ClientData) scalePtr);
  548.     }
  549.     for (error = 0; error <= 1; error++) {
  550. if (!error) {
  551.     /*
  552.      * First pass: set options to new values.
  553.      */
  554.     if (Tk_SetOptions(interp, (char *) scalePtr,
  555.     scalePtr->optionTable, objc, objv,
  556.     scalePtr->tkwin, &savedOptions, (int *) NULL) != TCL_OK) {
  557. continue;
  558.     }
  559. } else {
  560.     /*
  561.      * Second pass: restore options to old values.
  562.      */
  563.     errorResult = Tcl_GetObjResult(interp);
  564.     Tcl_IncrRefCount(errorResult);
  565.     Tk_RestoreSavedOptions(&savedOptions);
  566. }
  567. /*
  568.  * If the scale is tied to the value of a variable, then set 
  569.  * the scale's value from the value of the variable, if it exists
  570.  * and it holds a valid double value.
  571.  */
  572. if (scalePtr->varNamePtr != NULL) {
  573.     double value;
  574.     Tcl_Obj *valuePtr;
  575.     valuePtr = Tcl_ObjGetVar2(interp, scalePtr->varNamePtr, NULL,
  576.     TCL_GLOBAL_ONLY);
  577.     if ((valuePtr != NULL) &&
  578.     (Tcl_GetDoubleFromObj(NULL, valuePtr, &value) == TCL_OK)) {
  579. scalePtr->value = TkRoundToResolution(scalePtr, value);
  580.     }
  581. }
  582. /*
  583.  * Several options need special processing, such as parsing the
  584.  * orientation and creating GCs.
  585.  */
  586. scalePtr->fromValue = TkRoundToResolution(scalePtr, 
  587.                 scalePtr->fromValue);
  588. scalePtr->toValue = TkRoundToResolution(scalePtr, scalePtr->toValue);
  589. scalePtr->tickInterval = TkRoundToResolution(scalePtr,
  590.         scalePtr->tickInterval);
  591. /*
  592.  * Make sure that the tick interval has the right sign so that
  593.  * addition moves from fromValue to toValue.
  594.  */
  595. if ((scalePtr->tickInterval < 0)
  596. ^ ((scalePtr->toValue - scalePtr->fromValue) <  0)) {
  597.   scalePtr->tickInterval = -scalePtr->tickInterval;
  598. }
  599. ComputeFormat(scalePtr);
  600. scalePtr->labelLength = scalePtr->label ? strlen(scalePtr->label) : 0;
  601. Tk_SetBackgroundFromBorder(scalePtr->tkwin, scalePtr->bgBorder);
  602. if (scalePtr->highlightWidth < 0) {
  603.     scalePtr->highlightWidth = 0;
  604. }
  605. scalePtr->inset = scalePtr->highlightWidth + scalePtr->borderWidth;
  606. break;
  607.     }
  608.     if (!error) {
  609.         Tk_FreeSavedOptions(&savedOptions);
  610.     }
  611.     /*
  612.      * Set the scale value to itself;  all this does is to make sure
  613.      * that the scale's value is within the new acceptable range for
  614.      * the scale.  We don't set the var here because we need to make
  615.      * special checks for possibly changed varNamePtr.
  616.      */
  617.     TkScaleSetValue(scalePtr, scalePtr->value, 0, 1);
  618.     /*
  619.      * Reestablish the variable trace, if it is needed.
  620.      */
  621.     if (scalePtr->varNamePtr != NULL) {
  622. Tcl_Obj *valuePtr;
  623. /*
  624.  * Set the associated variable only when the new value differs
  625.  * from the current value, or the variable doesn't yet exist
  626.  */
  627. valuePtr = Tcl_ObjGetVar2(interp, scalePtr->varNamePtr, NULL,
  628. TCL_GLOBAL_ONLY);
  629. if ((valuePtr == NULL) || (Tcl_GetDoubleFromObj(NULL,
  630. valuePtr, &varValue) != TCL_OK)) {
  631.     ScaleSetVariable(scalePtr);
  632. } else {
  633.     char varString[TCL_DOUBLE_SPACE];
  634.     char scaleString[TCL_DOUBLE_SPACE];
  635.     sprintf(varString, scalePtr->format, varValue);
  636.     sprintf(scaleString, scalePtr->format, scalePtr->value);
  637.     if (strcmp(varString, scaleString)) {
  638. ScaleSetVariable(scalePtr);
  639.     }
  640. }
  641.         Tcl_TraceVar(interp, Tcl_GetString(scalePtr->varNamePtr),
  642.         TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  643.         ScaleVarProc, (ClientData) scalePtr);
  644.     }
  645.     ScaleWorldChanged((ClientData) scalePtr);
  646.     if (error) {
  647.         Tcl_SetObjResult(interp, errorResult);
  648. Tcl_DecrRefCount(errorResult);
  649. return TCL_ERROR;
  650.     } else {
  651. return TCL_OK;
  652.     }
  653. }
  654. /*
  655.  *---------------------------------------------------------------------------
  656.  *
  657.  * ScaleWorldChanged --
  658.  *
  659.  *      This procedure is called when the world has changed in some
  660.  *      way and the widget needs to recompute all its graphics contexts
  661.  * and determine its new geometry.
  662.  *
  663.  * Results:
  664.  *      None.
  665.  *
  666.  * Side effects:
  667.  *      Scale will be relayed out and redisplayed.
  668.  *
  669.  *---------------------------------------------------------------------------
  670.  */
  671.  
  672. static void
  673. ScaleWorldChanged(instanceData)
  674.     ClientData instanceData; /* Information about widget. */
  675. {
  676.     XGCValues gcValues;
  677.     GC gc;
  678.     TkScale *scalePtr;
  679.     scalePtr = (TkScale *) instanceData;
  680.     gcValues.foreground = scalePtr->troughColorPtr->pixel;
  681.     gc = Tk_GetGC(scalePtr->tkwin, GCForeground, &gcValues);
  682.     if (scalePtr->troughGC != None) {
  683. Tk_FreeGC(scalePtr->display, scalePtr->troughGC);
  684.     }
  685.     scalePtr->troughGC = gc;
  686.     gcValues.font = Tk_FontId(scalePtr->tkfont);
  687.     gcValues.foreground = scalePtr->textColorPtr->pixel;
  688.     gc = Tk_GetGC(scalePtr->tkwin, GCForeground | GCFont, &gcValues);
  689.     if (scalePtr->textGC != None) {
  690. Tk_FreeGC(scalePtr->display, scalePtr->textGC);
  691.     }
  692.     scalePtr->textGC = gc;
  693.     if (scalePtr->copyGC == None) {
  694. gcValues.graphics_exposures = False;
  695. scalePtr->copyGC = Tk_GetGC(scalePtr->tkwin, GCGraphicsExposures,
  696.     &gcValues);
  697.     }
  698.     scalePtr->inset = scalePtr->highlightWidth + scalePtr->borderWidth;
  699.     /*
  700.      * Recompute display-related information, and let the geometry
  701.      * manager know how much space is needed now.
  702.      */
  703.     ComputeScaleGeometry(scalePtr);
  704.     TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
  705. }
  706. /*
  707.  *----------------------------------------------------------------------
  708.  *
  709.  * ComputeFormat --
  710.  *
  711.  * This procedure is invoked to recompute the "format" field
  712.  * of a scale's widget record, which determines how the value
  713.  * of the scale is converted to a string.
  714.  *
  715.  * Results:
  716.  * None.
  717.  *
  718.  * Side effects:
  719.  * The format field of scalePtr is modified.
  720.  *
  721.  *----------------------------------------------------------------------
  722.  */
  723. static void
  724. ComputeFormat(scalePtr)
  725.     TkScale *scalePtr; /* Information about scale widget. */
  726. {
  727.     double maxValue, x;
  728.     int mostSigDigit, numDigits, leastSigDigit, afterDecimal;
  729.     int eDigits, fDigits;
  730.     /*
  731.      * Compute the displacement from the decimal of the most significant
  732.      * digit required for any number in the scale's range.
  733.      */
  734.     maxValue = fabs(scalePtr->fromValue);
  735.     x = fabs(scalePtr->toValue);
  736.     if (x > maxValue) {
  737. maxValue = x;
  738.     }
  739.     if (maxValue == 0) {
  740. maxValue = 1;
  741.     }
  742.     mostSigDigit = (int) floor(log10(maxValue));
  743.     /*
  744.      * If the number of significant digits wasn't specified explicitly,
  745.      * compute it. It's the difference between the most significant
  746.      * digit needed to represent any number on the scale and the
  747.      * most significant digit of the smallest difference between
  748.      * numbers on the scale.  In other words, display enough digits so
  749.      * that at least one digit will be different between any two adjacent
  750.      * positions of the scale.
  751.      */
  752.     numDigits = scalePtr->digits;
  753.     if (numDigits <= 0) {
  754. if  (scalePtr->resolution > 0) {
  755.     /*
  756.      * A resolution was specified for the scale, so just use it.
  757.      */
  758.     leastSigDigit = (int) floor(log10(scalePtr->resolution));
  759. } else {
  760.     /*
  761.      * No resolution was specified, so compute the difference
  762.      * in value between adjacent pixels and use it for the least
  763.      * significant digit.
  764.      */
  765.     x = fabs(scalePtr->fromValue - scalePtr->toValue);
  766.     if (scalePtr->length > 0) {
  767. x /= scalePtr->length;
  768.     }
  769.     if (x > 0){
  770. leastSigDigit = (int) floor(log10(x));
  771.     } else {
  772. leastSigDigit = 0;
  773.     }
  774. }
  775. numDigits = mostSigDigit - leastSigDigit + 1;
  776. if (numDigits < 1) {
  777.     numDigits = 1;
  778. }
  779.     }
  780.     /*
  781.      * Compute the number of characters required using "e" format and
  782.      * "f" format, and then choose whichever one takes fewer characters.
  783.      */
  784.     eDigits = numDigits + 4;
  785.     if (numDigits > 1) {
  786. eDigits++; /* Decimal point. */
  787.     }
  788.     afterDecimal = numDigits - mostSigDigit - 1;
  789.     if (afterDecimal < 0) {
  790. afterDecimal = 0;
  791.     }
  792.     fDigits = (mostSigDigit >= 0) ? mostSigDigit + afterDecimal : afterDecimal;
  793.     if (afterDecimal > 0) {
  794. fDigits++; /* Decimal point. */
  795.     }
  796.     if (mostSigDigit < 0) {
  797. fDigits++; /* Zero to left of decimal point. */
  798.     }
  799.     if (fDigits <= eDigits) {
  800. sprintf(scalePtr->format, "%%.%df", afterDecimal);
  801.     } else {
  802. sprintf(scalePtr->format, "%%.%de", numDigits-1);
  803.     }
  804. }
  805. /*
  806.  *----------------------------------------------------------------------
  807.  *
  808.  * ComputeScaleGeometry --
  809.  *
  810.  * This procedure is called to compute various geometrical
  811.  * information for a scale, such as where various things get
  812.  * displayed.  It's called when the window is reconfigured.
  813.  *
  814.  * Results:
  815.  * None.
  816.  *
  817.  * Side effects:
  818.  * Display-related numbers get changed in *scalePtr.  The
  819.  * geometry manager gets told about the window's preferred size.
  820.  *
  821.  *----------------------------------------------------------------------
  822.  */
  823. static void
  824. ComputeScaleGeometry(scalePtr)
  825.     register TkScale *scalePtr; /* Information about widget. */
  826. {
  827.     char valueString[PRINT_CHARS];
  828.     int tmp, valuePixels, x, y, extraSpace;
  829.     Tk_FontMetrics fm;
  830.     Tk_GetFontMetrics(scalePtr->tkfont, &fm);
  831.     scalePtr->fontHeight = fm.linespace + SPACING;
  832.     /*
  833.      * Horizontal scales are simpler than vertical ones because
  834.      * all sizes are the same (the height of a line of text);
  835.      * handle them first and then quit.
  836.      */
  837.     if (scalePtr->orient == ORIENT_HORIZONTAL) {
  838. y = scalePtr->inset;
  839. extraSpace = 0;
  840. if (scalePtr->labelLength != 0) {
  841.     scalePtr->horizLabelY = y + SPACING;
  842.     y += scalePtr->fontHeight;
  843.     extraSpace = SPACING;
  844. }
  845. if (scalePtr->showValue) {
  846.     scalePtr->horizValueY = y + SPACING;
  847.     y += scalePtr->fontHeight;
  848.     extraSpace = SPACING;
  849. } else {
  850.     scalePtr->horizValueY = y;
  851. }
  852. y += extraSpace;
  853. scalePtr->horizTroughY = y;
  854. y += scalePtr->width + 2*scalePtr->borderWidth;
  855. if (scalePtr->tickInterval != 0) {
  856.     scalePtr->horizTickY = y + SPACING;
  857.     y += scalePtr->fontHeight + SPACING;
  858. }
  859. Tk_GeometryRequest(scalePtr->tkwin,
  860. scalePtr->length + 2*scalePtr->inset, y + scalePtr->inset);
  861. Tk_SetInternalBorder(scalePtr->tkwin, scalePtr->inset);
  862. return;
  863.     }
  864.     /*
  865.      * Vertical scale:  compute the amount of space needed to display
  866.      * the scales value by formatting strings for the two end points;
  867.      * use whichever length is longer.
  868.      */
  869.     sprintf(valueString, scalePtr->format, scalePtr->fromValue);
  870.     valuePixels = Tk_TextWidth(scalePtr->tkfont, valueString, -1);
  871.     sprintf(valueString, scalePtr->format, scalePtr->toValue);
  872.     tmp = Tk_TextWidth(scalePtr->tkfont, valueString, -1);
  873.     if (valuePixels < tmp) {
  874. valuePixels = tmp;
  875.     }
  876.     /*
  877.      * Assign x-locations to the elements of the scale, working from
  878.      * left to right.
  879.      */
  880.     x = scalePtr->inset;
  881.     if ((scalePtr->tickInterval != 0) && (scalePtr->showValue)) {
  882. scalePtr->vertTickRightX = x + SPACING + valuePixels;
  883. scalePtr->vertValueRightX = scalePtr->vertTickRightX + valuePixels
  884. + fm.ascent/2;
  885. x = scalePtr->vertValueRightX + SPACING;
  886.     } else if (scalePtr->tickInterval != 0) {
  887. scalePtr->vertTickRightX = x + SPACING + valuePixels;
  888. scalePtr->vertValueRightX = scalePtr->vertTickRightX;
  889. x = scalePtr->vertTickRightX + SPACING;
  890.     } else if (scalePtr->showValue) {
  891. scalePtr->vertTickRightX = x;
  892. scalePtr->vertValueRightX = x + SPACING + valuePixels;
  893. x = scalePtr->vertValueRightX + SPACING;
  894.     } else {
  895. scalePtr->vertTickRightX = x;
  896. scalePtr->vertValueRightX = x;
  897.     }
  898.     scalePtr->vertTroughX = x;
  899.     x += 2*scalePtr->borderWidth + scalePtr->width;
  900.     if (scalePtr->labelLength == 0) {
  901. scalePtr->vertLabelX = 0;
  902.     } else {
  903. scalePtr->vertLabelX = x + fm.ascent/2;
  904. x = scalePtr->vertLabelX + fm.ascent/2
  905.     + Tk_TextWidth(scalePtr->tkfont, scalePtr->label,
  906.     scalePtr->labelLength);
  907.     }
  908.     Tk_GeometryRequest(scalePtr->tkwin, x + scalePtr->inset,
  909.     scalePtr->length + 2*scalePtr->inset);
  910.     Tk_SetInternalBorder(scalePtr->tkwin, scalePtr->inset);
  911. }
  912. /*
  913.  *--------------------------------------------------------------
  914.  *
  915.  * ScaleEventProc --
  916.  *
  917.  * This procedure is invoked by the Tk dispatcher for various
  918.  * events on scales.
  919.  *
  920.  * Results:
  921.  * None.
  922.  *
  923.  * Side effects:
  924.  * When the window gets deleted, internal structures get
  925.  * cleaned up.  When it gets exposed, it is redisplayed.
  926.  *
  927.  *--------------------------------------------------------------
  928.  */
  929. static void
  930. ScaleEventProc(clientData, eventPtr)
  931.     ClientData clientData; /* Information about window. */
  932.     XEvent *eventPtr; /* Information about event. */
  933. {
  934.     TkScale *scalePtr = (TkScale *) clientData;
  935.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  936. TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
  937.     } else if (eventPtr->type == DestroyNotify) {
  938. DestroyScale((char *) clientData);
  939.     } else if (eventPtr->type == ConfigureNotify) {
  940. ComputeScaleGeometry(scalePtr);
  941. TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
  942.     } else if (eventPtr->type == FocusIn) {
  943. if (eventPtr->xfocus.detail != NotifyInferior) {
  944.     scalePtr->flags |= GOT_FOCUS;
  945.     if (scalePtr->highlightWidth > 0) {
  946. TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
  947.     }
  948. }
  949.     } else if (eventPtr->type == FocusOut) {
  950. if (eventPtr->xfocus.detail != NotifyInferior) {
  951.     scalePtr->flags &= ~GOT_FOCUS;
  952.     if (scalePtr->highlightWidth > 0) {
  953. TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
  954.     }
  955. }
  956.     }
  957. }
  958. /*
  959.  *----------------------------------------------------------------------
  960.  *
  961.  * ScaleCmdDeletedProc --
  962.  *
  963.  * This procedure is invoked when a widget command is deleted.  If
  964.  * the widget isn't already in the process of being destroyed,
  965.  * this command destroys it.
  966.  *
  967.  * Results:
  968.  * None.
  969.  *
  970.  * Side effects:
  971.  * The widget is destroyed.
  972.  *
  973.  *----------------------------------------------------------------------
  974.  */
  975. static void
  976. ScaleCmdDeletedProc(clientData)
  977.     ClientData clientData; /* Pointer to widget record for widget. */
  978. {
  979.     TkScale *scalePtr = (TkScale *) clientData;
  980.     Tk_Window tkwin = scalePtr->tkwin;
  981.     /*
  982.      * This procedure could be invoked either because the window was
  983.      * destroyed and the command was then deleted (in which case tkwin
  984.      * is NULL) or because the command was deleted, and then this procedure
  985.      * destroys the widget.
  986.      */
  987.     if (!(scalePtr->flags & SCALE_DELETED)) {
  988. scalePtr->flags |= SCALE_DELETED;
  989. Tk_DestroyWindow(tkwin);
  990.     }
  991. }
  992. /*
  993.  *--------------------------------------------------------------
  994.  *
  995.  * TkEventuallyRedrawScale --
  996.  *
  997.  * Arrange for part or all of a scale widget to redrawn at
  998.  * the next convenient time in the future.
  999.  *
  1000.  * Results:
  1001.  * None.
  1002.  *
  1003.  * Side effects:
  1004.  * If "what" is REDRAW_SLIDER then just the slider and the
  1005.  * value readout will be redrawn;  if "what" is REDRAW_ALL
  1006.  * then the entire widget will be redrawn.
  1007.  *
  1008.  *--------------------------------------------------------------
  1009.  */
  1010. void
  1011. TkEventuallyRedrawScale(scalePtr, what)
  1012.     register TkScale *scalePtr; /* Information about widget. */
  1013.     int what; /* What to redraw:  REDRAW_SLIDER
  1014.  * or REDRAW_ALL. */
  1015. {
  1016.     if ((what == 0) || (scalePtr->tkwin == NULL)
  1017.     || !Tk_IsMapped(scalePtr->tkwin)) {
  1018. return;
  1019.     }
  1020.     if (!(scalePtr->flags & REDRAW_PENDING)) {
  1021. scalePtr->flags |= REDRAW_PENDING;
  1022. Tcl_DoWhenIdle(TkpDisplayScale, (ClientData) scalePtr);
  1023.     }
  1024.     scalePtr->flags |= what;
  1025. }
  1026. /*
  1027.  *--------------------------------------------------------------
  1028.  *
  1029.  * TkRoundToResolution --
  1030.  *
  1031.  * Round a given floating-point value to the nearest multiple
  1032.  * of the scale's resolution.
  1033.  *
  1034.  * Results:
  1035.  * The return value is the rounded result.
  1036.  *
  1037.  * Side effects:
  1038.  * None.
  1039.  *
  1040.  *--------------------------------------------------------------
  1041.  */
  1042. double
  1043. TkRoundToResolution(scalePtr, value)
  1044.     TkScale *scalePtr; /* Information about scale widget. */
  1045.     double value; /* Value to round. */
  1046. {
  1047.     double rem, new, tick;
  1048.     if (scalePtr->resolution <= 0) {
  1049. return value;
  1050.     }
  1051.     tick = floor(value/scalePtr->resolution);
  1052.     new = scalePtr->resolution * tick;
  1053.     rem = value - new;
  1054.     if (rem < 0) {
  1055. if (rem <= -scalePtr->resolution/2) {
  1056.     new = (tick - 1.0) * scalePtr->resolution;
  1057. }
  1058.     } else {
  1059. if (rem >= scalePtr->resolution/2) {
  1060.     new = (tick + 1.0) * scalePtr->resolution;
  1061. }
  1062.     }
  1063.     return new;
  1064. }
  1065. /*
  1066.  *----------------------------------------------------------------------
  1067.  *
  1068.  * ScaleVarProc --
  1069.  *
  1070.  * This procedure is invoked by Tcl whenever someone modifies a
  1071.  * variable associated with a scale widget.
  1072.  *
  1073.  * Results:
  1074.  * NULL is always returned.
  1075.  *
  1076.  * Side effects:
  1077.  * The value displayed in the scale will change to match the
  1078.  * variable's new value.  If the variable has a bogus value then
  1079.  * it is reset to the value of the scale.
  1080.  *
  1081.  *----------------------------------------------------------------------
  1082.  */
  1083.     /* ARGSUSED */
  1084. static char *
  1085. ScaleVarProc(clientData, interp, name1, name2, flags)
  1086.     ClientData clientData; /* Information about button. */
  1087.     Tcl_Interp *interp; /* Interpreter containing variable. */
  1088.     CONST char *name1; /* Name of variable. */
  1089.     CONST char *name2; /* Second part of variable name. */
  1090.     int flags; /* Information about what happened. */
  1091. {
  1092.     register TkScale *scalePtr = (TkScale *) clientData;
  1093.     char *resultStr;
  1094.     double value;
  1095.     Tcl_Obj *valuePtr;
  1096.     int result;
  1097.     /*
  1098.      * If the variable is unset, then immediately recreate it unless
  1099.      * the whole interpreter is going away.
  1100.      */
  1101.     if (flags & TCL_TRACE_UNSETS) {
  1102. if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
  1103.     Tcl_TraceVar(interp, Tcl_GetString(scalePtr->varNamePtr),
  1104.     TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  1105.     ScaleVarProc, clientData);
  1106.     scalePtr->flags |= NEVER_SET;
  1107.     TkScaleSetValue(scalePtr, scalePtr->value, 1, 0);
  1108. }
  1109. return (char *) NULL;
  1110.     }
  1111.     /*
  1112.      * If we came here because we updated the variable (in TkScaleSetValue),
  1113.      * then ignore the trace.  Otherwise update the scale with the value
  1114.      * of the variable.
  1115.      */
  1116.     if (scalePtr->flags & SETTING_VAR) {
  1117. return (char *) NULL;
  1118.     }
  1119.     resultStr = NULL;
  1120.     valuePtr = Tcl_ObjGetVar2(interp, scalePtr->varNamePtr, NULL, 
  1121.             TCL_GLOBAL_ONLY);
  1122.     result = Tcl_GetDoubleFromObj(interp, valuePtr, &value);
  1123.     if (result != TCL_OK) {
  1124.         resultStr = "can't assign non-numeric value to scale variable";
  1125. ScaleSetVariable(scalePtr);
  1126.     } else {
  1127. scalePtr->value = TkRoundToResolution(scalePtr, value);
  1128. /*
  1129.  * This code is a bit tricky because it sets the scale's value before
  1130.  * calling TkScaleSetValue.  This way, TkScaleSetValue won't bother 
  1131.  * to set the variable again or to invoke the -command.  However, it
  1132.  * also won't redisplay the scale, so we have to ask for that
  1133.  * explicitly.
  1134.  */
  1135. TkScaleSetValue(scalePtr, scalePtr->value, 1, 0);
  1136.     }
  1137.     TkEventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
  1138.     return resultStr;
  1139. }
  1140. /*
  1141.  *--------------------------------------------------------------
  1142.  *
  1143.  * TkScaleSetValue --
  1144.  *
  1145.  * This procedure changes the value of a scale and invokes
  1146.  * a Tcl command to reflect the current position of a scale
  1147.  *
  1148.  * Results:
  1149.  * None.
  1150.  *
  1151.  * Side effects:
  1152.  * A Tcl command is invoked, and an additional error-processing
  1153.  * command may also be invoked.  The scale's slider is redrawn.
  1154.  *
  1155.  *--------------------------------------------------------------
  1156.  */
  1157. void
  1158. TkScaleSetValue(scalePtr, value, setVar, invokeCommand)
  1159.     register TkScale *scalePtr; /* Info about widget. */
  1160.     double value; /* New value for scale.  Gets adjusted
  1161.  * if it's off the scale. */
  1162.     int setVar; /* Non-zero means reflect new value through
  1163.  * to associated variable, if any. */
  1164.     int invokeCommand; /* Non-zero means invoked -command option
  1165.  * to notify of new value, 0 means don't. */
  1166. {
  1167.     value = TkRoundToResolution(scalePtr, value);
  1168.     if ((value < scalePtr->fromValue)
  1169.     ^ (scalePtr->toValue < scalePtr->fromValue)) {
  1170. value = scalePtr->fromValue;
  1171.     }
  1172.     if ((value > scalePtr->toValue)
  1173.     ^ (scalePtr->toValue < scalePtr->fromValue)) {
  1174. value = scalePtr->toValue;
  1175.     }
  1176.     if (scalePtr->flags & NEVER_SET) {
  1177. scalePtr->flags &= ~NEVER_SET;
  1178.     } else if (scalePtr->value == value) {
  1179. return;
  1180.     }
  1181.     scalePtr->value = value;
  1182.     if (invokeCommand) {
  1183. scalePtr->flags |= INVOKE_COMMAND;
  1184.     }
  1185.     TkEventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
  1186.     if (setVar && scalePtr->varNamePtr) {
  1187. ScaleSetVariable(scalePtr);
  1188.     }
  1189. }
  1190. /*
  1191.  *--------------------------------------------------------------
  1192.  *
  1193.  * ScaleSetVariable --
  1194.  *
  1195.  * This procedure sets the variable associated with a scale, if any.
  1196.  *
  1197.  * Results:
  1198.  * None.
  1199.  *
  1200.  * Side effects:
  1201.  * Other write traces on the variable will trigger.
  1202.  *
  1203.  *--------------------------------------------------------------
  1204.  */
  1205. static void
  1206. ScaleSetVariable(scalePtr)
  1207.     register TkScale *scalePtr; /* Info about widget. */
  1208. {
  1209.     if (scalePtr->varNamePtr != NULL) {
  1210. char string[PRINT_CHARS];
  1211. sprintf(string, scalePtr->format, scalePtr->value);
  1212. scalePtr->flags |= SETTING_VAR;
  1213. Tcl_ObjSetVar2(scalePtr->interp, scalePtr->varNamePtr, NULL,
  1214. Tcl_NewStringObj(string, -1), TCL_GLOBAL_ONLY);
  1215. scalePtr->flags &= ~SETTING_VAR;
  1216.     }
  1217. }
  1218. /*
  1219.  *----------------------------------------------------------------------
  1220.  *
  1221.  * TkScalePixelToValue --
  1222.  *
  1223.  * Given a pixel within a scale window, return the scale
  1224.  * reading corresponding to that pixel.
  1225.  *
  1226.  * Results:
  1227.  * A double-precision scale reading.  If the value is outside
  1228.  * the legal range for the scale then it's rounded to the nearest
  1229.  * end of the scale.
  1230.  *
  1231.  * Side effects:
  1232.  * None.
  1233.  *
  1234.  *----------------------------------------------------------------------
  1235.  */
  1236. double
  1237. TkScalePixelToValue(scalePtr, x, y)
  1238.     register TkScale *scalePtr; /* Information about widget. */
  1239.     int x, y; /* Coordinates of point within
  1240.  * window. */
  1241. {
  1242.     double value, pixelRange;
  1243.     if (scalePtr->orient == ORIENT_VERTICAL) {
  1244. pixelRange = Tk_Height(scalePtr->tkwin) - scalePtr->sliderLength
  1245. - 2*scalePtr->inset - 2*scalePtr->borderWidth;
  1246. value = y;
  1247.     } else {
  1248. pixelRange = Tk_Width(scalePtr->tkwin) - scalePtr->sliderLength
  1249. - 2*scalePtr->inset - 2*scalePtr->borderWidth;
  1250. value = x;
  1251.     }
  1252.     if (pixelRange <= 0) {
  1253. /*
  1254.  * Not enough room for the slider to actually slide:  just return
  1255.  * the scale's current value.
  1256.  */
  1257. return scalePtr->value;
  1258.     }
  1259.     value -= scalePtr->sliderLength/2 + scalePtr->inset
  1260. + scalePtr->borderWidth;
  1261.     value /= pixelRange;
  1262.     if (value < 0) {
  1263. value = 0;
  1264.     }
  1265.     if (value > 1) {
  1266. value = 1;
  1267.     }
  1268.     value = scalePtr->fromValue +
  1269. value * (scalePtr->toValue - scalePtr->fromValue);
  1270.     return TkRoundToResolution(scalePtr, value);
  1271. }
  1272. /*
  1273.  *----------------------------------------------------------------------
  1274.  *
  1275.  * TkScaleValueToPixel --
  1276.  *
  1277.  * Given a reading of the scale, return the x-coordinate or
  1278.  * y-coordinate corresponding to that reading, depending on
  1279.  * whether the scale is vertical or horizontal, respectively.
  1280.  *
  1281.  * Results:
  1282.  * An integer value giving the pixel location corresponding
  1283.  * to reading.  The value is restricted to lie within the
  1284.  * defined range for the scale.
  1285.  *
  1286.  * Side effects:
  1287.  * None.
  1288.  *
  1289.  *----------------------------------------------------------------------
  1290.  */
  1291. int
  1292. TkScaleValueToPixel(scalePtr, value)
  1293.     register TkScale *scalePtr; /* Information about widget. */
  1294.     double value; /* Reading of the widget. */
  1295. {
  1296.     int y, pixelRange;
  1297.     double valueRange;
  1298.     valueRange = scalePtr->toValue - scalePtr->fromValue;
  1299.     pixelRange = ((scalePtr->orient == ORIENT_VERTICAL)
  1300.     ? Tk_Height(scalePtr->tkwin) : Tk_Width(scalePtr->tkwin))
  1301. - scalePtr->sliderLength - 2*scalePtr->inset - 2*scalePtr->borderWidth;
  1302.     if (valueRange == 0) {
  1303. y = 0;
  1304.     } else {
  1305. y = (int) ((value - scalePtr->fromValue) * pixelRange
  1306.   / valueRange + 0.5);
  1307. if (y < 0) {
  1308.     y = 0;
  1309. } else if (y > pixelRange) {
  1310.     y = pixelRange;
  1311. }
  1312.     }
  1313.     y += scalePtr->sliderLength/2 + scalePtr->inset + scalePtr->borderWidth;
  1314.     return y;
  1315. }