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