OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [tags/] [start/] [insight/] [tk/] [generic/] [tkScale.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
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-1996 Sun Microsystems, Inc.
16
 *
17
 * See the file "license.terms" for information on usage and redistribution
18
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
19
 *
20
 * RCS: @(#) $Id: tkScale.c,v 1.1.1.1 2002-01-16 10:25:52 markom Exp $
21
 */
22
 
23
#include "tkPort.h"
24
#include "default.h"
25
#include "tkInt.h"
26
#include "tclMath.h"
27
#include "tkScale.h"
28
 
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),
35
        TK_CONFIG_MONO_ONLY},
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),
41
        TK_CONFIG_MONO_ONLY},
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,
45
        (char *) NULL, 0, 0},
46
    {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
47
        (char *) NULL, 0, 0},
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,
57
        (char *) NULL, 0, 0},
58
    {TK_CONFIG_FONT, "-font", "font", "Font",
59
        DEF_SCALE_FONT, Tk_Offset(TkScale, tkfont),
60
        0},
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),
66
        TK_CONFIG_MONO_ONLY},
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",
75
        "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),
102
        TK_CONFIG_NULL_OK},
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,
118
        (char *) NULL, 0, 0}
119
};
120
 
121
/*
122
 * Forward declarations for procedures defined later in this file:
123
 */
124
 
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,
129
                            int flags));
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,
134
                            XEvent *eventPtr));
135
static char *           ScaleVarProc _ANSI_ARGS_((ClientData clientData,
136
                            Tcl_Interp *interp, char *name1, char *name2,
137
                            int flags));
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));
142
 
143
/*
144
 * The structure below defines scale class behavior by means of procedures
145
 * that can be invoked from generic window code.
146
 */
147
 
148
static TkClassProcs scaleClass = {
149
    NULL,                       /* createProc. */
150
    ScaleWorldChanged,          /* geometryProc. */
151
    NULL                        /* modalProc. */
152
};
153
 
154
 
155
/*
156
 *--------------------------------------------------------------
157
 *
158
 * Tk_ScaleCmd --
159
 *
160
 *      This procedure is invoked to process the "scale" Tcl
161
 *      command.  See the user documentation for details on what
162
 *      it does.
163
 *
164
 * Results:
165
 *      A standard Tcl result.
166
 *
167
 * Side effects:
168
 *      See the user documentation.
169
 *
170
 *--------------------------------------------------------------
171
 */
172
 
173
int
174
Tk_ScaleCmd(clientData, interp, argc, argv)
175
    ClientData clientData;              /* Main window associated with
176
                                 * interpreter. */
177
    Tcl_Interp *interp;         /* Current interpreter. */
178
    int argc;                   /* Number of arguments. */
179
    char **argv;                /* Argument strings. */
180
{
181
    Tk_Window tkwin = (Tk_Window) clientData;
182
    register TkScale *scalePtr;
183
    Tk_Window new;
184
 
185
    if (argc < 2) {
186
        Tcl_AppendResult(interp, "wrong # args: should be \"",
187
                argv[0], " pathName ?options?\"", (char *) NULL);
188
        return TCL_ERROR;
189
    }
190
 
191
    new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
192
    if (new == NULL) {
193
        return TCL_ERROR;
194
    }
195
    scalePtr = TkpCreateScale(new);
196
 
197
    /*
198
     * Initialize fields that won't be initialized by ConfigureScale,
199
     * or which ConfigureScale expects to have reasonable values
200
     * (e.g. resource pointers).
201
     */
202
 
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;
211
    scalePtr->width = 0;
212
    scalePtr->length = 0;
213
    scalePtr->value = 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;
240
    scalePtr->inset = 0;
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;
254
 
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) {
261
        goto error;
262
    }
263
 
264
    interp->result = Tk_PathName(scalePtr->tkwin);
265
    return TCL_OK;
266
 
267
    error:
268
    Tk_DestroyWindow(scalePtr->tkwin);
269
    return TCL_ERROR;
270
}
271
 
272
/*
273
 *--------------------------------------------------------------
274
 *
275
 * ScaleWidgetCmd --
276
 *
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.
280
 *
281
 * Results:
282
 *      A standard Tcl result.
283
 *
284
 * Side effects:
285
 *      See the user documentation.
286
 *
287
 *--------------------------------------------------------------
288
 */
289
 
290
static int
291
ScaleWidgetCmd(clientData, interp, argc, argv)
292
    ClientData clientData;              /* Information about scale
293
                                         * widget. */
294
    Tcl_Interp *interp;                 /* Current interpreter. */
295
    int argc;                           /* Number of arguments. */
296
    char **argv;                        /* Argument strings. */
297
{
298
    register TkScale *scalePtr = (TkScale *) clientData;
299
    int result = TCL_OK;
300
    size_t length;
301
    int c;
302
 
303
    if (argc < 2) {
304
        Tcl_AppendResult(interp, "wrong # args: should be \"",
305
                argv[0], " option ?arg arg ...?\"", (char *) NULL);
306
        return TCL_ERROR;
307
    }
308
    Tcl_Preserve((ClientData) scalePtr);
309
    c = argv[1][0];
310
    length = strlen(argv[1]);
311
    if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
312
            && (length >= 2)) {
313
        if (argc != 3) {
314
            Tcl_AppendResult(interp, "wrong # args: should be \"",
315
                    argv[0], " cget option\"",
316
                    (char *) NULL);
317
            goto error;
318
        }
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)
322
            && (length >= 3)) {
323
        if (argc == 2) {
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);
329
        } else {
330
            result = ConfigureScale(interp, scalePtr, argc-2, argv+2,
331
                    TK_CONFIG_ARGV_ONLY);
332
        }
333
    } else if ((c == 'c') && (strncmp(argv[1], "coords", length) == 0)
334
            && (length >= 3)) {
335
        int x, y ;
336
        double value;
337
 
338
        if ((argc != 2) && (argc != 3)) {
339
            Tcl_AppendResult(interp, "wrong # args: should be \"",
340
                    argv[0], " coords ?value?\"", (char *) NULL);
341
            goto error;
342
        }
343
        if (argc == 3) {
344
            if (Tcl_GetDouble(interp, argv[2], &value) != TCL_OK) {
345
                goto error;
346
            }
347
        } else {
348
            value = scalePtr->value;
349
        }
350
        if (scalePtr->vertical) {
351
            x = scalePtr->vertTroughX + scalePtr->width/2
352
                    + scalePtr->borderWidth;
353
            y = TkpValueToPixel(scalePtr, value);
354
        } else {
355
            x = TkpValueToPixel(scalePtr, value);
356
            y = scalePtr->horizTroughY + scalePtr->width/2
357
                    + scalePtr->borderWidth;
358
        }
359
        sprintf(interp->result, "%d %d", x, y);
360
    } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
361
        double value;
362
        int x, y;
363
 
364
        if ((argc != 2) && (argc != 4)) {
365
            Tcl_AppendResult(interp, "wrong # args: should be \"",
366
                    argv[0], " get ?x y?\"", (char *) NULL);
367
            goto error;
368
        }
369
        if (argc == 2) {
370
            value = scalePtr->value;
371
        } else {
372
            if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
373
                    || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
374
                goto error;
375
            }
376
            value = TkpPixelToValue(scalePtr, x, y);
377
        }
378
        sprintf(interp->result, scalePtr->format, value);
379
    } else if ((c == 'i') && (strncmp(argv[1], "identify", length) == 0)) {
380
        int x, y, thing;
381
 
382
        if (argc != 4) {
383
            Tcl_AppendResult(interp, "wrong # args: should be \"",
384
                    argv[0], " identify x y\"", (char *) NULL);
385
            goto error;
386
        }
387
        if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
388
                || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
389
            goto error;
390
        }
391
        thing = TkpScaleElement(scalePtr, x,y);
392
        switch (thing) {
393
            case TROUGH1:       interp->result = "trough1";     break;
394
            case SLIDER:        interp->result = "slider";      break;
395
            case TROUGH2:       interp->result = "trough2";     break;
396
        }
397
    } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
398
        double value;
399
 
400
        if (argc != 3) {
401
            Tcl_AppendResult(interp, "wrong # args: should be \"",
402
                    argv[0], " set value\"", (char *) NULL);
403
            goto error;
404
        }
405
        if (Tcl_GetDouble(interp, argv[2], &value) != TCL_OK) {
406
            goto error;
407
        }
408
        if (scalePtr->state != tkDisabledUid) {
409
            TkpSetScaleValue(scalePtr, value, 1, 1);
410
        }
411
    } else {
412
        Tcl_AppendResult(interp, "bad option \"", argv[1],
413
                "\": must be cget, configure, coords, get, identify, or set",
414
                (char *) NULL);
415
        goto error;
416
    }
417
    Tcl_Release((ClientData) scalePtr);
418
    return result;
419
 
420
    error:
421
    Tcl_Release((ClientData) scalePtr);
422
    return TCL_ERROR;
423
}
424
 
425
/*
426
 *----------------------------------------------------------------------
427
 *
428
 * DestroyScale --
429
 *
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).
433
 *
434
 * Results:
435
 *      None.
436
 *
437
 * Side effects:
438
 *      Everything associated with the scale is freed up.
439
 *
440
 *----------------------------------------------------------------------
441
 */
442
 
443
static void
444
DestroyScale(memPtr)
445
    char *memPtr;       /* Info about scale widget. */
446
{
447
    register TkScale *scalePtr = (TkScale *) memPtr;
448
 
449
    /*
450
     * Free up all the stuff that requires special handling, then
451
     * let Tk_FreeOptions handle all the standard option-related
452
     * stuff.
453
     */
454
 
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);
459
    }
460
    if (scalePtr->troughGC != None) {
461
        Tk_FreeGC(scalePtr->display, scalePtr->troughGC);
462
    }
463
    if (scalePtr->copyGC != None) {
464
        Tk_FreeGC(scalePtr->display, scalePtr->copyGC);
465
    }
466
    if (scalePtr->textGC != None) {
467
        Tk_FreeGC(scalePtr->display, scalePtr->textGC);
468
    }
469
    Tk_FreeOptions(configSpecs, (char *) scalePtr, scalePtr->display, 0);
470
    TkpDestroyScale(scalePtr);
471
}
472
 
473
/*
474
 *----------------------------------------------------------------------
475
 *
476
 * ConfigureScale --
477
 *
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.
481
 *
482
 * Results:
483
 *      The return value is a standard Tcl result.  If TCL_ERROR is
484
 *      returned, then interp->result contains an error message.
485
 *
486
 * Side effects:
487
 *      Configuration information, such as colors, border width,
488
 *      etc. get set for scalePtr;  old resources get freed,
489
 *      if there were any.
490
 *
491
 *----------------------------------------------------------------------
492
 */
493
 
494
static int
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. */
502
{
503
    size_t length;
504
 
505
    /*
506
     * Eliminate any existing trace on a variable monitored by the scale.
507
     */
508
 
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);
513
    }
514
 
515
    if (Tk_ConfigureWidget(interp, scalePtr->tkwin, configSpecs,
516
            argc, argv, (char *) scalePtr, flags) != TCL_OK) {
517
        return TCL_ERROR;
518
    }
519
 
520
    /*
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.
524
     */
525
 
526
    if (scalePtr->varName != NULL) {
527
        char *stringValue, *end;
528
        double value;
529
 
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);
535
            }
536
        }
537
        Tcl_TraceVar(interp, scalePtr->varName,
538
                TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
539
                ScaleVarProc, (ClientData) scalePtr);
540
    }
541
 
542
    /*
543
     * Several options need special processing, such as parsing the
544
     * orientation and creating GCs.
545
     */
546
 
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;
552
    } else {
553
        Tcl_AppendResult(interp, "bad orientation \"", scalePtr->orientUid,
554
                "\": must be vertical or horizontal", (char *) NULL);
555
        return TCL_ERROR;
556
    }
557
 
558
    scalePtr->fromValue = TkRoundToResolution(scalePtr, scalePtr->fromValue);
559
    scalePtr->toValue = TkRoundToResolution(scalePtr, scalePtr->toValue);
560
    scalePtr->tickInterval = TkRoundToResolution(scalePtr,
561
            scalePtr->tickInterval);
562
 
563
    /*
564
     * Make sure that the tick interval has the right sign so that
565
     * addition moves from fromValue to toValue.
566
     */
567
 
568
    if ((scalePtr->tickInterval < 0)
569
            ^ ((scalePtr->toValue - scalePtr->fromValue) <  0)) {
570
        scalePtr->tickInterval = -scalePtr->tickInterval;
571
    }
572
 
573
    /*
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,
577
     * if any.
578
     */
579
 
580
    ComputeFormat(scalePtr);
581
    TkpSetScaleValue(scalePtr, scalePtr->value, 1, 0);
582
 
583
    if (scalePtr->label != NULL) {
584
        scalePtr->labelLength = strlen(scalePtr->label);
585
    } else {
586
        scalePtr->labelLength = 0;
587
    }
588
 
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;
595
        return TCL_ERROR;
596
    }
597
 
598
    Tk_SetBackgroundFromBorder(scalePtr->tkwin, scalePtr->bgBorder);
599
 
600
    if (scalePtr->highlightWidth < 0) {
601
        scalePtr->highlightWidth = 0;
602
    }
603
    scalePtr->inset = scalePtr->highlightWidth + scalePtr->borderWidth;
604
 
605
    ScaleWorldChanged((ClientData) scalePtr);
606
    return TCL_OK;
607
}
608
 
609
/*
610
 *---------------------------------------------------------------------------
611
 *
612
 * ScaleWorldChanged --
613
 *
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.
617
 *
618
 * Results:
619
 *      None.
620
 *
621
 * Side effects:
622
 *      Scale will be relayed out and redisplayed.
623
 *
624
 *---------------------------------------------------------------------------
625
 */
626
 
627
static void
628
ScaleWorldChanged(instanceData)
629
    ClientData instanceData;    /* Information about widget. */
630
{
631
    XGCValues gcValues;
632
    GC gc;
633
    TkScale *scalePtr;
634
 
635
    scalePtr = (TkScale *) instanceData;
636
 
637
    gcValues.foreground = scalePtr->troughColorPtr->pixel;
638
    gc = Tk_GetGCColor(scalePtr->tkwin, GCForeground, &gcValues,
639
                       scalePtr->troughColorPtr, NULL);
640
    if (scalePtr->troughGC != None) {
641
        Tk_FreeGC(scalePtr->display, scalePtr->troughGC);
642
    }
643
    scalePtr->troughGC = gc;
644
 
645
    gcValues.font = Tk_FontId(scalePtr->tkfont);
646
    gcValues.foreground = scalePtr->textColorPtr->pixel;
647
    gc = Tk_GetGCColor(scalePtr->tkwin, GCForeground | GCFont, &gcValues,
648
                       scalePtr->textColorPtr, NULL);
649
    if (scalePtr->textGC != None) {
650
        Tk_FreeGC(scalePtr->display, scalePtr->textGC);
651
    }
652
    scalePtr->textGC = gc;
653
 
654
    if (scalePtr->copyGC == None) {
655
        gcValues.graphics_exposures = False;
656
        scalePtr->copyGC = Tk_GetGC(scalePtr->tkwin, GCGraphicsExposures,
657
            &gcValues);
658
    }
659
    scalePtr->inset = scalePtr->highlightWidth + scalePtr->borderWidth;
660
 
661
    /*
662
     * Recompute display-related information, and let the geometry
663
     * manager know how much space is needed now.
664
     */
665
 
666
    ComputeScaleGeometry(scalePtr);
667
 
668
    TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
669
}
670
 
671
/*
672
 *----------------------------------------------------------------------
673
 *
674
 * ComputeFormat --
675
 *
676
 *      This procedure is invoked to recompute the "format" field
677
 *      of a scale's widget record, which determines how the value
678
 *      of the scale is converted to a string.
679
 *
680
 * Results:
681
 *      None.
682
 *
683
 * Side effects:
684
 *      The format field of scalePtr is modified.
685
 *
686
 *----------------------------------------------------------------------
687
 */
688
 
689
static void
690
ComputeFormat(scalePtr)
691
    TkScale *scalePtr;                  /* Information about scale widget. */
692
{
693
    double maxValue, x;
694
    int mostSigDigit, numDigits, leastSigDigit, afterDecimal;
695
    int eDigits, fDigits;
696
 
697
    /*
698
     * Compute the displacement from the decimal of the most significant
699
     * digit required for any number in the scale's range.
700
     */
701
 
702
    maxValue = fabs(scalePtr->fromValue);
703
    x = fabs(scalePtr->toValue);
704
    if (x > maxValue) {
705
        maxValue = x;
706
    }
707
    if (maxValue == 0) {
708
        maxValue = 1;
709
    }
710
    mostSigDigit = (int) floor(log10(maxValue));
711
 
712
    /*
713
     * If the number of significant digits wasn't specified explicitly,
714
     * compute it. It's the difference between the most significant
715
     * digit needed to represent any number on the scale and the
716
     * most significant digit of the smallest difference between
717
     * numbers on the scale.  In other words, display enough digits so
718
     * that at least one digit will be different between any two adjacent
719
     * positions of the scale.
720
     */
721
 
722
    numDigits = scalePtr->digits;
723
    if (numDigits <= 0) {
724
        if  (scalePtr->resolution > 0) {
725
            /*
726
             * A resolution was specified for the scale, so just use it.
727
             */
728
 
729
            leastSigDigit = (int) floor(log10(scalePtr->resolution));
730
        } else {
731
            /*
732
             * No resolution was specified, so compute the difference
733
             * in value between adjacent pixels and use it for the least
734
             * significant digit.
735
             */
736
 
737
            x = fabs(scalePtr->fromValue - scalePtr->toValue);
738
            if (scalePtr->length > 0) {
739
                x /= scalePtr->length;
740
            }
741
            if (x > 0){
742
                leastSigDigit = (int) floor(log10(x));
743
            } else {
744
                leastSigDigit = 0;
745
            }
746
        }
747
        numDigits = mostSigDigit - leastSigDigit + 1;
748
        if (numDigits < 1) {
749
            numDigits = 1;
750
        }
751
    }
752
 
753
    /*
754
     * Compute the number of characters required using "e" format and
755
     * "f" format, and then choose whichever one takes fewer characters.
756
     */
757
 
758
    eDigits = numDigits + 4;
759
    if (numDigits > 1) {
760
        eDigits++;                      /* Decimal point. */
761
    }
762
    afterDecimal = numDigits - mostSigDigit - 1;
763
    if (afterDecimal < 0) {
764
        afterDecimal = 0;
765
    }
766
    fDigits = (mostSigDigit >= 0) ? mostSigDigit + afterDecimal : afterDecimal;
767
    if (afterDecimal > 0) {
768
        fDigits++;                      /* Decimal point. */
769
    }
770
    if (mostSigDigit < 0) {
771
        fDigits++;                      /* Zero to left of decimal point. */
772
    }
773
    if (fDigits <= eDigits) {
774
        sprintf(scalePtr->format, "%%.%df", afterDecimal);
775
    } else {
776
        sprintf(scalePtr->format, "%%.%de", numDigits-1);
777
    }
778
}
779
 
780
/*
781
 *----------------------------------------------------------------------
782
 *
783
 * ComputeScaleGeometry --
784
 *
785
 *      This procedure is called to compute various geometrical
786
 *      information for a scale, such as where various things get
787
 *      displayed.  It's called when the window is reconfigured.
788
 *
789
 * Results:
790
 *      None.
791
 *
792
 * Side effects:
793
 *      Display-related numbers get changed in *scalePtr.  The
794
 *      geometry manager gets told about the window's preferred size.
795
 *
796
 *----------------------------------------------------------------------
797
 */
798
 
799
static void
800
ComputeScaleGeometry(scalePtr)
801
    register TkScale *scalePtr;         /* Information about widget. */
802
{
803
    char valueString[PRINT_CHARS];
804
    int tmp, valuePixels, x, y, extraSpace;
805
    Tk_FontMetrics fm;
806
 
807
    /*
808
     * Horizontal scales are simpler than vertical ones because
809
     * all sizes are the same (the height of a line of text);
810
     * handle them first and then quit.
811
     */
812
 
813
    Tk_GetFontMetrics(scalePtr->tkfont, &fm);
814
    if (!scalePtr->vertical) {
815
        y = scalePtr->inset;
816
        extraSpace = 0;
817
        if (scalePtr->labelLength != 0) {
818
            scalePtr->horizLabelY = y + SPACING;
819
            y += fm.linespace + SPACING;
820
            extraSpace = SPACING;
821
        }
822
        if (scalePtr->showValue) {
823
            scalePtr->horizValueY = y + SPACING;
824
            y += fm.linespace + SPACING;
825
            extraSpace = SPACING;
826
        } else {
827
            scalePtr->horizValueY = y;
828
        }
829
        y += extraSpace;
830
        scalePtr->horizTroughY = y;
831
        y += scalePtr->width + 2*scalePtr->borderWidth;
832
        if (scalePtr->tickInterval != 0) {
833
            scalePtr->horizTickY = y + SPACING;
834
            y += fm.linespace + 2*SPACING;
835
        }
836
        Tk_GeometryRequest(scalePtr->tkwin,
837
                scalePtr->length + 2*scalePtr->inset, y + scalePtr->inset);
838
        Tk_SetInternalBorder(scalePtr->tkwin, scalePtr->inset);
839
        return;
840
    }
841
 
842
    /*
843
     * Vertical scale:  compute the amount of space needed to display
844
     * the scales value by formatting strings for the two end points;
845
     * use whichever length is longer.
846
     */
847
 
848
    sprintf(valueString, scalePtr->format, scalePtr->fromValue);
849
    valuePixels = Tk_TextWidth(scalePtr->tkfont, valueString, -1);
850
 
851
    sprintf(valueString, scalePtr->format, scalePtr->toValue);
852
    tmp = Tk_TextWidth(scalePtr->tkfont, valueString, -1);
853
    if (valuePixels < tmp) {
854
        valuePixels = tmp;
855
    }
856
 
857
    /*
858
     * Assign x-locations to the elements of the scale, working from
859
     * left to right.
860
     */
861
 
862
    x = scalePtr->inset;
863
    if ((scalePtr->tickInterval != 0) && (scalePtr->showValue)) {
864
        scalePtr->vertTickRightX = x + SPACING + valuePixels;
865
        scalePtr->vertValueRightX = scalePtr->vertTickRightX + valuePixels
866
                + fm.ascent/2;
867
        x = scalePtr->vertValueRightX + SPACING;
868
    } else if (scalePtr->tickInterval != 0) {
869
        scalePtr->vertTickRightX = x + SPACING + valuePixels;
870
        scalePtr->vertValueRightX = scalePtr->vertTickRightX;
871
        x = scalePtr->vertTickRightX + SPACING;
872
    } else if (scalePtr->showValue) {
873
        scalePtr->vertTickRightX = x;
874
        scalePtr->vertValueRightX = x + SPACING + valuePixels;
875
        x = scalePtr->vertValueRightX + SPACING;
876
    } else {
877
        scalePtr->vertTickRightX = x;
878
        scalePtr->vertValueRightX = x;
879
    }
880
    scalePtr->vertTroughX = x;
881
    x += 2*scalePtr->borderWidth + scalePtr->width;
882
    if (scalePtr->labelLength == 0) {
883
        scalePtr->vertLabelX = 0;
884
    } else {
885
        scalePtr->vertLabelX = x + fm.ascent/2;
886
        x = scalePtr->vertLabelX + fm.ascent/2
887
                + Tk_TextWidth(scalePtr->tkfont, scalePtr->label,
888
                        scalePtr->labelLength);
889
    }
890
    Tk_GeometryRequest(scalePtr->tkwin, x + scalePtr->inset,
891
            scalePtr->length + 2*scalePtr->inset);
892
    Tk_SetInternalBorder(scalePtr->tkwin, scalePtr->inset);
893
}
894
 
895
/*
896
 *--------------------------------------------------------------
897
 *
898
 * ScaleEventProc --
899
 *
900
 *      This procedure is invoked by the Tk dispatcher for various
901
 *      events on scales.
902
 *
903
 * Results:
904
 *      None.
905
 *
906
 * Side effects:
907
 *      When the window gets deleted, internal structures get
908
 *      cleaned up.  When it gets exposed, it is redisplayed.
909
 *
910
 *--------------------------------------------------------------
911
 */
912
 
913
static void
914
ScaleEventProc(clientData, eventPtr)
915
    ClientData clientData;      /* Information about window. */
916
    XEvent *eventPtr;           /* Information about event. */
917
{
918
    TkScale *scalePtr = (TkScale *) clientData;
919
 
920
    if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
921
        TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
922
    } else if (eventPtr->type == DestroyNotify) {
923
        if (scalePtr->tkwin != NULL) {
924
            scalePtr->tkwin = NULL;
925
            Tcl_DeleteCommandFromToken(scalePtr->interp, scalePtr->widgetCmd);
926
        }
927
        if (scalePtr->flags & REDRAW_ALL) {
928
            Tcl_CancelIdleCall(TkpDisplayScale, (ClientData) scalePtr);
929
        }
930
        Tcl_EventuallyFree((ClientData) scalePtr, DestroyScale);
931
    } else if (eventPtr->type == ConfigureNotify) {
932
        ComputeScaleGeometry(scalePtr);
933
        TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
934
    } else if (eventPtr->type == FocusIn) {
935
        if (eventPtr->xfocus.detail != NotifyInferior) {
936
            scalePtr->flags |= GOT_FOCUS;
937
            if (scalePtr->highlightWidth > 0) {
938
                TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
939
            }
940
        }
941
    } else if (eventPtr->type == FocusOut) {
942
        if (eventPtr->xfocus.detail != NotifyInferior) {
943
            scalePtr->flags &= ~GOT_FOCUS;
944
            if (scalePtr->highlightWidth > 0) {
945
                TkEventuallyRedrawScale(scalePtr, REDRAW_ALL);
946
            }
947
        }
948
    }
949
}
950
 
951
/*
952
 *----------------------------------------------------------------------
953
 *
954
 * ScaleCmdDeletedProc --
955
 *
956
 *      This procedure is invoked when a widget command is deleted.  If
957
 *      the widget isn't already in the process of being destroyed,
958
 *      this command destroys it.
959
 *
960
 * Results:
961
 *      None.
962
 *
963
 * Side effects:
964
 *      The widget is destroyed.
965
 *
966
 *----------------------------------------------------------------------
967
 */
968
 
969
static void
970
ScaleCmdDeletedProc(clientData)
971
    ClientData clientData;      /* Pointer to widget record for widget. */
972
{
973
    TkScale *scalePtr = (TkScale *) clientData;
974
    Tk_Window tkwin = scalePtr->tkwin;
975
 
976
    /*
977
     * This procedure could be invoked either because the window was
978
     * destroyed and the command was then deleted (in which case tkwin
979
     * is NULL) or because the command was deleted, and then this procedure
980
     * destroys the widget.
981
     */
982
 
983
    if (tkwin != NULL) {
984
        scalePtr->tkwin = NULL;
985
        Tk_DestroyWindow(tkwin);
986
    }
987
}
988
 
989
/*
990
 *--------------------------------------------------------------
991
 *
992
 * TkEventuallyRedrawScale --
993
 *
994
 *      Arrange for part or all of a scale widget to redrawn at
995
 *      the next convenient time in the future.
996
 *
997
 * Results:
998
 *      None.
999
 *
1000
 * Side effects:
1001
 *      If "what" is REDRAW_SLIDER then just the slider and the
1002
 *      value readout will be redrawn;  if "what" is REDRAW_ALL
1003
 *      then the entire widget will be redrawn.
1004
 *
1005
 *--------------------------------------------------------------
1006
 */
1007
 
1008
void
1009
TkEventuallyRedrawScale(scalePtr, what)
1010
    register TkScale *scalePtr; /* Information about widget. */
1011
    int what;                   /* What to redraw:  REDRAW_SLIDER
1012
                                 * or REDRAW_ALL. */
1013
{
1014
    if ((what == 0) || (scalePtr->tkwin == NULL)
1015
            || !Tk_IsMapped(scalePtr->tkwin)) {
1016
        return;
1017
    }
1018
    if ((scalePtr->flags & REDRAW_ALL) == 0) {
1019
        Tcl_DoWhenIdle(TkpDisplayScale, (ClientData) scalePtr);
1020
    }
1021
    scalePtr->flags |= what;
1022
}
1023
 
1024
/*
1025
 *--------------------------------------------------------------
1026
 *
1027
 * TkRoundToResolution --
1028
 *
1029
 *      Round a given floating-point value to the nearest multiple
1030
 *      of the scale's resolution.
1031
 *
1032
 * Results:
1033
 *      The return value is the rounded result.
1034
 *
1035
 * Side effects:
1036
 *      None.
1037
 *
1038
 *--------------------------------------------------------------
1039
 */
1040
 
1041
double
1042
TkRoundToResolution(scalePtr, value)
1043
    TkScale *scalePtr;          /* Information about scale widget. */
1044
    double value;               /* Value to round. */
1045
{
1046
    double rem, new;
1047
 
1048
    if (scalePtr->resolution <= 0) {
1049
        return value;
1050
    }
1051
    new = scalePtr->resolution * floor(value/scalePtr->resolution);
1052
    rem = value - new;
1053
    if (rem < 0) {
1054
        if (rem <= -scalePtr->resolution/2) {
1055
            new -= scalePtr->resolution;
1056
        }
1057
    } else {
1058
        if (rem >= scalePtr->resolution/2) {
1059
            new += scalePtr->resolution;
1060
        }
1061
    }
1062
    return new;
1063
}
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
 
1084
    /* ARGSUSED */
1085
static char *
1086
ScaleVarProc(clientData, interp, name1, name2, flags)
1087
    ClientData clientData;      /* Information about button. */
1088
    Tcl_Interp *interp;         /* Interpreter containing variable. */
1089
    char *name1;                /* Name of variable. */
1090
    char *name2;                /* Second part of variable name. */
1091
    int flags;                  /* Information about what happened. */
1092
{
1093
    register TkScale *scalePtr = (TkScale *) clientData;
1094
    char *stringValue, *end, *result;
1095
    double value;
1096
 
1097
    /*
1098
     * If the variable is unset, then immediately recreate it unless
1099
     * the whole interpreter is going away.
1100
     */
1101
 
1102
    if (flags & TCL_TRACE_UNSETS) {
1103
        if ((flags & TCL_TRACE_DESTROYED) && !(flags & TCL_INTERP_DESTROYED)) {
1104
            Tcl_TraceVar(interp, scalePtr->varName,
1105
                    TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
1106
                    ScaleVarProc, clientData);
1107
            scalePtr->flags |= NEVER_SET;
1108
            TkpSetScaleValue(scalePtr, scalePtr->value, 1, 0);
1109
        }
1110
        return (char *) NULL;
1111
    }
1112
 
1113
    /*
1114
     * If we came here because we updated the variable (in TkpSetScaleValue),
1115
     * then ignore the trace.  Otherwise update the scale with the value
1116
     * of the variable.
1117
     */
1118
 
1119
    if (scalePtr->flags & SETTING_VAR) {
1120
        return (char *) NULL;
1121
    }
1122
    result = NULL;
1123
    stringValue = Tcl_GetVar(interp, scalePtr->varName, TCL_GLOBAL_ONLY);
1124
    if (stringValue != NULL) {
1125
        value = strtod(stringValue, &end);
1126
        if ((end == stringValue) || (*end != 0)) {
1127
            result = "can't assign non-numeric value to scale variable";
1128
        } else {
1129
            scalePtr->value = TkRoundToResolution(scalePtr, value);
1130
        }
1131
 
1132
        /*
1133
         * This code is a bit tricky because it sets the scale's value before
1134
         * calling TkpSetScaleValue.  This way, TkpSetScaleValue won't bother
1135
         * to set the variable again or to invoke the -command.  However, it
1136
         * also won't redisplay the scale, so we have to ask for that
1137
         * explicitly.
1138
         */
1139
 
1140
        TkpSetScaleValue(scalePtr, scalePtr->value, 1, 0);
1141
        TkEventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
1142
    }
1143
 
1144
    return result;
1145
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.