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,
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
14
* Copyright (c) 1990-1994 The Regents of the University of California.
15
* Copyright (c) 1994-1996 Sun Microsystems, Inc.
17
* See the file "license.terms" for information on usage and redistribution
18
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
20
* RCS: @(#) $Id: tkScale.c,v 1.2 1998/09/14 18:23:16 stanton Exp $
29
static Tk_ConfigSpec configSpecs[] = {
30
{TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
31
DEF_SCALE_ACTIVE_BG_COLOR, Tk_Offset(TkScale, activeBorder),
32
TK_CONFIG_COLOR_ONLY},
33
{TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
34
DEF_SCALE_ACTIVE_BG_MONO, Tk_Offset(TkScale, activeBorder),
36
{TK_CONFIG_BORDER, "-background", "background", "Background",
37
DEF_SCALE_BG_COLOR, Tk_Offset(TkScale, bgBorder),
38
TK_CONFIG_COLOR_ONLY},
39
{TK_CONFIG_BORDER, "-background", "background", "Background",
40
DEF_SCALE_BG_MONO, Tk_Offset(TkScale, bgBorder),
42
{TK_CONFIG_DOUBLE, "-bigincrement", "bigIncrement", "BigIncrement",
43
DEF_SCALE_BIG_INCREMENT, Tk_Offset(TkScale, bigIncrement), 0},
44
{TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
46
{TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
48
{TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
49
DEF_SCALE_BORDER_WIDTH, Tk_Offset(TkScale, borderWidth), 0},
50
{TK_CONFIG_STRING, "-command", "command", "Command",
51
DEF_SCALE_COMMAND, Tk_Offset(TkScale, command), TK_CONFIG_NULL_OK},
52
{TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
53
DEF_SCALE_CURSOR, Tk_Offset(TkScale, cursor), TK_CONFIG_NULL_OK},
54
{TK_CONFIG_INT, "-digits", "digits", "Digits",
55
DEF_SCALE_DIGITS, Tk_Offset(TkScale, digits), 0},
56
{TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
58
{TK_CONFIG_FONT, "-font", "font", "Font",
59
DEF_SCALE_FONT, Tk_Offset(TkScale, tkfont),
61
{TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
62
DEF_SCALE_FG_COLOR, Tk_Offset(TkScale, textColorPtr),
63
TK_CONFIG_COLOR_ONLY},
64
{TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
65
DEF_SCALE_FG_MONO, Tk_Offset(TkScale, textColorPtr),
67
{TK_CONFIG_DOUBLE, "-from", "from", "From",
68
DEF_SCALE_FROM, Tk_Offset(TkScale, fromValue), 0},
69
{TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
70
"HighlightBackground", DEF_SCALE_HIGHLIGHT_BG,
71
Tk_Offset(TkScale, highlightBgColorPtr), 0},
72
{TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
73
DEF_SCALE_HIGHLIGHT, Tk_Offset(TkScale, highlightColorPtr), 0},
74
{TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
76
DEF_SCALE_HIGHLIGHT_WIDTH, Tk_Offset(TkScale, highlightWidth), 0},
77
{TK_CONFIG_STRING, "-label", "label", "Label",
78
DEF_SCALE_LABEL, Tk_Offset(TkScale, label), TK_CONFIG_NULL_OK},
79
{TK_CONFIG_PIXELS, "-length", "length", "Length",
80
DEF_SCALE_LENGTH, Tk_Offset(TkScale, length), 0},
81
{TK_CONFIG_UID, "-orient", "orient", "Orient",
82
DEF_SCALE_ORIENT, Tk_Offset(TkScale, orientUid), 0},
83
{TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
84
DEF_SCALE_RELIEF, Tk_Offset(TkScale, relief), 0},
85
{TK_CONFIG_INT, "-repeatdelay", "repeatDelay", "RepeatDelay",
86
DEF_SCALE_REPEAT_DELAY, Tk_Offset(TkScale, repeatDelay), 0},
87
{TK_CONFIG_INT, "-repeatinterval", "repeatInterval", "RepeatInterval",
88
DEF_SCALE_REPEAT_INTERVAL, Tk_Offset(TkScale, repeatInterval), 0},
89
{TK_CONFIG_DOUBLE, "-resolution", "resolution", "Resolution",
90
DEF_SCALE_RESOLUTION, Tk_Offset(TkScale, resolution), 0},
91
{TK_CONFIG_BOOLEAN, "-showvalue", "showValue", "ShowValue",
92
DEF_SCALE_SHOW_VALUE, Tk_Offset(TkScale, showValue), 0},
93
{TK_CONFIG_PIXELS, "-sliderlength", "sliderLength", "SliderLength",
94
DEF_SCALE_SLIDER_LENGTH, Tk_Offset(TkScale, sliderLength), 0},
95
{TK_CONFIG_RELIEF, "-sliderrelief", "sliderRelief", "SliderRelief",
96
DEF_SCALE_SLIDER_RELIEF, Tk_Offset(TkScale, sliderRelief),
97
TK_CONFIG_DONT_SET_DEFAULT},
98
{TK_CONFIG_UID, "-state", "state", "State",
99
DEF_SCALE_STATE, Tk_Offset(TkScale, state), 0},
100
{TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
101
DEF_SCALE_TAKE_FOCUS, Tk_Offset(TkScale, takeFocus),
103
{TK_CONFIG_DOUBLE, "-tickinterval", "tickInterval", "TickInterval",
104
DEF_SCALE_TICK_INTERVAL, Tk_Offset(TkScale, tickInterval), 0},
105
{TK_CONFIG_DOUBLE, "-to", "to", "To",
106
DEF_SCALE_TO, Tk_Offset(TkScale, toValue), 0},
107
{TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
108
DEF_SCALE_TROUGH_COLOR, Tk_Offset(TkScale, troughColorPtr),
109
TK_CONFIG_COLOR_ONLY},
110
{TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
111
DEF_SCALE_TROUGH_MONO, Tk_Offset(TkScale, troughColorPtr),
112
TK_CONFIG_MONO_ONLY},
113
{TK_CONFIG_STRING, "-variable", "variable", "Variable",
114
DEF_SCALE_VARIABLE, Tk_Offset(TkScale, varName), TK_CONFIG_NULL_OK},
115
{TK_CONFIG_PIXELS, "-width", "width", "Width",
116
DEF_SCALE_WIDTH, Tk_Offset(TkScale, width), 0},
117
{TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
122
* Forward declarations for procedures defined later in this file:
125
static void ComputeFormat _ANSI_ARGS_((TkScale *scalePtr));
126
static void ComputeScaleGeometry _ANSI_ARGS_((TkScale *scalePtr));
127
static int ConfigureScale _ANSI_ARGS_((Tcl_Interp *interp,
128
TkScale *scalePtr, int argc, char **argv,
130
static void DestroyScale _ANSI_ARGS_((char *memPtr));
131
static void ScaleCmdDeletedProc _ANSI_ARGS_((
132
ClientData clientData));
133
static void ScaleEventProc _ANSI_ARGS_((ClientData clientData,
135
static char * ScaleVarProc _ANSI_ARGS_((ClientData clientData,
136
Tcl_Interp *interp, char *name1, char *name2,
138
static int ScaleWidgetCmd _ANSI_ARGS_((ClientData clientData,
139
Tcl_Interp *interp, int argc, char **argv));
140
static void ScaleWorldChanged _ANSI_ARGS_((
141
ClientData instanceData));
144
* The structure below defines scale class behavior by means of procedures
145
* that can be invoked from generic window code.
148
static TkClassProcs scaleClass = {
149
NULL, /* createProc. */
150
ScaleWorldChanged, /* geometryProc. */
151
NULL /* modalProc. */
156
*--------------------------------------------------------------
160
* This procedure is invoked to process the "scale" Tcl
161
* command. See the user documentation for details on what
165
* A standard Tcl result.
168
* See the user documentation.
170
*--------------------------------------------------------------
174
Tk_ScaleCmd(clientData, interp, argc, argv)
175
ClientData clientData; /* Main window associated with
177
Tcl_Interp *interp; /* Current interpreter. */
178
int argc; /* Number of arguments. */
179
char **argv; /* Argument strings. */
181
Tk_Window tkwin = (Tk_Window) clientData;
182
register TkScale *scalePtr;
186
Tcl_AppendResult(interp, "wrong # args: should be \"",
187
argv[0], " pathName ?options?\"", (char *) NULL);
191
new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
195
scalePtr = TkpCreateScale(new);
198
* Initialize fields that won't be initialized by ConfigureScale,
199
* or which ConfigureScale expects to have reasonable values
200
* (e.g. resource pointers).
203
scalePtr->tkwin = new;
204
scalePtr->display = Tk_Display(new);
205
scalePtr->interp = interp;
206
scalePtr->widgetCmd = Tcl_CreateCommand(interp,
207
Tk_PathName(scalePtr->tkwin), ScaleWidgetCmd,
208
(ClientData) scalePtr, ScaleCmdDeletedProc);
209
scalePtr->orientUid = NULL;
210
scalePtr->vertical = 0;
212
scalePtr->length = 0;
214
scalePtr->varName = NULL;
215
scalePtr->fromValue = 0;
216
scalePtr->toValue = 0;
217
scalePtr->tickInterval = 0;
218
scalePtr->resolution = 1;
219
scalePtr->bigIncrement = 0.0;
220
scalePtr->command = NULL;
221
scalePtr->repeatDelay = 0;
222
scalePtr->repeatInterval = 0;
223
scalePtr->label = NULL;
224
scalePtr->labelLength = 0;
225
scalePtr->state = tkNormalUid;
226
scalePtr->borderWidth = 0;
227
scalePtr->bgBorder = NULL;
228
scalePtr->activeBorder = NULL;
229
scalePtr->sliderRelief = TK_RELIEF_RAISED;
230
scalePtr->troughColorPtr = NULL;
231
scalePtr->troughGC = None;
232
scalePtr->copyGC = None;
233
scalePtr->tkfont = NULL;
234
scalePtr->textColorPtr = NULL;
235
scalePtr->textGC = None;
236
scalePtr->relief = TK_RELIEF_FLAT;
237
scalePtr->highlightWidth = 0;
238
scalePtr->highlightBgColorPtr = NULL;
239
scalePtr->highlightColorPtr = NULL;
241
scalePtr->sliderLength = 0;
242
scalePtr->showValue = 0;
243
scalePtr->horizLabelY = 0;
244
scalePtr->horizValueY = 0;
245
scalePtr->horizTroughY = 0;
246
scalePtr->horizTickY = 0;
247
scalePtr->vertTickRightX = 0;
248
scalePtr->vertValueRightX = 0;
249
scalePtr->vertTroughX = 0;
250
scalePtr->vertLabelX = 0;
251
scalePtr->cursor = None;
252
scalePtr->takeFocus = NULL;
253
scalePtr->flags = NEVER_SET;
255
Tk_SetClass(scalePtr->tkwin, "Scale");
256
TkSetClassProcs(scalePtr->tkwin, &scaleClass, (ClientData) scalePtr);
257
Tk_CreateEventHandler(scalePtr->tkwin,
258
ExposureMask|StructureNotifyMask|FocusChangeMask,
259
ScaleEventProc, (ClientData) scalePtr);
260
if (ConfigureScale(interp, scalePtr, argc-2, argv+2, 0) != TCL_OK) {
264
interp->result = Tk_PathName(scalePtr->tkwin);
268
Tk_DestroyWindow(scalePtr->tkwin);
273
*--------------------------------------------------------------
277
* This procedure is invoked to process the Tcl command
278
* that corresponds to a widget managed by this module.
279
* See the user documentation for details on what it does.
282
* A standard Tcl result.
285
* See the user documentation.
287
*--------------------------------------------------------------
291
ScaleWidgetCmd(clientData, interp, argc, argv)
292
ClientData clientData; /* Information about scale
294
Tcl_Interp *interp; /* Current interpreter. */
295
int argc; /* Number of arguments. */
296
char **argv; /* Argument strings. */
298
register TkScale *scalePtr = (TkScale *) clientData;
304
Tcl_AppendResult(interp, "wrong # args: should be \"",
305
argv[0], " option ?arg arg ...?\"", (char *) NULL);
308
Tcl_Preserve((ClientData) scalePtr);
310
length = strlen(argv[1]);
311
if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
314
Tcl_AppendResult(interp, "wrong # args: should be \"",
315
argv[0], " cget option\"",
319
result = Tk_ConfigureValue(interp, scalePtr->tkwin, configSpecs,
320
(char *) scalePtr, argv[2], 0);
321
} else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
324
result = Tk_ConfigureInfo(interp, scalePtr->tkwin, configSpecs,
325
(char *) scalePtr, (char *) NULL, 0);
326
} else if (argc == 3) {
327
result = Tk_ConfigureInfo(interp, scalePtr->tkwin, configSpecs,
328
(char *) scalePtr, argv[2], 0);
330
result = ConfigureScale(interp, scalePtr, argc-2, argv+2,
331
TK_CONFIG_ARGV_ONLY);
333
} else if ((c == 'c') && (strncmp(argv[1], "coords", length) == 0)
338
if ((argc != 2) && (argc != 3)) {
339
Tcl_AppendResult(interp, "wrong # args: should be \"",
340
argv[0], " coords ?value?\"", (char *) NULL);
344
if (Tcl_GetDouble(interp, argv[2], &value) != TCL_OK) {
348
value = scalePtr->value;
350
if (scalePtr->vertical) {
351
x = scalePtr->vertTroughX + scalePtr->width/2
352
+ scalePtr->borderWidth;
353
y = TkpValueToPixel(scalePtr, value);
355
x = TkpValueToPixel(scalePtr, value);
356
y = scalePtr->horizTroughY + scalePtr->width/2
357
+ scalePtr->borderWidth;
359
sprintf(interp->result, "%d %d", x, y);
360
} else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
364
if ((argc != 2) && (argc != 4)) {
365
Tcl_AppendResult(interp, "wrong # args: should be \"",
366
argv[0], " get ?x y?\"", (char *) NULL);
370
value = scalePtr->value;
372
if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
373
|| (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
376
value = TkpPixelToValue(scalePtr, x, y);
378
sprintf(interp->result, scalePtr->format, value);
379
} else if ((c == 'i') && (strncmp(argv[1], "identify", length) == 0)) {
383
Tcl_AppendResult(interp, "wrong # args: should be \"",
384
argv[0], " identify x y\"", (char *) NULL);
387
if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
388
|| (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
391
thing = TkpScaleElement(scalePtr, x,y);
393
case TROUGH1: interp->result = "trough1"; break;
394
case SLIDER: interp->result = "slider"; break;
395
case TROUGH2: interp->result = "trough2"; break;
397
} else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
401
Tcl_AppendResult(interp, "wrong # args: should be \"",
402
argv[0], " set value\"", (char *) NULL);
405
if (Tcl_GetDouble(interp, argv[2], &value) != TCL_OK) {
408
if (scalePtr->state != tkDisabledUid) {
409
TkpSetScaleValue(scalePtr, value, 1, 1);
412
Tcl_AppendResult(interp, "bad option \"", argv[1],
413
"\": must be cget, configure, coords, get, identify, or set",
417
Tcl_Release((ClientData) scalePtr);
421
Tcl_Release((ClientData) scalePtr);
426
*----------------------------------------------------------------------
430
* This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
431
* to clean up the internal structure of a button at a safe time
432
* (when no-one is using it anymore).
438
* Everything associated with the scale is freed up.
440
*----------------------------------------------------------------------
445
char *memPtr; /* Info about scale widget. */
447
register TkScale *scalePtr = (TkScale *) memPtr;
450
* Free up all the stuff that requires special handling, then
451
* let Tk_FreeOptions handle all the standard option-related
455
if (scalePtr->varName != NULL) {
456
Tcl_UntraceVar(scalePtr->interp, scalePtr->varName,
457
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
458
ScaleVarProc, (ClientData) scalePtr);
460
if (scalePtr->troughGC != None) {
461
Tk_FreeGC(scalePtr->display, scalePtr->troughGC);
463
if (scalePtr->copyGC != None) {
464
Tk_FreeGC(scalePtr->display, scalePtr->copyGC);
466
if (scalePtr->textGC != None) {
467
Tk_FreeGC(scalePtr->display, scalePtr->textGC);
469
Tk_FreeOptions(configSpecs, (char *) scalePtr, scalePtr->display, 0);
470
TkpDestroyScale(scalePtr);
474
*----------------------------------------------------------------------
478
* This procedure is called to process an argv/argc list, plus
479
* the Tk option database, in order to configure (or
480
* reconfigure) a scale widget.
483
* The return value is a standard Tcl result. If TCL_ERROR is
484
* returned, then interp->result contains an error message.
487
* Configuration information, such as colors, border width,
488
* etc. get set for scalePtr; old resources get freed,
491
*----------------------------------------------------------------------
495
ConfigureScale(interp, scalePtr, argc, argv, flags)
496
Tcl_Interp *interp; /* Used for error reporting. */
497
register TkScale *scalePtr; /* Information about widget; may or may
498
* not already have values for some fields. */
499
int argc; /* Number of valid entries in argv. */
500
char **argv; /* Arguments. */
501
int flags; /* Flags to pass to Tk_ConfigureWidget. */
506
* Eliminate any existing trace on a variable monitored by the scale.
509
if (scalePtr->varName != NULL) {
510
Tcl_UntraceVar(interp, scalePtr->varName,
511
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
512
ScaleVarProc, (ClientData) scalePtr);
515
if (Tk_ConfigureWidget(interp, scalePtr->tkwin, configSpecs,
516
argc, argv, (char *) scalePtr, flags) != TCL_OK) {
521
* If the scale is tied to the value of a variable, then set up
522
* a trace on the variable's value and set the scale's value from
523
* the value of the variable, if it exists.
526
if (scalePtr->varName != NULL) {
527
char *stringValue, *end;
530
stringValue = Tcl_GetVar(interp, scalePtr->varName, TCL_GLOBAL_ONLY);
531
if (stringValue != NULL) {
532
value = strtod(stringValue, &end);
533
if ((end != stringValue) && (*end == 0)) {
534
scalePtr->value = TkRoundToResolution(scalePtr, value);
537
Tcl_TraceVar(interp, scalePtr->varName,
538
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
539
ScaleVarProc, (ClientData) scalePtr);
543
* Several options need special processing, such as parsing the
544
* orientation and creating GCs.
547
length = strlen(scalePtr->orientUid);
548
if (strncmp(scalePtr->orientUid, "vertical", length) == 0) {
549
scalePtr->vertical = 1;
550
} else if (strncmp(scalePtr->orientUid, "horizontal", length) == 0) {
551
scalePtr->vertical = 0;
553
Tcl_AppendResult(interp, "bad orientation \"", scalePtr->orientUid,
554
"\": must be vertical or horizontal", (char *) NULL);
558
scalePtr->fromValue = TkRoundToResolution(scalePtr, scalePtr->fromValue);
559
scalePtr->toValue = TkRoundToResolution(scalePtr, scalePtr->toValue);
560
scalePtr->tickInterval = TkRoundToResolution(scalePtr,
561
scalePtr->tickInterval);
564
* Make sure that the tick interval has the right sign so that
565
* addition moves from fromValue to toValue.
568
if ((scalePtr->tickInterval < 0)
569
^ ((scalePtr->toValue - scalePtr->fromValue) < 0)) {
570
scalePtr->tickInterval = -scalePtr->tickInterval;
574
* Set the scale value to itself; all this does is to make sure
575
* that the scale's value is within the new acceptable range for
576
* the scale and reflect the value in the associated variable,
580
ComputeFormat(scalePtr);
581
TkpSetScaleValue(scalePtr, scalePtr->value, 1, 1);
583
if (scalePtr->label != NULL) {
584
scalePtr->labelLength = strlen(scalePtr->label);
586
scalePtr->labelLength = 0;
589
if ((scalePtr->state != tkNormalUid)
590
&& (scalePtr->state != tkDisabledUid)
591
&& (scalePtr->state != tkActiveUid)) {
592
Tcl_AppendResult(interp, "bad state value \"", scalePtr->state,
593
"\": must be normal, active, or disabled", (char *) NULL);
594
scalePtr->state = tkNormalUid;
598
Tk_SetBackgroundFromBorder(scalePtr->tkwin, scalePtr->bgBorder);
600
if (scalePtr->highlightWidth < 0) {
601
scalePtr->highlightWidth = 0;
603
scalePtr->inset = scalePtr->highlightWidth + scalePtr->borderWidth;
605
ScaleWorldChanged((ClientData) scalePtr);
610
*---------------------------------------------------------------------------
612
* ScaleWorldChanged --
614
* This procedure is called when the world has changed in some
615
* way and the widget needs to recompute all its graphics contexts
616
* and determine its new geometry.
622
* Scale will be relayed out and redisplayed.
624
*---------------------------------------------------------------------------
628
ScaleWorldChanged(instanceData)
629
ClientData instanceData; /* Information about widget. */
635
scalePtr = (TkScale *) instanceData;
637
gcValues.foreground = scalePtr->troughColorPtr->pixel;
638
gc = Tk_GetGC(scalePtr->tkwin, GCForeground, &gcValues);
639
if (scalePtr->troughGC != None) {
640
Tk_FreeGC(scalePtr->display, scalePtr->troughGC);
642
scalePtr->troughGC = gc;
644
gcValues.font = Tk_FontId(scalePtr->tkfont);
645
gcValues.foreground = scalePtr->textColorPtr->pixel;
646
gc = Tk_GetGC(scalePtr->tkwin, GCForeground | GCFont, &gcValues);
647
if (scalePtr->textGC != None) {
648
Tk_FreeGC(scalePtr->display, scalePtr->textGC);
650
scalePtr->textGC = gc;
652
if (scalePtr->copyGC == None) {
653
gcValues.graphics_exposures = False;
654
scalePtr->copyGC = Tk_GetGC(scalePtr->tkwin, GCGraphicsExposures,
657
scalePtr->inset = scalePtr->highlightWidth + scalePtr->borderWidth;
660
* Recompute display-related information, and let the geometry
661
* manager know how much space is needed now.
664
ComputeScaleGeometry(scalePtr);
666
TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
670
*----------------------------------------------------------------------
674
* This procedure is invoked to recompute the "format" field
675
* of a scale's widget record, which determines how the value
676
* of the scale is converted to a string.
682
* The format field of scalePtr is modified.
684
*----------------------------------------------------------------------
688
ComputeFormat(scalePtr)
689
TkScale *scalePtr; /* Information about scale widget. */
692
int mostSigDigit, numDigits, leastSigDigit, afterDecimal;
693
int eDigits, fDigits;
696
* Compute the displacement from the decimal of the most significant
697
* digit required for any number in the scale's range.
700
maxValue = fabs(scalePtr->fromValue);
701
x = fabs(scalePtr->toValue);
708
mostSigDigit = (int) floor(log10(maxValue));
711
* If the number of significant digits wasn't specified explicitly,
712
* compute it. It's the difference between the most significant
713
* digit needed to represent any number on the scale and the
714
* most significant digit of the smallest difference between
715
* numbers on the scale. In other words, display enough digits so
716
* that at least one digit will be different between any two adjacent
717
* positions of the scale.
720
numDigits = scalePtr->digits;
721
if (numDigits <= 0) {
722
if (scalePtr->resolution > 0) {
724
* A resolution was specified for the scale, so just use it.
727
leastSigDigit = (int) floor(log10(scalePtr->resolution));
730
* No resolution was specified, so compute the difference
731
* in value between adjacent pixels and use it for the least
735
x = fabs(scalePtr->fromValue - scalePtr->toValue);
736
if (scalePtr->length > 0) {
737
x /= scalePtr->length;
740
leastSigDigit = (int) floor(log10(x));
745
numDigits = mostSigDigit - leastSigDigit + 1;
752
* Compute the number of characters required using "e" format and
753
* "f" format, and then choose whichever one takes fewer characters.
756
eDigits = numDigits + 4;
758
eDigits++; /* Decimal point. */
760
afterDecimal = numDigits - mostSigDigit - 1;
761
if (afterDecimal < 0) {
764
fDigits = (mostSigDigit >= 0) ? mostSigDigit + afterDecimal : afterDecimal;
765
if (afterDecimal > 0) {
766
fDigits++; /* Decimal point. */
768
if (mostSigDigit < 0) {
769
fDigits++; /* Zero to left of decimal point. */
771
if (fDigits <= eDigits) {
772
sprintf(scalePtr->format, "%%.%df", afterDecimal);
774
sprintf(scalePtr->format, "%%.%de", numDigits-1);
779
*----------------------------------------------------------------------
781
* ComputeScaleGeometry --
783
* This procedure is called to compute various geometrical
784
* information for a scale, such as where various things get
785
* displayed. It's called when the window is reconfigured.
791
* Display-related numbers get changed in *scalePtr. The
792
* geometry manager gets told about the window's preferred size.
794
*----------------------------------------------------------------------
798
ComputeScaleGeometry(scalePtr)
799
register TkScale *scalePtr; /* Information about widget. */
801
char valueString[PRINT_CHARS];
802
int tmp, valuePixels, x, y, extraSpace;
806
* Horizontal scales are simpler than vertical ones because
807
* all sizes are the same (the height of a line of text);
808
* handle them first and then quit.
811
Tk_GetFontMetrics(scalePtr->tkfont, &fm);
812
if (!scalePtr->vertical) {
815
if (scalePtr->labelLength != 0) {
816
scalePtr->horizLabelY = y + SPACING;
817
y += fm.linespace + SPACING;
818
extraSpace = SPACING;
820
if (scalePtr->showValue) {
821
scalePtr->horizValueY = y + SPACING;
822
y += fm.linespace + SPACING;
823
extraSpace = SPACING;
825
scalePtr->horizValueY = y;
828
scalePtr->horizTroughY = y;
829
y += scalePtr->width + 2*scalePtr->borderWidth;
830
if (scalePtr->tickInterval != 0) {
831
scalePtr->horizTickY = y + SPACING;
832
y += fm.linespace + 2*SPACING;
834
Tk_GeometryRequest(scalePtr->tkwin,
835
scalePtr->length + 2*scalePtr->inset, y + scalePtr->inset);
836
Tk_SetInternalBorder(scalePtr->tkwin, scalePtr->inset);
841
* Vertical scale: compute the amount of space needed to display
842
* the scales value by formatting strings for the two end points;
843
* use whichever length is longer.
846
sprintf(valueString, scalePtr->format, scalePtr->fromValue);
847
valuePixels = Tk_TextWidth(scalePtr->tkfont, valueString, -1);
849
sprintf(valueString, scalePtr->format, scalePtr->toValue);
850
tmp = Tk_TextWidth(scalePtr->tkfont, valueString, -1);
851
if (valuePixels < tmp) {
856
* Assign x-locations to the elements of the scale, working from
861
if ((scalePtr->tickInterval != 0) && (scalePtr->showValue)) {
862
scalePtr->vertTickRightX = x + SPACING + valuePixels;
863
scalePtr->vertValueRightX = scalePtr->vertTickRightX + valuePixels
865
x = scalePtr->vertValueRightX + SPACING;
866
} else if (scalePtr->tickInterval != 0) {
867
scalePtr->vertTickRightX = x + SPACING + valuePixels;
868
scalePtr->vertValueRightX = scalePtr->vertTickRightX;
869
x = scalePtr->vertTickRightX + SPACING;
870
} else if (scalePtr->showValue) {
871
scalePtr->vertTickRightX = x;
872
scalePtr->vertValueRightX = x + SPACING + valuePixels;
873
x = scalePtr->vertValueRightX + SPACING;
875
scalePtr->vertTickRightX = x;
876
scalePtr->vertValueRightX = x;
878
scalePtr->vertTroughX = x;
879
x += 2*scalePtr->borderWidth + scalePtr->width;
880
if (scalePtr->labelLength == 0) {
881
scalePtr->vertLabelX = 0;
883
scalePtr->vertLabelX = x + fm.ascent/2;
884
x = scalePtr->vertLabelX + fm.ascent/2
885
+ Tk_TextWidth(scalePtr->tkfont, scalePtr->label,
886
scalePtr->labelLength);
888
Tk_GeometryRequest(scalePtr->tkwin, x + scalePtr->inset,
889
scalePtr->length + 2*scalePtr->inset);
890
Tk_SetInternalBorder(scalePtr->tkwin, scalePtr->inset);
894
*--------------------------------------------------------------
898
* This procedure is invoked by the Tk dispatcher for various
905
* When the window gets deleted, internal structures get
906
* cleaned up. When it gets exposed, it is redisplayed.
908
*--------------------------------------------------------------
912
ScaleEventProc(clientData, eventPtr)
913
ClientData clientData; /* Information about window. */
914
XEvent *eventPtr; /* Information about event. */
916
TkScale *scalePtr = (TkScale *) clientData;
918
if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
919
TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
920
} else if (eventPtr->type == DestroyNotify) {
921
if (scalePtr->tkwin != NULL) {
922
scalePtr->tkwin = NULL;
923
Tcl_DeleteCommandFromToken(scalePtr->interp, scalePtr->widgetCmd);
925
if (scalePtr->flags & REDRAW_ALL) {
926
Tcl_CancelIdleCall(TkpDisplayScale, (ClientData) scalePtr);
928
Tcl_EventuallyFree((ClientData) scalePtr, DestroyScale);
929
} else if (eventPtr->type == ConfigureNotify) {
930
ComputeScaleGeometry(scalePtr);
931
TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
932
} else if (eventPtr->type == FocusIn) {
933
if (eventPtr->xfocus.detail != NotifyInferior) {
934
scalePtr->flags |= GOT_FOCUS;
935
if (scalePtr->highlightWidth > 0) {
936
TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
939
} else if (eventPtr->type == FocusOut) {
940
if (eventPtr->xfocus.detail != NotifyInferior) {
941
scalePtr->flags &= ~GOT_FOCUS;
942
if (scalePtr->highlightWidth > 0) {
943
TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
950
*----------------------------------------------------------------------
952
* ScaleCmdDeletedProc --
954
* This procedure is invoked when a widget command is deleted. If
955
* the widget isn't already in the process of being destroyed,
956
* this command destroys it.
962
* The widget is destroyed.
964
*----------------------------------------------------------------------
968
ScaleCmdDeletedProc(clientData)
969
ClientData clientData; /* Pointer to widget record for widget. */
971
TkScale *scalePtr = (TkScale *) clientData;
972
Tk_Window tkwin = scalePtr->tkwin;
975
* This procedure could be invoked either because the window was
976
* destroyed and the command was then deleted (in which case tkwin
977
* is NULL) or because the command was deleted, and then this procedure
978
* destroys the widget.
982
scalePtr->tkwin = NULL;
983
Tk_DestroyWindow(tkwin);
988
*--------------------------------------------------------------
990
* TkEventuallyRedrawScale --
992
* Arrange for part or all of a scale widget to redrawn at
993
* the next convenient time in the future.
999
* If "what" is REDRAW_SLIDER then just the slider and the
1000
* value readout will be redrawn; if "what" is REDRAW_ALL
1001
* then the entire widget will be redrawn.
1003
*--------------------------------------------------------------
1007
TkEventuallyRedrawScale(scalePtr, what)
1008
register TkScale *scalePtr; /* Information about widget. */
1009
int what; /* What to redraw: REDRAW_SLIDER
1012
if ((what == 0) || (scalePtr->tkwin == NULL)
1013
|| !Tk_IsMapped(scalePtr->tkwin)) {
1016
if ((scalePtr->flags & REDRAW_ALL) == 0) {
1017
Tcl_DoWhenIdle(TkpDisplayScale, (ClientData) scalePtr);
1019
scalePtr->flags |= what;
1023
*--------------------------------------------------------------
1025
* TkRoundToResolution --
1027
* Round a given floating-point value to the nearest multiple
1028
* of the scale's resolution.
1031
* The return value is the rounded result.
1036
*--------------------------------------------------------------
1040
TkRoundToResolution(scalePtr, value)
1041
TkScale *scalePtr; /* Information about scale widget. */
1042
double value; /* Value to round. */
1046
if (scalePtr->resolution <= 0) {
1049
new = scalePtr->resolution * floor(value/scalePtr->resolution);
1052
if (rem <= -scalePtr->resolution/2) {
1053
new -= scalePtr->resolution;
1056
if (rem >= scalePtr->resolution/2) {
1057
new += scalePtr->resolution;
1064
*----------------------------------------------------------------------
1068
* This procedure is invoked by Tcl whenever someone modifies a
1069
* variable associated with a scale widget.
1072
* NULL is always returned.
1075
* The value displayed in the scale will change to match the
1076
* variable's new value. If the variable has a bogus value then
1077
* it is reset to the value of the scale.
1079
*----------------------------------------------------------------------
1084
ScaleVarProc(clientData, interp, name1, name2, flags)
1085
ClientData clientData; /* Information about button. */
1086
Tcl_Interp *interp; /* Interpreter containing variable. */
1087
char *name1; /* Name of variable. */
1088
char *name2; /* Second part of variable name. */
1089
int flags; /* Information about what happened. */
1091
register TkScale *scalePtr = (TkScale *) clientData;
1092
char *stringValue, *end, *result;
1096
* If the variable is unset, then immediately recreate it unless
1097
* the whole interpreter is going away.
1100
if (flags & TCL_TRACE_UNSETS) {
1101
if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1102
Tcl_TraceVar(interp, scalePtr->varName,
1103
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1104
ScaleVarProc, clientData);
1105
scalePtr->flags |= NEVER_SET;
1106
TkpSetScaleValue(scalePtr, scalePtr->value, 1, 0);
1108
return (char *) NULL;
1112
* If we came here because we updated the variable (in TkpSetScaleValue),
1113
* then ignore the trace. Otherwise update the scale with the value
1117
if (scalePtr->flags & SETTING_VAR) {
1118
return (char *) NULL;
1121
stringValue = Tcl_GetVar(interp, scalePtr->varName, TCL_GLOBAL_ONLY);
1122
if (stringValue != NULL) {
1123
value = strtod(stringValue, &end);
1124
if ((end == stringValue) || (*end != 0)) {
1125
result = "can't assign non-numeric value to scale variable";
1127
scalePtr->value = TkRoundToResolution(scalePtr, value);
1131
* This code is a bit tricky because it sets the scale's value before
1132
* calling TkpSetScaleValue. This way, TkpSetScaleValue won't bother
1133
* to set the variable again or to invoke the -command. However, it
1134
* also won't redisplay the scale, so we have to ask for that
1138
TkpSetScaleValue(scalePtr, scalePtr->value, 1, 0);
1139
TkEventuallyRedrawScale(scalePtr, REDRAW_SLIDER);