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