|
|
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.