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