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