]> git.zerfleddert.de Git - micropolis/blob - src/sim/w_inter.c
XINCLUDE: use /usr/X11R6/include everywhere
[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
299 static void NotifyInterval(register Interval *intervalPtr);
300 \f
301 /*
302 *--------------------------------------------------------------
303 *
304 * Tk_IntervalCmd --
305 *
306 * This procedure is invoked to process the "interval" Tcl
307 * command. See the user documentation for details on what
308 * it does.
309 *
310 * Results:
311 * A standard Tcl result.
312 *
313 * Side effects:
314 * See the user documentation.
315 *
316 *--------------------------------------------------------------
317 */
318
319 int
320 Tk_IntervalCmd(clientData, interp, argc, argv)
321 ClientData clientData; /* Main window associated with
322 * interpreter. */
323 Tcl_Interp *interp; /* Current interpreter. */
324 int argc; /* Number of arguments. */
325 char **argv; /* Argument strings. */
326 {
327 Tk_Window tkwin = (Tk_Window) clientData;
328 register Interval *intervalPtr;
329 Tk_Window new;
330
331 if (argc < 2) {
332 Tcl_AppendResult(interp, "wrong # args: should be \"",
333 argv[0], " pathName ?options?\"", (char *) NULL);
334 return TCL_ERROR;
335 }
336
337 new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
338 if (new == NULL) {
339 return TCL_ERROR;
340 }
341
342 /*
343 * Initialize fields that won't be initialized by ConfigureInterval,
344 * or which ConfigureInterval expects to have reasonable values
345 * (e.g. resource pointers).
346 */
347
348 intervalPtr = (Interval *) ckalloc(sizeof(Interval));
349 intervalPtr->tkwin = new;
350 intervalPtr->interp = interp;
351 intervalPtr->minValue = 0;
352 intervalPtr->maxValue = 0;
353 intervalPtr->command = NULL;
354 intervalPtr->label = NULL;
355 intervalPtr->state = tkNormalUid;
356 intervalPtr->bgBorder = NULL;
357 intervalPtr->sliderBorder = NULL;
358 intervalPtr->activeBorder = NULL;
359 intervalPtr->fontPtr = NULL;
360 intervalPtr->textColorPtr = NULL;
361 intervalPtr->textGC = None;
362 intervalPtr->cursor = None;
363 intervalPtr->flags = 0;
364
365 Tk_SetClass(intervalPtr->tkwin, "Interval");
366 Tk_CreateEventHandler(intervalPtr->tkwin, ExposureMask|StructureNotifyMask,
367 IntervalEventProc, (ClientData) intervalPtr);
368 Tk_CreateEventHandler(intervalPtr->tkwin, EnterWindowMask|LeaveWindowMask
369 |PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
370 IntervalMouseProc, (ClientData) intervalPtr);
371 Tcl_CreateCommand(interp, Tk_PathName(intervalPtr->tkwin), IntervalWidgetCmd,
372 (ClientData) intervalPtr, (void (*)()) NULL);
373 if (ConfigureInterval(interp, intervalPtr, argc-2, argv+2, 0) != TCL_OK) {
374 goto error;
375 }
376
377 interp->result = Tk_PathName(intervalPtr->tkwin);
378 return TCL_OK;
379
380 error:
381 Tk_DestroyWindow(intervalPtr->tkwin);
382 return TCL_ERROR;
383 }
384 \f
385 /*
386 *--------------------------------------------------------------
387 *
388 * IntervalWidgetCmd --
389 *
390 * This procedure is invoked to process the Tcl command
391 * that corresponds to a widget managed by this module.
392 * See the user documentation for details on what it does.
393 *
394 * Results:
395 * A standard Tcl result.
396 *
397 * Side effects:
398 * See the user documentation.
399 *
400 *--------------------------------------------------------------
401 */
402
403 static int
404 IntervalWidgetCmd(clientData, interp, argc, argv)
405 ClientData clientData; /* Information about interval
406 * widget. */
407 Tcl_Interp *interp; /* Current interpreter. */
408 int argc; /* Number of arguments. */
409 char **argv; /* Argument strings. */
410 {
411 register Interval *intervalPtr = (Interval *) clientData;
412 int result = TCL_OK;
413 int length;
414 char c;
415
416 if (argc < 2) {
417 Tcl_AppendResult(interp, "wrong # args: should be \"",
418 argv[0], " option ?arg arg ...?\"", (char *) NULL);
419 return TCL_ERROR;
420 }
421 Tk_Preserve((ClientData) intervalPtr);
422 c = argv[1][0];
423 length = strlen(argv[1]);
424 if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
425 if (argc == 2) {
426 result = Tk_ConfigureInfo(interp, intervalPtr->tkwin, configSpecs,
427 (char *) intervalPtr, (char *) NULL, 0);
428 } else if (argc == 3) {
429 result = Tk_ConfigureInfo(interp, intervalPtr->tkwin, configSpecs,
430 (char *) intervalPtr, argv[2], 0);
431 } else {
432 result = ConfigureInterval(interp, intervalPtr, argc-2, argv+2,
433 TK_CONFIG_ARGV_ONLY);
434 }
435 } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
436 if (argc != 2) {
437 Tcl_AppendResult(interp, "wrong # args: should be \"",
438 argv[0], " get\"", (char *) NULL);
439 goto error;
440 }
441 sprintf(interp->result, "%d %d", intervalPtr->minValue, intervalPtr->maxValue);
442 } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
443 int minValue, maxValue;
444
445 if (argc != 4) {
446 Tcl_AppendResult(interp, "wrong # args: should be \"",
447 argv[0], " set minValue maxValue\"", (char *) NULL);
448 goto error;
449 }
450 if (Tcl_GetInt(interp, argv[2], &minValue) != TCL_OK) {
451 goto error;
452 }
453 if (Tcl_GetInt(interp, argv[3], &maxValue) != TCL_OK) {
454 goto error;
455 }
456 if (minValue > maxValue) {
457 int temp = minValue;
458 minValue = maxValue; maxValue = temp;
459 }
460 if (intervalPtr->state == tkNormalUid) {
461 if ((minValue < intervalPtr->fromValue)
462 ^ (intervalPtr->toValue < intervalPtr->fromValue)) {
463 minValue = intervalPtr->fromValue;
464 }
465 if ((minValue > intervalPtr->toValue)
466 ^ (intervalPtr->toValue < intervalPtr->fromValue)) {
467 minValue = intervalPtr->toValue;
468 }
469 if ((maxValue < intervalPtr->fromValue)
470 ^ (intervalPtr->toValue < intervalPtr->fromValue)) {
471 maxValue = intervalPtr->fromValue;
472 }
473 if ((maxValue > intervalPtr->toValue)
474 ^ (intervalPtr->toValue < intervalPtr->fromValue)) {
475 maxValue = intervalPtr->toValue;
476 }
477 SetInterval(intervalPtr, minValue, maxValue, 1);
478 }
479 } else if ((c == 'r') && (strncmp(argv[1], "reset", length) == 0)) {
480 if (argc != 2) {
481 Tcl_AppendResult(interp, "wrong # args: should be \"",
482 argv[0], " reset\"", (char *) NULL);
483 goto error;
484 }
485 SetInterval(intervalPtr,
486 intervalPtr->fromValue, intervalPtr->toValue, 0);
487 } else {
488 Tcl_AppendResult(interp, "bad option \"", argv[1],
489 "\": must be configure, get, or set", (char *) NULL);
490 goto error;
491 }
492 Tk_Release((ClientData) intervalPtr);
493 return result;
494
495 error:
496 Tk_Release((ClientData) intervalPtr);
497 return TCL_ERROR;
498 }
499 \f
500 /*
501 *----------------------------------------------------------------------
502 *
503 * DestroyInterval --
504 *
505 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
506 * to clean up the internal structure of a button at a safe time
507 * (when no-one is using it anymore).
508 *
509 * Results:
510 * None.
511 *
512 * Side effects:
513 * Everything associated with the interval is freed up.
514 *
515 *----------------------------------------------------------------------
516 */
517
518 static void
519 DestroyInterval(clientData)
520 ClientData clientData; /* Info about interval widget. */
521 {
522 register Interval *intervalPtr = (Interval *) clientData;
523
524 if (intervalPtr->command != NULL) {
525 ckfree(intervalPtr->command);
526 }
527 if (intervalPtr->label != NULL) {
528 ckfree(intervalPtr->label);
529 }
530 if (intervalPtr->bgBorder != NULL) {
531 Tk_Free3DBorder(intervalPtr->bgBorder);
532 }
533 if (intervalPtr->sliderBorder != NULL) {
534 Tk_Free3DBorder(intervalPtr->sliderBorder);
535 }
536 if (intervalPtr->activeBorder != NULL) {
537 Tk_Free3DBorder(intervalPtr->activeBorder);
538 }
539 if (intervalPtr->fontPtr != NULL) {
540 Tk_FreeFontStruct(intervalPtr->fontPtr);
541 }
542 if (intervalPtr->textColorPtr != NULL) {
543 Tk_FreeColor(intervalPtr->textColorPtr);
544 }
545 if (intervalPtr->textGC != None) {
546 Tk_FreeGC(intervalPtr->textGC);
547 }
548 if (intervalPtr->cursor != None) {
549 Tk_FreeCursor(intervalPtr->cursor);
550 }
551 ckfree((char *) intervalPtr);
552 }
553 \f
554 /*
555 *----------------------------------------------------------------------
556 *
557 * ConfigureInterval --
558 *
559 * This procedure is called to process an argv/argc list, plus
560 * the Tk option database, in order to configure (or
561 * reconfigure) a interval widget.
562 *
563 * Results:
564 * The return value is a standard Tcl result. If TCL_ERROR is
565 * returned, then interp->result contains an error message.
566 *
567 * Side effects:
568 * Configuration information, such as colors, border width,
569 * etc. get set for intervalPtr; old resources get freed,
570 * if there were any.
571 *
572 *----------------------------------------------------------------------
573 */
574
575 static int
576 ConfigureInterval(interp, intervalPtr, argc, argv, flags)
577 Tcl_Interp *interp; /* Used for error reporting. */
578 register Interval *intervalPtr; /* Information about widget; may or may
579 * not already have values for some fields. */
580 int argc; /* Number of valid entries in argv. */
581 char **argv; /* Arguments. */
582 int flags; /* Flags to pass to Tk_ConfigureWidget. */
583 {
584 XGCValues gcValues;
585 GC newGC;
586 int length;
587
588 if (Tk_ConfigureWidget(interp, intervalPtr->tkwin, configSpecs,
589 argc, argv, (char *) intervalPtr, flags) != TCL_OK) {
590 return TCL_ERROR;
591 }
592
593 /*
594 * A few options need special processing, such as parsing the
595 * orientation or setting the background from a 3-D border.
596 */
597
598 length = strlen(intervalPtr->orientUid);
599 if (strncmp(intervalPtr->orientUid, "vertical", length) == 0) {
600 intervalPtr->vertical = 1;
601 } else if (strncmp(intervalPtr->orientUid, "horizontal", length) == 0) {
602 intervalPtr->vertical = 0;
603 } else {
604 Tcl_AppendResult(interp, "bad orientation \"", intervalPtr->orientUid,
605 "\": must be vertical or horizontal", (char *) NULL);
606 return TCL_ERROR;
607 }
608
609 if ((intervalPtr->state != tkNormalUid)
610 && (intervalPtr->state != tkDisabledUid)) {
611 Tcl_AppendResult(interp, "bad state value \"", intervalPtr->state,
612 "\": must be normal or disabled", (char *) NULL);
613 intervalPtr->state = tkNormalUid;
614 return TCL_ERROR;
615 }
616
617 /*
618 * Make sure that the tick interval has the right sign so that
619 * addition moves from fromValue to toValue.
620 */
621
622 if ((intervalPtr->tickInterval < 0)
623 ^ ((intervalPtr->toValue - intervalPtr->fromValue) < 0)) {
624 intervalPtr->tickInterval = -intervalPtr->tickInterval;
625 }
626
627 /*
628 * Set the interval mix and max values to themselves; all this does is
629 * to make sure that the interval's value is within the new acceptable
630 * range for the interval.
631 */
632
633 SetInterval(intervalPtr, intervalPtr->minValue, intervalPtr->maxValue, 0);
634
635 if (intervalPtr->command != NULL) {
636 intervalPtr->commandLength = strlen(intervalPtr->command);
637 } else {
638 intervalPtr->commandLength = 0;
639 }
640
641 if (intervalPtr->label != NULL) {
642 intervalPtr->labelLength = strlen(intervalPtr->label);
643 } else {
644 intervalPtr->labelLength = 0;
645 }
646
647 Tk_SetBackgroundFromBorder(intervalPtr->tkwin, intervalPtr->bgBorder);
648
649 gcValues.font = intervalPtr->fontPtr->fid;
650 gcValues.foreground = intervalPtr->textColorPtr->pixel;
651 newGC = Tk_GetGC(intervalPtr->tkwin, GCForeground|GCFont, &gcValues);
652 if (intervalPtr->textGC != None) {
653 Tk_FreeGC(intervalPtr->textGC);
654 }
655 intervalPtr->textGC = newGC;
656
657 if (intervalPtr->relief != TK_RELIEF_FLAT) {
658 intervalPtr->offset = intervalPtr->borderWidth;
659 } else {
660 intervalPtr->offset = 0;
661 }
662
663 /*
664 * Recompute display-related information, and let the geometry
665 * manager know how much space is needed now.
666 */
667
668 ComputeIntervalGeometry(intervalPtr);
669
670 EventuallyRedrawInterval(intervalPtr, REDRAW_ALL);
671 return TCL_OK;
672 }
673 \f
674 /*
675 *----------------------------------------------------------------------
676 *
677 * ComputeIntervalGeometry --
678 *
679 * This procedure is called to compute various geometrical
680 * information for a interval, such as where various things get
681 * displayed. It's called when the window is reconfigured.
682 *
683 * Results:
684 * None.
685 *
686 * Side effects:
687 * Display-related numbers get changed in *scrollPtr. The
688 * geometry manager gets told about the window's preferred size.
689 *
690 *----------------------------------------------------------------------
691 */
692
693 static void
694 ComputeIntervalGeometry(intervalPtr)
695 register Interval *intervalPtr; /* Information about widget. */
696 {
697 XCharStruct bbox;
698 char valueString[30];
699 int dummy, lineHeight;
700
701 /*
702 * Horizontal intervals are simpler than vertical ones because
703 * all sizes are the same (the height of a line of text);
704 * handle them first and then quit.
705 */
706
707 if (!intervalPtr->vertical) {
708 lineHeight = intervalPtr->fontPtr->ascent + intervalPtr->fontPtr->descent;
709 if (intervalPtr->tickInterval != 0) {
710 intervalPtr->tickPixels = lineHeight;
711 } else {
712 intervalPtr->tickPixels = 0;
713 }
714 if (intervalPtr->showValue) {
715 intervalPtr->valuePixels = lineHeight + SPACING;
716 } else {
717 intervalPtr->valuePixels = 0;
718 }
719 if (intervalPtr->labelLength != 0) {
720 intervalPtr->labelPixels = lineHeight;
721 } else {
722 intervalPtr->labelPixels = 0;
723 }
724
725 Tk_GeometryRequest(intervalPtr->tkwin,
726 intervalPtr->length + 2*intervalPtr->offset,
727 intervalPtr->tickPixels + intervalPtr->valuePixels
728 + intervalPtr->width + 2*intervalPtr->borderWidth
729 + intervalPtr->labelPixels + 2*intervalPtr->offset);
730 Tk_SetInternalBorder(intervalPtr->tkwin, intervalPtr->borderWidth);
731 return;
732 }
733
734 /*
735 * Vertical interval: compute the amount of space needed for tick marks
736 * and current value by formatting strings for the two end points;
737 * use whichever length is longer.
738 */
739
740 sprintf(valueString, "%d", intervalPtr->fromValue);
741 XTextExtents(intervalPtr->fontPtr, valueString, strlen(valueString),
742 &dummy, &dummy, &dummy, &bbox);
743 intervalPtr->tickPixels = bbox.rbearing + bbox.lbearing;
744 sprintf(valueString, "%d", intervalPtr->toValue);
745 XTextExtents(intervalPtr->fontPtr, valueString, strlen(valueString),
746 &dummy, &dummy, &dummy, &bbox);
747 if (intervalPtr->tickPixels < bbox.rbearing + bbox.lbearing) {
748 intervalPtr->tickPixels = bbox.rbearing + bbox.lbearing;
749 }
750
751 /*
752 * Pad the value with a bit of extra space for prettier printing.
753 */
754
755 intervalPtr->tickPixels += intervalPtr->fontPtr->ascent/2;
756 intervalPtr->valuePixels = intervalPtr->tickPixels;
757 if (intervalPtr->tickInterval == 0) {
758 intervalPtr->tickPixels = 0;
759 }
760 if (!intervalPtr->showValue) {
761 intervalPtr->valuePixels = 0;
762 }
763
764 if (intervalPtr->labelLength == 0) {
765 intervalPtr->labelPixels = 0;
766 } else {
767 XTextExtents(intervalPtr->fontPtr, intervalPtr->label,
768 intervalPtr->labelLength, &dummy, &dummy, &dummy, &bbox);
769 intervalPtr->labelPixels = bbox.rbearing + bbox.lbearing
770 + intervalPtr->fontPtr->ascent;
771 }
772 Tk_GeometryRequest(intervalPtr->tkwin, 2*intervalPtr->borderWidth
773 + intervalPtr->tickPixels + intervalPtr->valuePixels + SPACING
774 + intervalPtr->width + intervalPtr->labelPixels,
775 intervalPtr->length);
776 Tk_SetInternalBorder(intervalPtr->tkwin, intervalPtr->borderWidth);
777 }
778 \f
779 /*
780 *--------------------------------------------------------------
781 *
782 * DisplayVerticalInterval --
783 *
784 * This procedure redraws the contents of a vertical interval
785 * window. It is invoked as a do-when-idle handler, so it only
786 * runs when there's nothing else for the application to do.
787 *
788 * Results:
789 * None.
790 *
791 * Side effects:
792 * Information appears on the screen.
793 *
794 *--------------------------------------------------------------
795 */
796
797 static void
798 DisplayVerticalInterval(clientData)
799 ClientData clientData; /* Information about widget. */
800 {
801 register Interval *intervalPtr = (Interval *) clientData;
802 register Tk_Window tkwin = intervalPtr->tkwin;
803 int tickRightEdge, valueRightEdge, labelLeftEdge, intervalLeftEdge;
804 int totalPixels, x, width, height, tickValue, min, max;
805 int relief;
806 Tk_3DBorder sliderBorder;
807
808 if ((intervalPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
809 goto done;
810 }
811
812 /*
813 * Scanning from left to right across the window, the window
814 * will contain four columns: ticks, value, interval, and label.
815 * Compute the x-coordinate for each of the columns.
816 */
817
818 totalPixels = intervalPtr->tickPixels + intervalPtr->valuePixels
819 + 2*intervalPtr->borderWidth + intervalPtr->width
820 + 2*SPACING + intervalPtr->labelPixels;
821 tickRightEdge = (Tk_Width(tkwin) - totalPixels)/2 + intervalPtr->tickPixels;
822 valueRightEdge = tickRightEdge + intervalPtr->valuePixels;
823 intervalLeftEdge = valueRightEdge + SPACING;
824 labelLeftEdge = intervalLeftEdge + 2*intervalPtr->borderWidth
825 + intervalPtr->width + intervalPtr->fontPtr->ascent/2;
826
827 /*
828 * Display the information from left to right across the window.
829 */
830
831 if (intervalPtr->flags & REDRAW_OTHER) {
832 XClearWindow(Tk_Display(tkwin), Tk_WindowId(tkwin));
833
834 /*
835 * Display the tick marks.
836 */
837
838 if (intervalPtr->tickPixels != 0) {
839 for (tickValue = intervalPtr->fromValue; ;
840 tickValue += intervalPtr->tickInterval) {
841 if (intervalPtr->toValue > intervalPtr->fromValue) {
842 if (tickValue > intervalPtr->toValue) {
843 break;
844 }
845 } else {
846 if (tickValue < intervalPtr->toValue) {
847 break;
848 }
849 }
850 DisplayVerticalValue(intervalPtr, tickValue, tickRightEdge);
851 }
852 }
853 }
854
855 /*
856 * Display the values, if they are desired. If not redisplaying the
857 * entire window, clear the area of the value to get rid of the
858 * old value displayed there.
859 */
860
861 if (intervalPtr->showValue) {
862 if (!(intervalPtr->flags & REDRAW_OTHER)) {
863 XClearArea(Tk_Display(tkwin), Tk_WindowId(tkwin),
864 valueRightEdge-intervalPtr->valuePixels, intervalPtr->offset,
865 intervalPtr->valuePixels,
866 Tk_Height(tkwin) - 2*intervalPtr->offset, False);
867 }
868 DisplayVerticalValue(intervalPtr, intervalPtr->minValue, valueRightEdge);
869 DisplayVerticalValue(intervalPtr, intervalPtr->maxValue, valueRightEdge);
870 }
871
872 /*
873 * Display the interval and the slider. If not redisplaying the
874 * entire window, must clear the trench area to erase the old
875 * slider, but don't need to redraw the border.
876 */
877
878 if (intervalPtr->flags & REDRAW_OTHER) {
879 Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
880 intervalPtr->bgBorder, intervalLeftEdge, intervalPtr->offset,
881 intervalPtr->width + 2*intervalPtr->borderWidth,
882 Tk_Height(tkwin) - 2*intervalPtr->offset, intervalPtr->borderWidth,
883 TK_RELIEF_SUNKEN);
884 } else {
885 XClearArea(Tk_Display(tkwin), Tk_WindowId(tkwin),
886 intervalLeftEdge + intervalPtr->borderWidth,
887 intervalPtr->offset + intervalPtr->borderWidth,
888 intervalPtr->width,
889 Tk_Height(tkwin) - 2*intervalPtr->offset
890 - 2*intervalPtr->borderWidth, False);
891 }
892 if (intervalPtr->flags & ACTIVE) {
893 sliderBorder = intervalPtr->activeBorder;
894 } else {
895 sliderBorder = intervalPtr->sliderBorder;
896 }
897 width = intervalPtr->width;
898
899 min = ValueToPixel(intervalPtr, intervalPtr->minValue);
900 max = ValueToPixel(intervalPtr, intervalPtr->maxValue);
901
902 height = (max - min) + (2 * intervalPtr->borderWidth);
903
904 x = intervalLeftEdge + intervalPtr->borderWidth;
905
906 relief = (intervalPtr->flags & BUTTON_PRESSED) ? TK_RELIEF_SUNKEN
907 : TK_RELIEF_RAISED;
908 Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin), sliderBorder,
909 x, min, width, height, intervalPtr->borderWidth, relief);
910
911 /*
912 * Draw the label to the right of the interval.
913 */
914
915 if ((intervalPtr->flags & REDRAW_OTHER) && (intervalPtr->labelPixels != 0)) {
916 XDrawString(Tk_Display(intervalPtr->tkwin), Tk_WindowId(intervalPtr->tkwin),
917 intervalPtr->textGC, labelLeftEdge,
918 intervalPtr->offset + (3*intervalPtr->fontPtr->ascent)/2,
919 intervalPtr->label, intervalPtr->labelLength);
920 }
921
922 /*
923 * Draw the window border.
924 */
925
926 if ((intervalPtr->flags & REDRAW_OTHER)
927 && (intervalPtr->relief != TK_RELIEF_FLAT)) {
928 Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
929 intervalPtr->bgBorder, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
930 intervalPtr->borderWidth, intervalPtr->relief);
931 }
932
933 done:
934 intervalPtr->flags &= ~REDRAW_ALL;
935 }
936 \f
937 /*
938 *----------------------------------------------------------------------
939 *
940 * DisplayVerticalValue --
941 *
942 * This procedure is called to display values (interval readings)
943 * for vertically-oriented intervals.
944 *
945 * Results:
946 * None.
947 *
948 * Side effects:
949 * The numerical value corresponding to value is displayed with
950 * its right edge at "rightEdge", and at a vertical position in
951 * the interval that corresponds to "value".
952 *
953 *----------------------------------------------------------------------
954 */
955
956 static void
957 DisplayVerticalValue(intervalPtr, value, rightEdge)
958 register Interval *intervalPtr; /* Information about widget in which to
959 * display value. */
960 int value; /* Y-coordinate of number to display,
961 * specified in application coords, not
962 * in pixels (we'll compute pixels). */
963 int rightEdge; /* X-coordinate of right edge of text,
964 * specified in pixels. */
965 {
966 register Tk_Window tkwin = intervalPtr->tkwin;
967 int y, dummy, length;
968 char valueString[30];
969 XCharStruct bbox;
970
971 y = ValueToPixel(intervalPtr, value) + intervalPtr->fontPtr->ascent/2;
972 sprintf(valueString, "%d", value);
973 length = strlen(valueString);
974 XTextExtents(intervalPtr->fontPtr, valueString, length,
975 &dummy, &dummy, &dummy, &bbox);
976
977 /*
978 * Adjust the y-coordinate if necessary to keep the text entirely
979 * inside the window.
980 */
981
982 if ((y - bbox.ascent) < intervalPtr->offset) {
983 y = intervalPtr->offset + bbox.ascent;
984 }
985 if ((y + bbox.descent) > (Tk_Height(tkwin) - intervalPtr->offset)) {
986 y = Tk_Height(tkwin) - intervalPtr->offset - bbox.descent;
987 }
988 XDrawString(Tk_Display(tkwin), Tk_WindowId(tkwin),
989 intervalPtr->textGC, rightEdge - bbox.rbearing,
990 y, valueString, length);
991 }
992 \f
993 /*
994 *--------------------------------------------------------------
995 *
996 * DisplayHorizontalInterval --
997 *
998 * This procedure redraws the contents of a horizontal interval
999 * window. It is invoked as a do-when-idle handler, so it only
1000 * runs when there's nothing else for the application to do.
1001 *
1002 * Results:
1003 * None.
1004 *
1005 * Side effects:
1006 * Information appears on the screen.
1007 *
1008 *--------------------------------------------------------------
1009 */
1010
1011 static void
1012 DisplayHorizontalInterval(clientData)
1013 ClientData clientData; /* Information about widget. */
1014 {
1015 register Interval *intervalPtr = (Interval *) clientData;
1016 register Tk_Window tkwin = intervalPtr->tkwin;
1017 int tickBottom, valueBottom, labelBottom, intervalBottom;
1018 int totalPixels, y, width, height, tickValue, min, max;
1019 int relief;
1020 Tk_3DBorder sliderBorder;
1021
1022 if ((intervalPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
1023 goto done;
1024 }
1025
1026 /*
1027 * Scanning from bottom to top across the window, the window
1028 * will contain four rows: ticks, value, interval, and label.
1029 * Compute the y-coordinate for each of the rows.
1030 */
1031
1032 totalPixels = intervalPtr->tickPixels + intervalPtr->valuePixels
1033 + 2*intervalPtr->borderWidth + intervalPtr->width
1034 + intervalPtr->labelPixels;
1035 tickBottom = (Tk_Height(tkwin) + totalPixels)/2 - 1;
1036 valueBottom = tickBottom - intervalPtr->tickPixels;
1037 intervalBottom = valueBottom - intervalPtr->valuePixels;
1038 labelBottom = intervalBottom - 2*intervalPtr->borderWidth - intervalPtr->width;
1039
1040 /*
1041 * Display the information from bottom to top across the window.
1042 */
1043
1044 if (intervalPtr->flags & REDRAW_OTHER) {
1045 XClearWindow(Tk_Display(tkwin), Tk_WindowId(tkwin));
1046
1047 /*
1048 * Display the tick marks.
1049 */
1050
1051 if (intervalPtr->tickPixels != 0) {
1052 for (tickValue = intervalPtr->fromValue; ;
1053 tickValue += intervalPtr->tickInterval) {
1054 if (intervalPtr->toValue > intervalPtr->fromValue) {
1055 if (tickValue > intervalPtr->toValue) {
1056 break;
1057 }
1058 } else {
1059 if (tickValue < intervalPtr->toValue) {
1060 break;
1061 }
1062 }
1063 DisplayHorizontalValue(intervalPtr, tickValue, tickBottom);
1064 }
1065 }
1066 }
1067
1068 /*
1069 * Display the values, if they are desired. If not redisplaying the
1070 * entire window, clear the area of the value to get rid of the
1071 * old value displayed there.
1072 */
1073
1074 if (intervalPtr->showValue) {
1075 if (!(intervalPtr->flags & REDRAW_OTHER)) {
1076 XClearArea(Tk_Display(tkwin), Tk_WindowId(tkwin),
1077 intervalPtr->offset, intervalBottom + 1,
1078 Tk_Width(tkwin) - 2*intervalPtr->offset,
1079 valueBottom - intervalBottom, False);
1080 }
1081 DisplayHorizontalValue(intervalPtr, intervalPtr->minValue, valueBottom);
1082 DisplayHorizontalValue(intervalPtr, intervalPtr->maxValue, valueBottom);
1083 }
1084
1085 /*
1086 * Display the interval and the slider. If not redisplaying the
1087 * entire window, must clear the trench area to erase the old
1088 * slider, but don't need to redraw the border.
1089 */
1090
1091 y = intervalBottom - 2*intervalPtr->borderWidth - intervalPtr->width + 1;
1092 if (intervalPtr->flags & REDRAW_OTHER) {
1093 Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
1094 intervalPtr->bgBorder, intervalPtr->offset, y,
1095 Tk_Width(tkwin) - 2*intervalPtr->offset,
1096 intervalPtr->width + 2*intervalPtr->borderWidth,
1097 intervalPtr->borderWidth, TK_RELIEF_SUNKEN);
1098 } else {
1099 XClearArea(Tk_Display(tkwin), Tk_WindowId(tkwin),
1100 intervalPtr->offset + intervalPtr->borderWidth,
1101 y + intervalPtr->borderWidth,
1102 Tk_Width(tkwin) - 2*intervalPtr->offset - 2*intervalPtr->borderWidth,
1103 intervalPtr->width, False);
1104 }
1105 if (intervalPtr->flags & ACTIVE) {
1106 sliderBorder = intervalPtr->activeBorder;
1107 } else {
1108 sliderBorder = intervalPtr->sliderBorder;
1109 }
1110 height = intervalPtr->width;
1111
1112 min = ValueToPixel(intervalPtr, intervalPtr->minValue);
1113 max = ValueToPixel(intervalPtr, intervalPtr->maxValue);
1114
1115 width = (max - min) + (2 * intervalPtr->borderWidth);
1116
1117 y += intervalPtr->borderWidth;
1118 relief = (intervalPtr->flags & BUTTON_PRESSED) ? TK_RELIEF_SUNKEN
1119 : TK_RELIEF_RAISED;
1120 Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin), sliderBorder,
1121 min, y, width, height, intervalPtr->borderWidth, relief);
1122
1123 /*
1124 * Draw the label to the top of the interval.
1125 */
1126
1127 if ((intervalPtr->flags & REDRAW_OTHER) && (intervalPtr->labelPixels != 0)) {
1128 XDrawString(Tk_Display(intervalPtr->tkwin), Tk_WindowId(intervalPtr->tkwin),
1129 intervalPtr->textGC, intervalPtr->offset + intervalPtr->fontPtr->ascent/2,
1130 labelBottom - intervalPtr->fontPtr->descent,
1131 intervalPtr->label, intervalPtr->labelLength);
1132 }
1133
1134 /*
1135 * Draw the window border.
1136 */
1137
1138 if ((intervalPtr->flags & REDRAW_OTHER)
1139 && (intervalPtr->relief != TK_RELIEF_FLAT)) {
1140 Tk_Draw3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
1141 intervalPtr->bgBorder, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
1142 intervalPtr->borderWidth, intervalPtr->relief);
1143 }
1144
1145 done:
1146 intervalPtr->flags &= ~REDRAW_ALL;
1147 }
1148 \f
1149 /*
1150 *----------------------------------------------------------------------
1151 *
1152 * DisplayHorizontalValue --
1153 *
1154 * This procedure is called to display values (interval readings)
1155 * for horizontally-oriented intervals.
1156 *
1157 * Results:
1158 * None.
1159 *
1160 * Side effects:
1161 * The numerical value corresponding to value is displayed with
1162 * its bottom edge at "bottom", and at a horizontal position in
1163 * the interval that corresponds to "value".
1164 *
1165 *----------------------------------------------------------------------
1166 */
1167
1168 static void
1169 DisplayHorizontalValue(intervalPtr, value, bottom)
1170 register Interval *intervalPtr; /* Information about widget in which to
1171 * display value. */
1172 int value; /* Y-coordinate of number to display,
1173 * specified in application coords, not
1174 * in pixels (we'll compute pixels). */
1175 int bottom; /* Y-coordinate of bottom edge of text,
1176 * specified in pixels. */
1177 {
1178 register Tk_Window tkwin = intervalPtr->tkwin;
1179 int x, y, dummy, length;
1180 char valueString[30];
1181 XCharStruct bbox;
1182
1183 x = ValueToPixel(intervalPtr, value);
1184 y = bottom - intervalPtr->fontPtr->descent;
1185 sprintf(valueString, "%d", value);
1186 length = strlen(valueString);
1187 XTextExtents(intervalPtr->fontPtr, valueString, length,
1188 &dummy, &dummy, &dummy, &bbox);
1189
1190 /*
1191 * Adjust the x-coordinate if necessary to keep the text entirely
1192 * inside the window.
1193 */
1194
1195 x -= (bbox.lbearing + bbox.rbearing)/2;
1196 if ((x - bbox.lbearing) < intervalPtr->offset) {
1197 x = intervalPtr->offset + bbox.lbearing;
1198 }
1199 if ((x + bbox.rbearing) > (Tk_Width(tkwin) - intervalPtr->offset)) {
1200 x = Tk_Width(tkwin) - intervalPtr->offset - bbox.rbearing;
1201 }
1202 XDrawString(Tk_Display(tkwin), Tk_WindowId(tkwin),
1203 intervalPtr->textGC, x, y, valueString, length);
1204 }
1205 \f
1206 /*
1207 *----------------------------------------------------------------------
1208 *
1209 * PixelToValue --
1210 *
1211 * Given a pixel within a interval window, return the interval
1212 * reading corresponding to that pixel.
1213 *
1214 * Results:
1215 * An integer interval reading.
1216 *
1217 * Side effects:
1218 * None.
1219 *
1220 *----------------------------------------------------------------------
1221 */
1222
1223 static int
1224 PixelToValue(intervalPtr, x, y)
1225 register Interval *intervalPtr; /* Information about widget. */
1226 int x, y; /* Coordinates of point within
1227 * window. */
1228 {
1229 int value, pixelRange;
1230
1231 if (intervalPtr->vertical) {
1232 pixelRange = Tk_Height(intervalPtr->tkwin)
1233 - 2*intervalPtr->offset - 4*intervalPtr->borderWidth;
1234 value = y;
1235 } else {
1236 pixelRange = Tk_Width(intervalPtr->tkwin)
1237 - 2*intervalPtr->offset - 4*intervalPtr->borderWidth;
1238 value = x;
1239 }
1240
1241 if (pixelRange <= 0) {
1242 /*
1243 * Not enough room for the slider to actually slide: just return
1244 * a constant.
1245 */
1246
1247 return (0);
1248 }
1249 value -= intervalPtr->offset + intervalPtr->borderWidth;
1250 #if 0
1251 if (value < 0) {
1252 value = 0;
1253 }
1254 if (value > pixelRange) {
1255 value = pixelRange;
1256 }
1257 #endif
1258 if (intervalPtr->toValue > intervalPtr->fromValue) {
1259 value = intervalPtr->fromValue +
1260 ((value * (intervalPtr->toValue - intervalPtr->fromValue))
1261 + pixelRange/2)/pixelRange;
1262 } else {
1263 value = intervalPtr->toValue +
1264 (((pixelRange - value)
1265 * (intervalPtr->fromValue - intervalPtr->toValue))
1266 + pixelRange/2)/pixelRange;
1267 }
1268 return value;
1269 }
1270 \f
1271 /*
1272 *----------------------------------------------------------------------
1273 *
1274 * ValueToPixel --
1275 *
1276 * Given a reading of the interval, return the x-coordinate or
1277 * y-coordinate corresponding to that reading, depending on
1278 * whether the interval is vertical or horizontal, respectively.
1279 *
1280 * Results:
1281 * An integer value giving the pixel location corresponding
1282 * to reading. The value is restricted to lie within the
1283 * defined range for the interval.
1284 *
1285 * Side effects:
1286 * None.
1287 *
1288 *----------------------------------------------------------------------
1289 */
1290
1291 static int
1292 ValueToPixel(intervalPtr, value)
1293 register Interval *intervalPtr; /* Information about widget. */
1294 int value; /* Reading of the widget. */
1295 {
1296 int y, pixelRange, valueRange;
1297
1298 valueRange = intervalPtr->toValue - intervalPtr->fromValue;
1299 pixelRange = (intervalPtr->vertical ? Tk_Height(intervalPtr->tkwin)
1300 : Tk_Width(intervalPtr->tkwin))
1301 - 2*intervalPtr->offset - 4*intervalPtr->borderWidth;
1302 y = ((value - intervalPtr->fromValue) * pixelRange
1303 + valueRange/2) / valueRange;
1304 if (y < 0) {
1305 y = 0;
1306 } else if (y > pixelRange) {
1307 y = pixelRange;
1308 }
1309 y += intervalPtr->offset + intervalPtr->borderWidth;
1310 return y;
1311 }
1312 \f
1313 /*
1314 *--------------------------------------------------------------
1315 *
1316 * IntervalEventProc --
1317 *
1318 * This procedure is invoked by the Tk dispatcher for various
1319 * events on intervals.
1320 *
1321 * Results:
1322 * None.
1323 *
1324 * Side effects:
1325 * When the window gets deleted, internal structures get
1326 * cleaned up. When it gets exposed, it is redisplayed.
1327 *
1328 *--------------------------------------------------------------
1329 */
1330
1331 static void
1332 IntervalEventProc(clientData, eventPtr)
1333 ClientData clientData; /* Information about window. */
1334 XEvent *eventPtr; /* Information about event. */
1335 {
1336 Interval *intervalPtr = (Interval *) clientData;
1337
1338 if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
1339 EventuallyRedrawInterval(intervalPtr, REDRAW_ALL);
1340 } else if (eventPtr->type == DestroyNotify) {
1341 Tcl_DeleteCommand(intervalPtr->interp, Tk_PathName(intervalPtr->tkwin));
1342 intervalPtr->tkwin = NULL;
1343 if (intervalPtr->flags & REDRAW_ALL) {
1344 if (intervalPtr->vertical) {
1345 Tk_CancelIdleCall(DisplayVerticalInterval, (ClientData) intervalPtr);
1346 } else {
1347 Tk_CancelIdleCall(DisplayHorizontalInterval,
1348 (ClientData) intervalPtr);
1349 }
1350 }
1351 Tk_EventuallyFree((ClientData) intervalPtr, DestroyInterval);
1352 } else if (eventPtr->type == ConfigureNotify) {
1353 ComputeIntervalGeometry(intervalPtr);
1354 }
1355 }
1356 \f
1357 /*
1358 *--------------------------------------------------------------
1359 *
1360 * IntervalMouseProc --
1361 *
1362 * This procedure is called back by Tk in response to
1363 * mouse events such as window entry, window exit, mouse
1364 * motion, and button presses.
1365 *
1366 * Results:
1367 * None.
1368 *
1369 * Side effects:
1370 * This procedure implements the "feel" of the interval by
1371 * issuing commands in response to button presses and mouse
1372 * motion.
1373 *
1374 *--------------------------------------------------------------
1375 */
1376
1377 static void
1378 IntervalMouseProc(clientData, eventPtr)
1379 ClientData clientData; /* Information about window. */
1380 register XEvent *eventPtr; /* Information about event. */
1381 {
1382 register Interval *intervalPtr = (Interval *) clientData;
1383
1384 if (intervalPtr->state != tkNormalUid) {
1385 return;
1386 }
1387
1388 Tk_Preserve((ClientData) intervalPtr);
1389 if (eventPtr->type == EnterNotify) {
1390 intervalPtr->flags |= ACTIVE;
1391 EventuallyRedrawInterval(intervalPtr, REDRAW_SLIDER);
1392 } else if (eventPtr->type == LeaveNotify) {
1393 intervalPtr->flags &= ~ACTIVE;
1394 EventuallyRedrawInterval(intervalPtr, REDRAW_SLIDER);
1395 } else if ((eventPtr->type == MotionNotify)
1396 && (intervalPtr->flags & BUTTON_PRESSED)) {
1397 TrackInterval(intervalPtr, PixelToValue(intervalPtr,
1398 eventPtr->xmotion.x, eventPtr->xmotion.y));
1399 } else if ((eventPtr->type == ButtonPress)
1400 /* && (eventPtr->xbutton.button == Button1) */
1401 && (eventPtr->xbutton.state == 0)) {
1402 intervalPtr->flags |= BUTTON_PRESSED;
1403 StartTrackInterval(intervalPtr, PixelToValue(intervalPtr,
1404 eventPtr->xbutton.x, eventPtr->xbutton.y));
1405 /* NotifyInterval(intervalPtr); */
1406 EventuallyRedrawInterval(intervalPtr, REDRAW_SLIDER);
1407 } else if ((eventPtr->type == ButtonRelease)
1408 /* && (eventPtr->xbutton.button == Button1) */
1409 && (intervalPtr->flags & BUTTON_PRESSED)) {
1410 intervalPtr->flags &= ~BUTTON_PRESSED;
1411 TrackInterval(intervalPtr, PixelToValue(intervalPtr,
1412 eventPtr->xmotion.x, eventPtr->xmotion.y));
1413 /* NotifyInterval(intervalPtr); */
1414 EventuallyRedrawInterval(intervalPtr, REDRAW_SLIDER);
1415 }
1416 Tk_Release((ClientData) intervalPtr);
1417 }
1418 \f
1419 /*
1420 *--------------------------------------------------------------
1421 *
1422 * TrackInterval --
1423 *
1424 * This procedure changes the value of a interval and invokes
1425 * a Tcl command to reflect the current position of a interval
1426 *
1427 * Results:
1428 * None.
1429 *
1430 * Side effects:
1431 * A Tcl command is invoked, and an additional error-processing
1432 * command may also be invoked. The interval's slider is redrawn.
1433 *
1434 *--------------------------------------------------------------
1435 */
1436
1437 static void
1438 StartTrackInterval(intervalPtr, value)
1439 register Interval *intervalPtr; /* Info about widget. */
1440 int value; /* New value for interval. Gets
1441 * adjusted if it's off the interval. */
1442 {
1443 if ((value < intervalPtr->fromValue)
1444 ^ (intervalPtr->toValue < intervalPtr->fromValue)) {
1445 value = intervalPtr->fromValue;
1446 }
1447 if ((value > intervalPtr->toValue)
1448 ^ (intervalPtr->toValue < intervalPtr->fromValue)) {
1449 value = intervalPtr->toValue;
1450 }
1451 intervalPtr->trackValue = value;
1452 intervalPtr->trackWidth = intervalPtr->maxValue - intervalPtr->minValue;
1453 if (value <= intervalPtr->minValue) {
1454 intervalPtr->trackState = -1;
1455 } else if (value >= intervalPtr->maxValue) {
1456 intervalPtr->trackState = 1;
1457 } else {
1458 intervalPtr->trackState = 0;
1459 }
1460 SetInterval(intervalPtr, intervalPtr->minValue, intervalPtr->maxValue, 1);
1461 }
1462
1463
1464 static void
1465 TrackInterval(intervalPtr, value)
1466 register Interval *intervalPtr; /* Info about widget. */
1467 int value;
1468 {
1469 int min, max, delta, lastmin, lastmax;
1470
1471
1472 delta = value - intervalPtr->trackValue;
1473 if (delta == 0) return;
1474
1475 intervalPtr->trackValue = value;
1476
1477 min = intervalPtr->minValue;
1478 max = intervalPtr->maxValue;
1479
1480 switch (intervalPtr->trackState) {
1481 case -1: /* left trench */
1482 min += delta;
1483 if (min > max) max = min;
1484 break;
1485 case 1: /* right trench */
1486 max += delta;
1487 if (min > max) min = max;
1488 break;
1489 case 0: /* center slider */
1490 lastmin = min; lastmax = max;
1491 min += delta; max += delta;
1492 if ((max - min) != intervalPtr->trackWidth) { /* squished */
1493 if (lastmin == intervalPtr->fromValue) {
1494 min = max - intervalPtr->trackWidth;
1495 } else if (lastmax == intervalPtr->toValue) {
1496 max = min + intervalPtr->trackWidth;
1497 }
1498 }
1499 break;
1500 }
1501
1502 SetInterval(intervalPtr, min, max, 1);
1503 }
1504
1505
1506 static void
1507 SetInterval(intervalPtr, min, max, notify)
1508 register Interval *intervalPtr; /* Info about widget. */
1509 int min, max, notify;
1510 {
1511 if (min > max) {
1512 int temp = min;
1513 min = max;
1514 max = temp;
1515 }
1516
1517 if ((min < intervalPtr->fromValue)
1518 ^ (intervalPtr->toValue < intervalPtr->fromValue)) {
1519 min = intervalPtr->fromValue;
1520 }
1521 if ((min > intervalPtr->toValue)
1522 ^ (intervalPtr->toValue < intervalPtr->fromValue)) {
1523 min = intervalPtr->toValue;
1524 }
1525 if ((max < intervalPtr->fromValue)
1526 ^ (intervalPtr->toValue < intervalPtr->fromValue)) {
1527 max = intervalPtr->fromValue;
1528 }
1529 if ((max > intervalPtr->toValue)
1530 ^ (intervalPtr->toValue < intervalPtr->fromValue)) {
1531 max = intervalPtr->toValue;
1532 }
1533
1534 if ((min == intervalPtr->minValue) &&
1535 (max == intervalPtr->maxValue)) {
1536 return;
1537 }
1538
1539 intervalPtr->minValue = min;
1540 intervalPtr->maxValue = max;
1541 EventuallyRedrawInterval(intervalPtr, REDRAW_SLIDER);
1542
1543 if (notify)
1544 NotifyInterval(intervalPtr);
1545 }
1546
1547
1548 static void
1549 NotifyInterval(register Interval *intervalPtr)
1550 {
1551 int result;
1552 char string[256];
1553
1554 sprintf(string, " %d %d", intervalPtr->minValue, intervalPtr->maxValue);
1555 result = Tcl_VarEval(intervalPtr->interp, intervalPtr->command, string,
1556 (char *) NULL);
1557 if (result != TCL_OK) {
1558 TkBindError(intervalPtr->interp);
1559 }
1560 }
1561
1562
1563 \f
1564 /*
1565 *--------------------------------------------------------------
1566 *
1567 * EventuallyRedrawInterval --
1568 *
1569 * Arrange for part or all of a interval widget to redrawn at
1570 * the next convenient time in the future.
1571 *
1572 * Results:
1573 * None.
1574 *
1575 * Side effects:
1576 * If "what" is REDRAW_SLIDER then just the slider and the
1577 * value readout will be redrawn; if "what" is REDRAW_ALL
1578 * then the entire widget will be redrawn.
1579 *
1580 *--------------------------------------------------------------
1581 */
1582
1583 static void
1584 EventuallyRedrawInterval(intervalPtr, what)
1585 register Interval *intervalPtr; /* Information about widget. */
1586 int what; /* What to redraw: REDRAW_SLIDER
1587 * or REDRAW_ALL. */
1588 {
1589 if ((what == 0) || (intervalPtr->tkwin == NULL)
1590 || !Tk_IsMapped(intervalPtr->tkwin)) {
1591 return;
1592 }
1593 if ((intervalPtr->flags & REDRAW_ALL) == 0) {
1594 if (intervalPtr->vertical) {
1595 Tk_DoWhenIdle(DisplayVerticalInterval, (ClientData) intervalPtr);
1596 } else {
1597 Tk_DoWhenIdle(DisplayHorizontalInterval, (ClientData) intervalPtr);
1598 }
1599 }
1600 intervalPtr->flags |= what;
1601 }
Impressum, Datenschutz