1182 lines
36 KiB
C
1182 lines
36 KiB
C
/*
|
||
* tkScale.c --
|
||
*
|
||
* This module implements a scale widgets for the Tk toolkit.
|
||
* A scale displays a slider that can be adjusted to change a
|
||
* value; it also displays numeric labels and a textual label,
|
||
* if desired.
|
||
*
|
||
* The modifications to use floating-point values are based on
|
||
* an implementation by Paul Mackerras. The -variable option
|
||
* is due to Henning Schulzrinne. All of these are used with
|
||
* permission.
|
||
*
|
||
* Copyright (c) 1990-1994 The Regents of the University of California.
|
||
* Copyright (c) 1994-1996 Sun Microsystems, Inc.
|
||
*
|
||
* See the file "license.terms" for information on usage and redistribution
|
||
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||
*
|
||
* SCCS: @(#) tkScale.c 1.88 97/07/31 09:11:57
|
||
*/
|
||
|
||
#include "tkPort.h"
|
||
#include "default.h"
|
||
#include "tkInt.h"
|
||
#ifdef STk_CODE
|
||
# include <math.h>
|
||
#else
|
||
# include "tclMath.h"
|
||
#endif
|
||
#include "tkScale.h"
|
||
|
||
static Tk_ConfigSpec configSpecs[] = {
|
||
{TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
|
||
DEF_SCALE_ACTIVE_BG_COLOR, Tk_Offset(TkScale, activeBorder),
|
||
TK_CONFIG_COLOR_ONLY},
|
||
{TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
|
||
DEF_SCALE_ACTIVE_BG_MONO, Tk_Offset(TkScale, activeBorder),
|
||
TK_CONFIG_MONO_ONLY},
|
||
{TK_CONFIG_BORDER, "-background", "background", "Background",
|
||
DEF_SCALE_BG_COLOR, Tk_Offset(TkScale, bgBorder),
|
||
TK_CONFIG_COLOR_ONLY},
|
||
{TK_CONFIG_BORDER, "-background", "background", "Background",
|
||
DEF_SCALE_BG_MONO, Tk_Offset(TkScale, bgBorder),
|
||
TK_CONFIG_MONO_ONLY},
|
||
{TK_CONFIG_DOUBLE, "-bigincrement", "bigIncrement", "BigIncrement",
|
||
DEF_SCALE_BIG_INCREMENT, Tk_Offset(TkScale, bigIncrement), 0},
|
||
{TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
|
||
(char *) NULL, 0, 0},
|
||
{TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
|
||
(char *) NULL, 0, 0},
|
||
{TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
|
||
DEF_SCALE_BORDER_WIDTH, Tk_Offset(TkScale, borderWidth), 0},
|
||
#ifdef STk_CODE
|
||
{TK_CONFIG_CLOSURE, "-command", "command", "Command",
|
||
#else
|
||
{TK_CONFIG_STRING, "-command", "command", "Command",
|
||
#endif
|
||
DEF_SCALE_COMMAND, Tk_Offset(TkScale, command), TK_CONFIG_NULL_OK},
|
||
{TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
|
||
DEF_SCALE_CURSOR, Tk_Offset(TkScale, cursor), TK_CONFIG_NULL_OK},
|
||
{TK_CONFIG_INT, "-digits", "digits", "Digits",
|
||
DEF_SCALE_DIGITS, Tk_Offset(TkScale, digits), 0},
|
||
#ifdef STk_CODE
|
||
{TK_CONFIG_ENV, "-environment", "environment", "Environment",
|
||
DEF_SCALE_ENV, Tk_Offset(TkScale, env), 0},
|
||
#endif
|
||
{TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
|
||
(char *) NULL, 0, 0},
|
||
{TK_CONFIG_FONT, "-font", "font", "Font",
|
||
DEF_SCALE_FONT, Tk_Offset(TkScale, tkfont),
|
||
0},
|
||
{TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
|
||
DEF_SCALE_FG_COLOR, Tk_Offset(TkScale, textColorPtr),
|
||
TK_CONFIG_COLOR_ONLY},
|
||
{TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
|
||
DEF_SCALE_FG_MONO, Tk_Offset(TkScale, textColorPtr),
|
||
TK_CONFIG_MONO_ONLY},
|
||
{TK_CONFIG_DOUBLE, "-from", "from", "From",
|
||
DEF_SCALE_FROM, Tk_Offset(TkScale, fromValue), 0},
|
||
{TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
|
||
"HighlightBackground", DEF_SCALE_HIGHLIGHT_BG,
|
||
Tk_Offset(TkScale, highlightBgColorPtr), 0},
|
||
{TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
|
||
DEF_SCALE_HIGHLIGHT, Tk_Offset(TkScale, highlightColorPtr), 0},
|
||
{TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
|
||
"HighlightThickness",
|
||
DEF_SCALE_HIGHLIGHT_WIDTH, Tk_Offset(TkScale, highlightWidth), 0},
|
||
{TK_CONFIG_STRING, "-label", "label", "Label",
|
||
DEF_SCALE_LABEL, Tk_Offset(TkScale, label), TK_CONFIG_NULL_OK},
|
||
{TK_CONFIG_PIXELS, "-length", "length", "Length",
|
||
DEF_SCALE_LENGTH, Tk_Offset(TkScale, length), 0},
|
||
{TK_CONFIG_UID, "-orient", "orient", "Orient",
|
||
DEF_SCALE_ORIENT, Tk_Offset(TkScale, orientUid), 0},
|
||
{TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
|
||
DEF_SCALE_RELIEF, Tk_Offset(TkScale, relief), 0},
|
||
{TK_CONFIG_INT, "-repeatdelay", "repeatDelay", "RepeatDelay",
|
||
DEF_SCALE_REPEAT_DELAY, Tk_Offset(TkScale, repeatDelay), 0},
|
||
{TK_CONFIG_INT, "-repeatinterval", "repeatInterval", "RepeatInterval",
|
||
DEF_SCALE_REPEAT_INTERVAL, Tk_Offset(TkScale, repeatInterval), 0},
|
||
{TK_CONFIG_DOUBLE, "-resolution", "resolution", "Resolution",
|
||
DEF_SCALE_RESOLUTION, Tk_Offset(TkScale, resolution), 0},
|
||
{TK_CONFIG_BOOLEAN, "-showvalue", "showValue", "ShowValue",
|
||
DEF_SCALE_SHOW_VALUE, Tk_Offset(TkScale, showValue), 0},
|
||
{TK_CONFIG_PIXELS, "-sliderlength", "sliderLength", "SliderLength",
|
||
DEF_SCALE_SLIDER_LENGTH, Tk_Offset(TkScale, sliderLength), 0},
|
||
{TK_CONFIG_RELIEF, "-sliderrelief", "sliderRelief", "SliderRelief",
|
||
DEF_SCALE_SLIDER_RELIEF, Tk_Offset(TkScale, sliderRelief),
|
||
TK_CONFIG_DONT_SET_DEFAULT},
|
||
{TK_CONFIG_UID, "-state", "state", "State",
|
||
DEF_SCALE_STATE, Tk_Offset(TkScale, state), 0},
|
||
#ifdef STk_CODE
|
||
{TK_CONFIG_CLOSURE, "-takefocus", "takeFocus", "TakeFocus",
|
||
#else
|
||
{TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
|
||
#endif
|
||
DEF_SCALE_TAKE_FOCUS, Tk_Offset(TkScale, takeFocus),
|
||
TK_CONFIG_NULL_OK},
|
||
{TK_CONFIG_DOUBLE, "-tickinterval", "tickInterval", "TickInterval",
|
||
DEF_SCALE_TICK_INTERVAL, Tk_Offset(TkScale, tickInterval), 0},
|
||
{TK_CONFIG_DOUBLE, "-to", "to", "To",
|
||
DEF_SCALE_TO, Tk_Offset(TkScale, toValue), 0},
|
||
{TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
|
||
DEF_SCALE_TROUGH_COLOR, Tk_Offset(TkScale, troughColorPtr),
|
||
TK_CONFIG_COLOR_ONLY},
|
||
{TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
|
||
DEF_SCALE_TROUGH_MONO, Tk_Offset(TkScale, troughColorPtr),
|
||
TK_CONFIG_MONO_ONLY},
|
||
{TK_CONFIG_STRING, "-variable", "variable", "Variable",
|
||
DEF_SCALE_VARIABLE, Tk_Offset(TkScale, varName), TK_CONFIG_NULL_OK},
|
||
{TK_CONFIG_PIXELS, "-width", "width", "Width",
|
||
DEF_SCALE_WIDTH, Tk_Offset(TkScale, width), 0},
|
||
{TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
|
||
(char *) NULL, 0, 0}
|
||
};
|
||
|
||
/*
|
||
* Forward declarations for procedures defined later in this file:
|
||
*/
|
||
|
||
static void ComputeFormat _ANSI_ARGS_((TkScale *scalePtr));
|
||
static void ComputeScaleGeometry _ANSI_ARGS_((TkScale *scalePtr));
|
||
static int ConfigureScale _ANSI_ARGS_((Tcl_Interp *interp,
|
||
TkScale *scalePtr, int argc, char **argv,
|
||
int flags));
|
||
static void DestroyScale _ANSI_ARGS_((char *memPtr));
|
||
static void ScaleCmdDeletedProc _ANSI_ARGS_((
|
||
ClientData clientData));
|
||
static void ScaleEventProc _ANSI_ARGS_((ClientData clientData,
|
||
XEvent *eventPtr));
|
||
static char * ScaleVarProc _ANSI_ARGS_((ClientData clientData,
|
||
Tcl_Interp *interp, char *name1, char *name2,
|
||
int flags));
|
||
static int ScaleWidgetCmd _ANSI_ARGS_((ClientData clientData,
|
||
Tcl_Interp *interp, int argc, char **argv));
|
||
static void ScaleWorldChanged _ANSI_ARGS_((
|
||
ClientData instanceData));
|
||
|
||
/*
|
||
* The structure below defines scale class behavior by means of procedures
|
||
* that can be invoked from generic window code.
|
||
*/
|
||
|
||
static TkClassProcs scaleClass = {
|
||
NULL, /* createProc. */
|
||
ScaleWorldChanged, /* geometryProc. */
|
||
NULL /* modalProc. */
|
||
};
|
||
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* Tk_ScaleCmd --
|
||
*
|
||
* This procedure is invoked to process the "scale" Tcl
|
||
* command. See the user documentation for details on what
|
||
* it does.
|
||
*
|
||
* Results:
|
||
* A standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
int
|
||
Tk_ScaleCmd(clientData, interp, argc, argv)
|
||
ClientData clientData; /* Main window associated with
|
||
* interpreter. */
|
||
Tcl_Interp *interp; /* Current interpreter. */
|
||
int argc; /* Number of arguments. */
|
||
char **argv; /* Argument strings. */
|
||
{
|
||
Tk_Window tkwin = (Tk_Window) clientData;
|
||
register TkScale *scalePtr;
|
||
Tk_Window new;
|
||
|
||
if (argc < 2) {
|
||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
argv[0], " pathName ?options?\"", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
|
||
if (new == NULL) {
|
||
return TCL_ERROR;
|
||
}
|
||
scalePtr = TkpCreateScale(new);
|
||
|
||
/*
|
||
* Initialize fields that won't be initialized by ConfigureScale,
|
||
* or which ConfigureScale expects to have reasonable values
|
||
* (e.g. resource pointers).
|
||
*/
|
||
|
||
scalePtr->tkwin = new;
|
||
scalePtr->display = Tk_Display(new);
|
||
scalePtr->interp = interp;
|
||
scalePtr->widgetCmd = Tcl_CreateCommand(interp,
|
||
Tk_PathName(scalePtr->tkwin), ScaleWidgetCmd,
|
||
(ClientData) scalePtr, ScaleCmdDeletedProc);
|
||
scalePtr->orientUid = NULL;
|
||
scalePtr->vertical = 0;
|
||
scalePtr->width = 0;
|
||
scalePtr->length = 0;
|
||
scalePtr->value = 0;
|
||
scalePtr->varName = NULL;
|
||
scalePtr->fromValue = 0;
|
||
scalePtr->toValue = 0;
|
||
scalePtr->tickInterval = 0;
|
||
scalePtr->resolution = 1;
|
||
scalePtr->bigIncrement = 0.0;
|
||
scalePtr->command = NULL;
|
||
scalePtr->repeatDelay = 0;
|
||
scalePtr->repeatInterval = 0;
|
||
scalePtr->label = NULL;
|
||
scalePtr->labelLength = 0;
|
||
scalePtr->state = tkNormalUid;
|
||
scalePtr->borderWidth = 0;
|
||
scalePtr->bgBorder = NULL;
|
||
scalePtr->activeBorder = NULL;
|
||
scalePtr->sliderRelief = TK_RELIEF_RAISED;
|
||
scalePtr->troughColorPtr = NULL;
|
||
scalePtr->troughGC = None;
|
||
scalePtr->copyGC = None;
|
||
scalePtr->tkfont = NULL;
|
||
scalePtr->textColorPtr = NULL;
|
||
scalePtr->textGC = None;
|
||
scalePtr->relief = TK_RELIEF_FLAT;
|
||
scalePtr->highlightWidth = 0;
|
||
scalePtr->highlightBgColorPtr = NULL;
|
||
scalePtr->highlightColorPtr = NULL;
|
||
scalePtr->inset = 0;
|
||
scalePtr->sliderLength = 0;
|
||
scalePtr->showValue = 0;
|
||
scalePtr->horizLabelY = 0;
|
||
scalePtr->horizValueY = 0;
|
||
scalePtr->horizTroughY = 0;
|
||
scalePtr->horizTickY = 0;
|
||
scalePtr->vertTickRightX = 0;
|
||
scalePtr->vertValueRightX = 0;
|
||
scalePtr->vertTroughX = 0;
|
||
scalePtr->vertLabelX = 0;
|
||
scalePtr->cursor = None;
|
||
scalePtr->takeFocus = NULL;
|
||
scalePtr->flags = NEVER_SET;
|
||
#ifdef STk_CODE
|
||
scalePtr->env = NULL;
|
||
#endif
|
||
|
||
Tk_SetClass(scalePtr->tkwin, "Scale");
|
||
TkSetClassProcs(scalePtr->tkwin, &scaleClass, (ClientData) scalePtr);
|
||
Tk_CreateEventHandler(scalePtr->tkwin,
|
||
ExposureMask|StructureNotifyMask|FocusChangeMask,
|
||
ScaleEventProc, (ClientData) scalePtr);
|
||
if (ConfigureScale(interp, scalePtr, argc-2, argv+2, 0) != TCL_OK) {
|
||
goto error;
|
||
}
|
||
|
||
#ifdef STk_CODE
|
||
STk_sharp_dot_result(interp, Tk_PathName(scalePtr->tkwin));
|
||
#else
|
||
interp->result = Tk_PathName(scalePtr->tkwin);
|
||
#endif
|
||
return TCL_OK;
|
||
|
||
error:
|
||
Tk_DestroyWindow(scalePtr->tkwin);
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* ScaleWidgetCmd --
|
||
*
|
||
* This procedure is invoked to process the Tcl command
|
||
* that corresponds to a widget managed by this module.
|
||
* See the user documentation for details on what it does.
|
||
*
|
||
* Results:
|
||
* A standard Tcl result.
|
||
*
|
||
* Side effects:
|
||
* See the user documentation.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
static int
|
||
ScaleWidgetCmd(clientData, interp, argc, argv)
|
||
ClientData clientData; /* Information about scale
|
||
* widget. */
|
||
Tcl_Interp *interp; /* Current interpreter. */
|
||
int argc; /* Number of arguments. */
|
||
char **argv; /* Argument strings. */
|
||
{
|
||
register TkScale *scalePtr = (TkScale *) clientData;
|
||
int result = TCL_OK;
|
||
size_t length;
|
||
int c;
|
||
|
||
if (argc < 2) {
|
||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
argv[0], " option ?arg arg ...?\"", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
Tcl_Preserve((ClientData) scalePtr);
|
||
c = argv[1][0];
|
||
length = strlen(argv[1]);
|
||
if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
|
||
&& (length >= 2)) {
|
||
if (argc != 3) {
|
||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
argv[0], " cget option\"",
|
||
(char *) NULL);
|
||
goto error;
|
||
}
|
||
result = Tk_ConfigureValue(interp, scalePtr->tkwin, configSpecs,
|
||
(char *) scalePtr, argv[2], 0);
|
||
} else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
|
||
&& (length >= 3)) {
|
||
if (argc == 2) {
|
||
result = Tk_ConfigureInfo(interp, scalePtr->tkwin, configSpecs,
|
||
(char *) scalePtr, (char *) NULL, 0);
|
||
} else if (argc == 3) {
|
||
result = Tk_ConfigureInfo(interp, scalePtr->tkwin, configSpecs,
|
||
(char *) scalePtr, argv[2], 0);
|
||
} else {
|
||
result = ConfigureScale(interp, scalePtr, argc-2, argv+2,
|
||
TK_CONFIG_ARGV_ONLY);
|
||
}
|
||
} else if ((c == 'c') && (strncmp(argv[1], "coords", length) == 0)
|
||
&& (length >= 3)) {
|
||
int x, y ;
|
||
double value;
|
||
|
||
if ((argc != 2) && (argc != 3)) {
|
||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
argv[0], " coords ?value?\"", (char *) NULL);
|
||
goto error;
|
||
}
|
||
if (argc == 3) {
|
||
if (Tcl_GetDouble(interp, argv[2], &value) != TCL_OK) {
|
||
goto error;
|
||
}
|
||
} else {
|
||
value = scalePtr->value;
|
||
}
|
||
if (scalePtr->vertical) {
|
||
x = scalePtr->vertTroughX + scalePtr->width/2
|
||
+ scalePtr->borderWidth;
|
||
y = TkpValueToPixel(scalePtr, value);
|
||
} else {
|
||
x = TkpValueToPixel(scalePtr, value);
|
||
y = scalePtr->horizTroughY + scalePtr->width/2
|
||
+ scalePtr->borderWidth;
|
||
}
|
||
sprintf(interp->result, "%d %d", x, y);
|
||
} else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
|
||
double value;
|
||
int x, y;
|
||
|
||
if ((argc != 2) && (argc != 4)) {
|
||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
argv[0], " get ?x y?\"", (char *) NULL);
|
||
goto error;
|
||
}
|
||
if (argc == 2) {
|
||
value = scalePtr->value;
|
||
} else {
|
||
if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
|
||
|| (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
|
||
goto error;
|
||
}
|
||
value = TkpPixelToValue(scalePtr, x, y);
|
||
}
|
||
sprintf(interp->result, scalePtr->format, value);
|
||
} else if ((c == 'i') && (strncmp(argv[1], "identify", length) == 0)) {
|
||
int x, y, thing;
|
||
|
||
if (argc != 4) {
|
||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
argv[0], " identify x y\"", (char *) NULL);
|
||
goto error;
|
||
}
|
||
if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
|
||
|| (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
|
||
goto error;
|
||
}
|
||
thing = TkpScaleElement(scalePtr, x,y);
|
||
switch (thing) {
|
||
#ifdef STk_CODE
|
||
case TROUGH1: interp->result = "\"trough1\""; break;
|
||
case SLIDER: interp->result = "\"slider\""; break;
|
||
case TROUGH2: interp->result = "\"trough2\""; break;
|
||
default: interp->result = "\"\"";
|
||
#else
|
||
case TROUGH1: interp->result = "trough1"; break;
|
||
case SLIDER: interp->result = "slider"; break;
|
||
case TROUGH2: interp->result = "trough2"; break;
|
||
#endif
|
||
}
|
||
} else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
|
||
double value;
|
||
|
||
if (argc != 3) {
|
||
Tcl_AppendResult(interp, "wrong # args: should be \"",
|
||
argv[0], " set value\"", (char *) NULL);
|
||
goto error;
|
||
}
|
||
if (Tcl_GetDouble(interp, argv[2], &value) != TCL_OK) {
|
||
goto error;
|
||
}
|
||
if (scalePtr->state != tkDisabledUid) {
|
||
TkpSetScaleValue(scalePtr, value, 1, 1);
|
||
}
|
||
} else {
|
||
Tcl_AppendResult(interp, "bad option \"", argv[1],
|
||
"\": must be cget, configure, coords, get, identify, or set",
|
||
(char *) NULL);
|
||
goto error;
|
||
}
|
||
Tcl_Release((ClientData) scalePtr);
|
||
return result;
|
||
|
||
error:
|
||
Tcl_Release((ClientData) scalePtr);
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* DestroyScale --
|
||
*
|
||
* This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
|
||
* to clean up the internal structure of a button at a safe time
|
||
* (when no-one is using it anymore).
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Everything associated with the scale is freed up.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
DestroyScale(memPtr)
|
||
char *memPtr; /* Info about scale widget. */
|
||
{
|
||
register TkScale *scalePtr = (TkScale *) memPtr;
|
||
|
||
/*
|
||
* Free up all the stuff that requires special handling, then
|
||
* let Tk_FreeOptions handle all the standard option-related
|
||
* stuff.
|
||
*/
|
||
|
||
if (scalePtr->varName != NULL) {
|
||
Tcl_UntraceVar(scalePtr->interp, scalePtr->varName,
|
||
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
|
||
ScaleVarProc, (ClientData) scalePtr);
|
||
}
|
||
if (scalePtr->troughGC != None) {
|
||
Tk_FreeGC(scalePtr->display, scalePtr->troughGC);
|
||
}
|
||
if (scalePtr->copyGC != None) {
|
||
Tk_FreeGC(scalePtr->display, scalePtr->copyGC);
|
||
}
|
||
if (scalePtr->textGC != None) {
|
||
Tk_FreeGC(scalePtr->display, scalePtr->textGC);
|
||
}
|
||
Tk_FreeOptions(configSpecs, (char *) scalePtr, scalePtr->display, 0);
|
||
TkpDestroyScale(scalePtr);
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* ConfigureScale --
|
||
*
|
||
* This procedure is called to process an argv/argc list, plus
|
||
* the Tk option database, in order to configure (or
|
||
* reconfigure) a scale widget.
|
||
*
|
||
* Results:
|
||
* The return value is a standard Tcl result. If TCL_ERROR is
|
||
* returned, then interp->result contains an error message.
|
||
*
|
||
* Side effects:
|
||
* Configuration information, such as colors, border width,
|
||
* etc. get set for scalePtr; old resources get freed,
|
||
* if there were any.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static int
|
||
ConfigureScale(interp, scalePtr, argc, argv, flags)
|
||
Tcl_Interp *interp; /* Used for error reporting. */
|
||
register TkScale *scalePtr; /* Information about widget; may or may
|
||
* not already have values for some fields. */
|
||
int argc; /* Number of valid entries in argv. */
|
||
char **argv; /* Arguments. */
|
||
int flags; /* Flags to pass to Tk_ConfigureWidget. */
|
||
{
|
||
size_t length;
|
||
|
||
/*
|
||
* Eliminate any existing trace on a variable monitored by the scale.
|
||
*/
|
||
|
||
if (scalePtr->varName != NULL) {
|
||
Tcl_UntraceVar(interp, scalePtr->varName,
|
||
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
|
||
ScaleVarProc, (ClientData) scalePtr);
|
||
}
|
||
|
||
if (Tk_ConfigureWidget(interp, scalePtr->tkwin, configSpecs,
|
||
argc, argv, (char *) scalePtr, flags) != TCL_OK) {
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
/*
|
||
* If the scale is tied to the value of a variable, then set up
|
||
* a trace on the variable's value and set the scale's value from
|
||
* the value of the variable, if it exists.
|
||
*/
|
||
|
||
if (scalePtr->varName != NULL) {
|
||
char *stringValue, *end;
|
||
double value;
|
||
|
||
#ifdef STk_CODE
|
||
stringValue = STk_tcl_getvar(scalePtr->varName, scalePtr->env);
|
||
#else
|
||
stringValue = Tcl_GetVar(interp, scalePtr->varName, TCL_GLOBAL_ONLY);
|
||
#endif
|
||
if (stringValue != NULL) {
|
||
value = strtod(stringValue, &end);
|
||
if ((end != stringValue) && (*end == 0)) {
|
||
scalePtr->value = TkRoundToResolution(scalePtr, value);
|
||
}
|
||
}
|
||
Tcl_TraceVar(interp, scalePtr->varName,
|
||
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
|
||
ScaleVarProc, (ClientData) scalePtr);
|
||
}
|
||
|
||
/*
|
||
* Several options need special processing, such as parsing the
|
||
* orientation and creating GCs.
|
||
*/
|
||
|
||
length = strlen(scalePtr->orientUid);
|
||
if (strncmp(scalePtr->orientUid, "vertical", length) == 0) {
|
||
scalePtr->vertical = 1;
|
||
} else if (strncmp(scalePtr->orientUid, "horizontal", length) == 0) {
|
||
scalePtr->vertical = 0;
|
||
} else {
|
||
Tcl_AppendResult(interp, "bad orientation \"", scalePtr->orientUid,
|
||
"\": must be vertical or horizontal", (char *) NULL);
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
scalePtr->fromValue = TkRoundToResolution(scalePtr, scalePtr->fromValue);
|
||
scalePtr->toValue = TkRoundToResolution(scalePtr, scalePtr->toValue);
|
||
scalePtr->tickInterval = TkRoundToResolution(scalePtr,
|
||
scalePtr->tickInterval);
|
||
|
||
/*
|
||
* Make sure that the tick interval has the right sign so that
|
||
* addition moves from fromValue to toValue.
|
||
*/
|
||
|
||
if ((scalePtr->tickInterval < 0)
|
||
^ ((scalePtr->toValue - scalePtr->fromValue) < 0)) {
|
||
scalePtr->tickInterval = -scalePtr->tickInterval;
|
||
}
|
||
|
||
/*
|
||
* Set the scale value to itself; all this does is to make sure
|
||
* that the scale's value is within the new acceptable range for
|
||
* the scale and reflect the value in the associated variable,
|
||
* if any.
|
||
*/
|
||
|
||
ComputeFormat(scalePtr);
|
||
TkpSetScaleValue(scalePtr, scalePtr->value, 1, 1);
|
||
|
||
if (scalePtr->label != NULL) {
|
||
scalePtr->labelLength = strlen(scalePtr->label);
|
||
} else {
|
||
scalePtr->labelLength = 0;
|
||
}
|
||
|
||
if ((scalePtr->state != tkNormalUid)
|
||
&& (scalePtr->state != tkDisabledUid)
|
||
&& (scalePtr->state != tkActiveUid)) {
|
||
Tcl_AppendResult(interp, "bad state value \"", scalePtr->state,
|
||
"\": must be normal, active, or disabled", (char *) NULL);
|
||
scalePtr->state = tkNormalUid;
|
||
return TCL_ERROR;
|
||
}
|
||
|
||
Tk_SetBackgroundFromBorder(scalePtr->tkwin, scalePtr->bgBorder);
|
||
|
||
if (scalePtr->highlightWidth < 0) {
|
||
scalePtr->highlightWidth = 0;
|
||
}
|
||
scalePtr->inset = scalePtr->highlightWidth + scalePtr->borderWidth;
|
||
|
||
ScaleWorldChanged((ClientData) scalePtr);
|
||
return TCL_OK;
|
||
}
|
||
|
||
/*
|
||
*---------------------------------------------------------------------------
|
||
*
|
||
* ScaleWorldChanged --
|
||
*
|
||
* This procedure is called when the world has changed in some
|
||
* way and the widget needs to recompute all its graphics contexts
|
||
* and determine its new geometry.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Scale will be relayed out and redisplayed.
|
||
*
|
||
*---------------------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
ScaleWorldChanged(instanceData)
|
||
ClientData instanceData; /* Information about widget. */
|
||
{
|
||
XGCValues gcValues;
|
||
GC gc;
|
||
TkScale *scalePtr;
|
||
|
||
scalePtr = (TkScale *) instanceData;
|
||
|
||
gcValues.foreground = scalePtr->troughColorPtr->pixel;
|
||
gc = Tk_GetGC(scalePtr->tkwin, GCForeground, &gcValues);
|
||
if (scalePtr->troughGC != None) {
|
||
Tk_FreeGC(scalePtr->display, scalePtr->troughGC);
|
||
}
|
||
scalePtr->troughGC = gc;
|
||
|
||
gcValues.font = Tk_FontId(scalePtr->tkfont);
|
||
gcValues.foreground = scalePtr->textColorPtr->pixel;
|
||
gc = Tk_GetGC(scalePtr->tkwin, GCForeground | GCFont, &gcValues);
|
||
if (scalePtr->textGC != None) {
|
||
Tk_FreeGC(scalePtr->display, scalePtr->textGC);
|
||
}
|
||
scalePtr->textGC = gc;
|
||
|
||
if (scalePtr->copyGC == None) {
|
||
gcValues.graphics_exposures = False;
|
||
scalePtr->copyGC = Tk_GetGC(scalePtr->tkwin, GCGraphicsExposures,
|
||
&gcValues);
|
||
}
|
||
scalePtr->inset = scalePtr->highlightWidth + scalePtr->borderWidth;
|
||
|
||
/*
|
||
* Recompute display-related information, and let the geometry
|
||
* manager know how much space is needed now.
|
||
*/
|
||
|
||
ComputeScaleGeometry(scalePtr);
|
||
|
||
TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* ComputeFormat --
|
||
*
|
||
* This procedure is invoked to recompute the "format" field
|
||
* of a scale's widget record, which determines how the value
|
||
* of the scale is converted to a string.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* The format field of scalePtr is modified.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
ComputeFormat(scalePtr)
|
||
TkScale *scalePtr; /* Information about scale widget. */
|
||
{
|
||
double maxValue, x;
|
||
int mostSigDigit, numDigits, leastSigDigit, afterDecimal;
|
||
int eDigits, fDigits;
|
||
|
||
/*
|
||
* Compute the displacement from the decimal of the most significant
|
||
* digit required for any number in the scale's range.
|
||
*/
|
||
|
||
maxValue = fabs(scalePtr->fromValue);
|
||
x = fabs(scalePtr->toValue);
|
||
if (x > maxValue) {
|
||
maxValue = x;
|
||
}
|
||
if (maxValue == 0) {
|
||
maxValue = 1;
|
||
}
|
||
mostSigDigit = (int) floor(log10(maxValue));
|
||
|
||
/*
|
||
* If the number of significant digits wasn't specified explicitly,
|
||
* compute it. It's the difference between the most significant
|
||
* digit needed to represent any number on the scale and the
|
||
* most significant digit of the smallest difference between
|
||
* numbers on the scale. In other words, display enough digits so
|
||
* that at least one digit will be different between any two adjacent
|
||
* positions of the scale.
|
||
*/
|
||
|
||
numDigits = scalePtr->digits;
|
||
if (numDigits <= 0) {
|
||
if (scalePtr->resolution > 0) {
|
||
/*
|
||
* A resolution was specified for the scale, so just use it.
|
||
*/
|
||
|
||
leastSigDigit = (int) floor(log10(scalePtr->resolution));
|
||
} else {
|
||
/*
|
||
* No resolution was specified, so compute the difference
|
||
* in value between adjacent pixels and use it for the least
|
||
* significant digit.
|
||
*/
|
||
|
||
x = fabs(scalePtr->fromValue - scalePtr->toValue);
|
||
if (scalePtr->length > 0) {
|
||
x /= scalePtr->length;
|
||
}
|
||
if (x > 0){
|
||
leastSigDigit = (int) floor(log10(x));
|
||
} else {
|
||
leastSigDigit = 0;
|
||
}
|
||
}
|
||
numDigits = mostSigDigit - leastSigDigit + 1;
|
||
if (numDigits < 1) {
|
||
numDigits = 1;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Compute the number of characters required using "e" format and
|
||
* "f" format, and then choose whichever one takes fewer characters.
|
||
*/
|
||
|
||
eDigits = numDigits + 4;
|
||
if (numDigits > 1) {
|
||
eDigits++; /* Decimal point. */
|
||
}
|
||
afterDecimal = numDigits - mostSigDigit - 1;
|
||
if (afterDecimal < 0) {
|
||
afterDecimal = 0;
|
||
}
|
||
fDigits = (mostSigDigit >= 0) ? mostSigDigit + afterDecimal : afterDecimal;
|
||
if (afterDecimal > 0) {
|
||
fDigits++; /* Decimal point. */
|
||
}
|
||
if (mostSigDigit < 0) {
|
||
fDigits++; /* Zero to left of decimal point. */
|
||
}
|
||
if (fDigits <= eDigits) {
|
||
sprintf(scalePtr->format, "%%.%df", afterDecimal);
|
||
} else {
|
||
sprintf(scalePtr->format, "%%.%de", numDigits-1);
|
||
}
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* ComputeScaleGeometry --
|
||
*
|
||
* This procedure is called to compute various geometrical
|
||
* information for a scale, such as where various things get
|
||
* displayed. It's called when the window is reconfigured.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Display-related numbers get changed in *scalePtr. The
|
||
* geometry manager gets told about the window's preferred size.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
ComputeScaleGeometry(scalePtr)
|
||
register TkScale *scalePtr; /* Information about widget. */
|
||
{
|
||
char valueString[PRINT_CHARS];
|
||
int tmp, valuePixels, x, y, extraSpace;
|
||
Tk_FontMetrics fm;
|
||
|
||
/*
|
||
* Horizontal scales are simpler than vertical ones because
|
||
* all sizes are the same (the height of a line of text);
|
||
* handle them first and then quit.
|
||
*/
|
||
|
||
Tk_GetFontMetrics(scalePtr->tkfont, &fm);
|
||
if (!scalePtr->vertical) {
|
||
y = scalePtr->inset;
|
||
extraSpace = 0;
|
||
if (scalePtr->labelLength != 0) {
|
||
scalePtr->horizLabelY = y + SPACING;
|
||
y += fm.linespace + SPACING;
|
||
extraSpace = SPACING;
|
||
}
|
||
if (scalePtr->showValue) {
|
||
scalePtr->horizValueY = y + SPACING;
|
||
y += fm.linespace + SPACING;
|
||
extraSpace = SPACING;
|
||
} else {
|
||
scalePtr->horizValueY = y;
|
||
}
|
||
y += extraSpace;
|
||
scalePtr->horizTroughY = y;
|
||
y += scalePtr->width + 2*scalePtr->borderWidth;
|
||
if (scalePtr->tickInterval != 0) {
|
||
scalePtr->horizTickY = y + SPACING;
|
||
y += fm.linespace + 2*SPACING;
|
||
}
|
||
Tk_GeometryRequest(scalePtr->tkwin,
|
||
scalePtr->length + 2*scalePtr->inset, y + scalePtr->inset);
|
||
Tk_SetInternalBorder(scalePtr->tkwin, scalePtr->inset);
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* Vertical scale: compute the amount of space needed to display
|
||
* the scales value by formatting strings for the two end points;
|
||
* use whichever length is longer.
|
||
*/
|
||
|
||
sprintf(valueString, scalePtr->format, scalePtr->fromValue);
|
||
valuePixels = Tk_TextWidth(scalePtr->tkfont, valueString, -1);
|
||
|
||
sprintf(valueString, scalePtr->format, scalePtr->toValue);
|
||
tmp = Tk_TextWidth(scalePtr->tkfont, valueString, -1);
|
||
if (valuePixels < tmp) {
|
||
valuePixels = tmp;
|
||
}
|
||
|
||
/*
|
||
* Assign x-locations to the elements of the scale, working from
|
||
* left to right.
|
||
*/
|
||
|
||
x = scalePtr->inset;
|
||
if ((scalePtr->tickInterval != 0) && (scalePtr->showValue)) {
|
||
scalePtr->vertTickRightX = x + SPACING + valuePixels;
|
||
scalePtr->vertValueRightX = scalePtr->vertTickRightX + valuePixels
|
||
+ fm.ascent/2;
|
||
x = scalePtr->vertValueRightX + SPACING;
|
||
} else if (scalePtr->tickInterval != 0) {
|
||
scalePtr->vertTickRightX = x + SPACING + valuePixels;
|
||
scalePtr->vertValueRightX = scalePtr->vertTickRightX;
|
||
x = scalePtr->vertTickRightX + SPACING;
|
||
} else if (scalePtr->showValue) {
|
||
scalePtr->vertTickRightX = x;
|
||
scalePtr->vertValueRightX = x + SPACING + valuePixels;
|
||
x = scalePtr->vertValueRightX + SPACING;
|
||
} else {
|
||
scalePtr->vertTickRightX = x;
|
||
scalePtr->vertValueRightX = x;
|
||
}
|
||
scalePtr->vertTroughX = x;
|
||
x += 2*scalePtr->borderWidth + scalePtr->width;
|
||
if (scalePtr->labelLength == 0) {
|
||
scalePtr->vertLabelX = 0;
|
||
} else {
|
||
scalePtr->vertLabelX = x + fm.ascent/2;
|
||
x = scalePtr->vertLabelX + fm.ascent/2
|
||
+ Tk_TextWidth(scalePtr->tkfont, scalePtr->label,
|
||
scalePtr->labelLength);
|
||
}
|
||
Tk_GeometryRequest(scalePtr->tkwin, x + scalePtr->inset,
|
||
scalePtr->length + 2*scalePtr->inset);
|
||
Tk_SetInternalBorder(scalePtr->tkwin, scalePtr->inset);
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* ScaleEventProc --
|
||
*
|
||
* This procedure is invoked by the Tk dispatcher for various
|
||
* events on scales.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* When the window gets deleted, internal structures get
|
||
* cleaned up. When it gets exposed, it is redisplayed.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
ScaleEventProc(clientData, eventPtr)
|
||
ClientData clientData; /* Information about window. */
|
||
XEvent *eventPtr; /* Information about event. */
|
||
{
|
||
TkScale *scalePtr = (TkScale *) clientData;
|
||
|
||
if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
|
||
TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
|
||
} else if (eventPtr->type == DestroyNotify) {
|
||
if (scalePtr->tkwin != NULL) {
|
||
scalePtr->tkwin = NULL;
|
||
Tcl_DeleteCommandFromToken(scalePtr->interp, scalePtr->widgetCmd);
|
||
}
|
||
if (scalePtr->flags & REDRAW_ALL) {
|
||
Tcl_CancelIdleCall(TkpDisplayScale, (ClientData) scalePtr);
|
||
}
|
||
Tcl_EventuallyFree((ClientData) scalePtr, DestroyScale);
|
||
} else if (eventPtr->type == ConfigureNotify) {
|
||
ComputeScaleGeometry(scalePtr);
|
||
TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
|
||
} else if (eventPtr->type == FocusIn) {
|
||
if (eventPtr->xfocus.detail != NotifyInferior) {
|
||
scalePtr->flags |= GOT_FOCUS;
|
||
if (scalePtr->highlightWidth > 0) {
|
||
TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
|
||
}
|
||
}
|
||
} else if (eventPtr->type == FocusOut) {
|
||
if (eventPtr->xfocus.detail != NotifyInferior) {
|
||
scalePtr->flags &= ~GOT_FOCUS;
|
||
if (scalePtr->highlightWidth > 0) {
|
||
TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* ScaleCmdDeletedProc --
|
||
*
|
||
* This procedure is invoked when a widget command is deleted. If
|
||
* the widget isn't already in the process of being destroyed,
|
||
* this command destroys it.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* The widget is destroyed.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
static void
|
||
ScaleCmdDeletedProc(clientData)
|
||
ClientData clientData; /* Pointer to widget record for widget. */
|
||
{
|
||
TkScale *scalePtr = (TkScale *) clientData;
|
||
Tk_Window tkwin = scalePtr->tkwin;
|
||
|
||
/*
|
||
* This procedure could be invoked either because the window was
|
||
* destroyed and the command was then deleted (in which case tkwin
|
||
* is NULL) or because the command was deleted, and then this procedure
|
||
* destroys the widget.
|
||
*/
|
||
|
||
if (tkwin != NULL) {
|
||
scalePtr->tkwin = NULL;
|
||
Tk_DestroyWindow(tkwin);
|
||
}
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* TkEventuallyRedrawScale --
|
||
*
|
||
* Arrange for part or all of a scale widget to redrawn at
|
||
* the next convenient time in the future.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* If "what" is REDRAW_SLIDER then just the slider and the
|
||
* value readout will be redrawn; if "what" is REDRAW_ALL
|
||
* then the entire widget will be redrawn.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
TkEventuallyRedrawScale(scalePtr, what)
|
||
register TkScale *scalePtr; /* Information about widget. */
|
||
int what; /* What to redraw: REDRAW_SLIDER
|
||
* or REDRAW_ALL. */
|
||
{
|
||
if ((what == 0) || (scalePtr->tkwin == NULL)
|
||
|| !Tk_IsMapped(scalePtr->tkwin)) {
|
||
return;
|
||
}
|
||
if ((scalePtr->flags & REDRAW_ALL) == 0) {
|
||
Tcl_DoWhenIdle(TkpDisplayScale, (ClientData) scalePtr);
|
||
}
|
||
scalePtr->flags |= what;
|
||
}
|
||
|
||
/*
|
||
*--------------------------------------------------------------
|
||
*
|
||
* TkRoundToResolution --
|
||
*
|
||
* Round a given floating-point value to the nearest multiple
|
||
* of the scale's resolution.
|
||
*
|
||
* Results:
|
||
* The return value is the rounded result.
|
||
*
|
||
* Side effects:
|
||
* None.
|
||
*
|
||
*--------------------------------------------------------------
|
||
*/
|
||
|
||
double
|
||
TkRoundToResolution(scalePtr, value)
|
||
TkScale *scalePtr; /* Information about scale widget. */
|
||
double value; /* Value to round. */
|
||
{
|
||
double rem, new;
|
||
|
||
if (scalePtr->resolution <= 0) {
|
||
return value;
|
||
}
|
||
new = scalePtr->resolution * floor(value/scalePtr->resolution);
|
||
rem = value - new;
|
||
if (rem < 0) {
|
||
if (rem <= -scalePtr->resolution/2) {
|
||
new -= scalePtr->resolution;
|
||
}
|
||
} else {
|
||
if (rem >= scalePtr->resolution/2) {
|
||
new += scalePtr->resolution;
|
||
}
|
||
}
|
||
return new;
|
||
}
|
||
|
||
/*
|
||
*----------------------------------------------------------------------
|
||
*
|
||
* ScaleVarProc --
|
||
*
|
||
* This procedure is invoked by Tcl whenever someone modifies a
|
||
* variable associated with a scale widget.
|
||
*
|
||
* Results:
|
||
* NULL is always returned.
|
||
*
|
||
* Side effects:
|
||
* The value displayed in the scale will change to match the
|
||
* variable's new value. If the variable has a bogus value then
|
||
* it is reset to the value of the scale.
|
||
*
|
||
*----------------------------------------------------------------------
|
||
*/
|
||
|
||
/* ARGSUSED */
|
||
static char *
|
||
ScaleVarProc(clientData, interp, name1, name2, flags)
|
||
ClientData clientData; /* Information about button. */
|
||
Tcl_Interp *interp; /* Interpreter containing variable. */
|
||
char *name1; /* Name of variable. */
|
||
char *name2; /* Second part of variable name. */
|
||
int flags; /* Information about what happened. */
|
||
{
|
||
register TkScale *scalePtr = (TkScale *) clientData;
|
||
char *stringValue, *end, *result;
|
||
double value;
|
||
|
||
/*
|
||
* If the variable is unset, then immediately recreate it unless
|
||
* the whole interpreter is going away.
|
||
*/
|
||
|
||
if (flags & TCL_TRACE_UNSETS) {
|
||
if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
|
||
Tcl_TraceVar(interp, scalePtr->varName,
|
||
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
|
||
ScaleVarProc, clientData);
|
||
scalePtr->flags |= NEVER_SET;
|
||
TkpSetScaleValue(scalePtr, scalePtr->value, 1, 0);
|
||
}
|
||
return (char *) NULL;
|
||
}
|
||
|
||
/*
|
||
* If we came here because we updated the variable (in TkpSetScaleValue),
|
||
* then ignore the trace. Otherwise update the scale with the value
|
||
* of the variable.
|
||
*/
|
||
|
||
if (scalePtr->flags & SETTING_VAR) {
|
||
return (char *) NULL;
|
||
}
|
||
result = NULL;
|
||
#ifdef STk_CODE
|
||
stringValue = STk_tcl_getvar(scalePtr->varName, scalePtr->env);
|
||
#else
|
||
stringValue = Tcl_GetVar(interp, scalePtr->varName, TCL_GLOBAL_ONLY);
|
||
#endif
|
||
if (stringValue != NULL) {
|
||
value = strtod(stringValue, &end);
|
||
if ((end == stringValue) || (*end != 0)) {
|
||
result = "can't assign non-numeric value to scale variable";
|
||
} else {
|
||
scalePtr->value = TkRoundToResolution(scalePtr, value);
|
||
}
|
||
|
||
/*
|
||
* This code is a bit tricky because it sets the scale's value before
|
||
* calling TkpSetScaleValue. This way, TkpSetScaleValue won't bother
|
||
* to set the variable again or to invoke the -command. However, it
|
||
* also won't redisplay the scale, so we have to ask for that
|
||
* explicitly.
|
||
*/
|
||
|
||
TkpSetScaleValue(scalePtr, scalePtr->value, 1, 0);
|
||
TkEventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
|
||
}
|
||
|
||
return result;
|
||
}
|