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, 
   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. 
  19 /* Improvements in the version used for Micropolis are copyrighted and 
  20  * licensed under these copyright terms. 
  22  * Micropolis, Unix Version.  This game was released for the Unix platform 
  23  * in or about 1990 and has been modified for inclusion in the One Laptop 
  24  * Per Child program.  Copyright (C) 1989 - 2007 Electronic Arts Inc.  If 
  25  * you need assistance with this program, you may contact: 
  26  *   http://wiki.laptop.org/go/Micropolis  or email  micropolis@laptop.org. 
  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. 
  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/>. 
  40  *             ADDITIONAL TERMS per GNU GPL Section 7 
  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. 
  48  * Any propagation or conveyance of this program must include this 
  49  * copyright notice and these terms. 
  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. 
  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. 
  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 
  87  * A data structure of the following type is kept for each interval 
  88  * widget managed by this file: 
  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 
  96     Tcl_Interp 
*interp
;         /* Interpreter associated with interval. */ 
  97     Tk_Uid orientUid
;           /* Orientation for window ("vertical" or 
  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 
 105     int toValue
;                /* Value corresponding to right or bottom 
 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. 
 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. */ 
 125      * Information used when displaying widget: 
 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, 
 138     int length
;                 /* Desired long dimension of interval, 
 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. */ 
 156      * Miscellaneous information: 
 159     Cursor cursor
;              /* Current cursor for window, or None. */ 
 160     int flags
;                  /* Various flags;  see below for 
 165  * Flag bits for intervals: 
 167  * REDRAW_SLIDER -              1 means slider (and numerical readout) need 
 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 
 174  * BUTTON_PRESSED -             1 means a button press is in progress, so 
 175  *                              slider should appear depressed and should be 
 179 #define REDRAW_SLIDER           1 
 180 #define REDRAW_OTHER            2 
 183 #define BUTTON_PRESSED          8 
 186  * Space to leave between interval area and text. 
 192  * Information used for argv parsing. 
 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
), 
 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
, 
 265  * Forward declarations for procedures defined later in this file: 
 268 static void             ComputeIntervalGeometry 
_ANSI_ARGS_((Interval 
*intervalPtr
)); 
 269 static int              ConfigureInterval 
_ANSI_ARGS_((Tcl_Interp 
*interp
, 
 270                             Interval 
*intervalPtr
, int argc
, char **argv
, 
 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
, 
 283 static int              PixelToValue 
_ANSI_ARGS_((Interval 
*intervalPtr
, int x
, 
 285 static void             IntervalEventProc 
_ANSI_ARGS_((ClientData clientData
, 
 287 static void             IntervalMouseProc 
_ANSI_ARGS_((ClientData clientData
, 
 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
, 
 295 static void             StartTrackInterval 
_ANSI_ARGS_((Interval 
*intervalPtr
, 
 297 static int              ValueToPixel 
_ANSI_ARGS_((Interval 
*intervalPtr
, int value
)); 
 299 static void             NotifyInterval(register Interval 
*intervalPtr
); 
 302  *-------------------------------------------------------------- 
 306  *      This procedure is invoked to process the "interval" Tcl 
 307  *      command.  See the user documentation for details on what 
 311  *      A standard Tcl result. 
 314  *      See the user documentation. 
 316  *-------------------------------------------------------------- 
 320 Tk_IntervalCmd(clientData
, interp
, argc
, argv
) 
 321     ClientData clientData
;              /* Main window associated with 
 323     Tcl_Interp 
*interp
;         /* Current interpreter. */ 
 324     int argc
;                   /* Number of arguments. */ 
 325     char **argv
;                /* Argument strings. */ 
 327     Tk_Window tkwin 
= (Tk_Window
) clientData
; 
 328     register Interval 
*intervalPtr
; 
 332         Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 333                 argv
[0], " pathName ?options?\"", (char *) NULL
); 
 337     new = Tk_CreateWindowFromPath(interp
, tkwin
, argv
[1], (char *) NULL
); 
 343      * Initialize fields that won't be initialized by ConfigureInterval, 
 344      * or which ConfigureInterval expects to have reasonable values 
 345      * (e.g. resource pointers). 
 348     intervalPtr 
= (Interval 
*) ckalloc(sizeof(Interval
)); 
 349     intervalPtr
->tkwin 
= new; 
 350     intervalPtr
->interp 
= interp
; 
 351     intervalPtr
->minValue 
= 0; 
 352     intervalPtr
->maxValue 
= 0; 
 353     intervalPtr
->command 
= NULL
; 
 354     intervalPtr
->label 
= NULL
; 
 355     intervalPtr
->state 
= tkNormalUid
; 
 356     intervalPtr
->bgBorder 
= NULL
; 
 357     intervalPtr
->sliderBorder 
= NULL
; 
 358     intervalPtr
->activeBorder 
= NULL
; 
 359     intervalPtr
->fontPtr 
= NULL
; 
 360     intervalPtr
->textColorPtr 
= NULL
; 
 361     intervalPtr
->textGC 
= None
; 
 362     intervalPtr
->cursor 
= None
; 
 363     intervalPtr
->flags 
= 0; 
 365     Tk_SetClass(intervalPtr
->tkwin
, "Interval"); 
 366     Tk_CreateEventHandler(intervalPtr
->tkwin
, ExposureMask
|StructureNotifyMask
, 
 367             IntervalEventProc
, (ClientData
) intervalPtr
); 
 368     Tk_CreateEventHandler(intervalPtr
->tkwin
, EnterWindowMask
|LeaveWindowMask
 
 369             |PointerMotionMask
|ButtonPressMask
|ButtonReleaseMask
, 
 370             IntervalMouseProc
, (ClientData
) intervalPtr
); 
 371     Tcl_CreateCommand(interp
, Tk_PathName(intervalPtr
->tkwin
), IntervalWidgetCmd
, 
 372             (ClientData
) intervalPtr
, (void (*)()) NULL
); 
 373     if (ConfigureInterval(interp
, intervalPtr
, argc
-2, argv
+2, 0) != TCL_OK
) { 
 377     interp
->result 
= Tk_PathName(intervalPtr
->tkwin
); 
 381     Tk_DestroyWindow(intervalPtr
->tkwin
); 
 386  *-------------------------------------------------------------- 
 388  * IntervalWidgetCmd -- 
 390  *      This procedure is invoked to process the Tcl command 
 391  *      that corresponds to a widget managed by this module. 
 392  *      See the user documentation for details on what it does. 
 395  *      A standard Tcl result. 
 398  *      See the user documentation. 
 400  *-------------------------------------------------------------- 
 404 IntervalWidgetCmd(clientData
, interp
, argc
, argv
) 
 405     ClientData clientData
;              /* Information about interval 
 407     Tcl_Interp 
*interp
;                 /* Current interpreter. */ 
 408     int argc
;                           /* Number of arguments. */ 
 409     char **argv
;                        /* Argument strings. */ 
 411     register Interval 
*intervalPtr 
= (Interval 
*) clientData
; 
 417         Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 418                 argv
[0], " option ?arg arg ...?\"", (char *) NULL
); 
 421     Tk_Preserve((ClientData
) intervalPtr
); 
 423     length 
= strlen(argv
[1]); 
 424     if ((c 
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)) { 
 426             result 
= Tk_ConfigureInfo(interp
, intervalPtr
->tkwin
, configSpecs
, 
 427                     (char *) intervalPtr
, (char *) NULL
, 0); 
 428         } else if (argc 
== 3) { 
 429             result 
= Tk_ConfigureInfo(interp
, intervalPtr
->tkwin
, configSpecs
, 
 430                     (char *) intervalPtr
, argv
[2], 0); 
 432             result 
= ConfigureInterval(interp
, intervalPtr
, argc
-2, argv
+2, 
 433                     TK_CONFIG_ARGV_ONLY
); 
 435     } else if ((c 
== 'g') && (strncmp(argv
[1], "get", length
) == 0)) { 
 437             Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 438                     argv
[0], " get\"", (char *) NULL
); 
 441         sprintf(interp
->result
, "%d %d", intervalPtr
->minValue
, intervalPtr
->maxValue
); 
 442     } else if ((c 
== 's') && (strncmp(argv
[1], "set", length
) == 0)) { 
 443         int minValue
, maxValue
; 
 446             Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 447                     argv
[0], " set minValue maxValue\"", (char *) NULL
); 
 450         if (Tcl_GetInt(interp
, argv
[2], &minValue
) != TCL_OK
) { 
 453         if (Tcl_GetInt(interp
, argv
[3], &maxValue
) != TCL_OK
) { 
 456         if (minValue 
> maxValue
) { 
 458           minValue 
= maxValue
; maxValue 
= temp
; 
 460         if (intervalPtr
->state 
== tkNormalUid
) { 
 461             if ((minValue 
< intervalPtr
->fromValue
) 
 462                     ^ (intervalPtr
->toValue 
< intervalPtr
->fromValue
)) { 
 463                 minValue 
= intervalPtr
->fromValue
; 
 465             if ((minValue 
> intervalPtr
->toValue
) 
 466                     ^ (intervalPtr
->toValue 
< intervalPtr
->fromValue
)) { 
 467                 minValue 
= intervalPtr
->toValue
; 
 469             if ((maxValue 
< intervalPtr
->fromValue
) 
 470                     ^ (intervalPtr
->toValue 
< intervalPtr
->fromValue
)) { 
 471                 maxValue 
= intervalPtr
->fromValue
; 
 473             if ((maxValue 
> intervalPtr
->toValue
) 
 474                     ^ (intervalPtr
->toValue 
< intervalPtr
->fromValue
)) { 
 475                 maxValue 
= intervalPtr
->toValue
; 
 477             SetInterval(intervalPtr
, minValue
, maxValue
, 1); 
 479     } else if ((c 
== 'r') && (strncmp(argv
[1], "reset", length
) == 0)) { 
 481             Tcl_AppendResult(interp
, "wrong # args: should be \"", 
 482                     argv
[0], " reset\"", (char *) NULL
); 
 485         SetInterval(intervalPtr
, 
 486                     intervalPtr
->fromValue
, intervalPtr
->toValue
, 0); 
 488         Tcl_AppendResult(interp
, "bad option \"", argv
[1], 
 489                 "\":  must be configure, get, or set", (char *) NULL
); 
 492     Tk_Release((ClientData
) intervalPtr
); 
 496     Tk_Release((ClientData
) intervalPtr
); 
 501  *---------------------------------------------------------------------- 
 505  *      This procedure is invoked by Tk_EventuallyFree or Tk_Release 
 506  *      to clean up the internal structure of a button at a safe time 
 507  *      (when no-one is using it anymore). 
 513  *      Everything associated with the interval is freed up. 
 515  *---------------------------------------------------------------------- 
 519 DestroyInterval(clientData
) 
 520     ClientData clientData
;      /* Info about interval widget. */ 
 522     register Interval 
*intervalPtr 
= (Interval 
*) clientData
; 
 524     if (intervalPtr
->command 
!= NULL
) { 
 525         ckfree(intervalPtr
->command
); 
 527     if (intervalPtr
->label 
!= NULL
) { 
 528         ckfree(intervalPtr
->label
); 
 530     if (intervalPtr
->bgBorder 
!= NULL
) { 
 531         Tk_Free3DBorder(intervalPtr
->bgBorder
); 
 533     if (intervalPtr
->sliderBorder 
!= NULL
) { 
 534         Tk_Free3DBorder(intervalPtr
->sliderBorder
); 
 536     if (intervalPtr
->activeBorder 
!= NULL
) { 
 537         Tk_Free3DBorder(intervalPtr
->activeBorder
); 
 539     if (intervalPtr
->fontPtr 
!= NULL
) { 
 540         Tk_FreeFontStruct(intervalPtr
->fontPtr
); 
 542     if (intervalPtr
->textColorPtr 
!= NULL
) { 
 543         Tk_FreeColor(intervalPtr
->textColorPtr
); 
 545     if (intervalPtr
->textGC 
!= None
) { 
 546         Tk_FreeGC(intervalPtr
->textGC
); 
 548     if (intervalPtr
->cursor 
!= None
) { 
 549         Tk_FreeCursor(intervalPtr
->cursor
); 
 551     ckfree((char *) intervalPtr
); 
 555  *---------------------------------------------------------------------- 
 557  * ConfigureInterval -- 
 559  *      This procedure is called to process an argv/argc list, plus 
 560  *      the Tk option database, in order to configure (or 
 561  *      reconfigure) a interval widget. 
 564  *      The return value is a standard Tcl result.  If TCL_ERROR is 
 565  *      returned, then interp->result contains an error message. 
 568  *      Configuration information, such as colors, border width, 
 569  *      etc. get set for intervalPtr;  old resources get freed, 
 572  *---------------------------------------------------------------------- 
 576 ConfigureInterval(interp
, intervalPtr
, argc
, argv
, flags
) 
 577     Tcl_Interp 
*interp
;         /* Used for error reporting. */ 
 578     register Interval 
*intervalPtr
;     /* Information about widget;  may or may 
 579                                  * not already have values for some fields. */ 
 580     int argc
;                   /* Number of valid entries in argv. */ 
 581     char **argv
;                /* Arguments. */ 
 582     int flags
;                  /* Flags to pass to Tk_ConfigureWidget. */ 
 588     if (Tk_ConfigureWidget(interp
, intervalPtr
->tkwin
, configSpecs
, 
 589             argc
, argv
, (char *) intervalPtr
, flags
) != TCL_OK
) { 
 594      * A few options need special processing, such as parsing the 
 595      * orientation or setting the background from a 3-D border. 
 598     length 
= strlen(intervalPtr
->orientUid
); 
 599     if (strncmp(intervalPtr
->orientUid
, "vertical", length
) == 0) { 
 600         intervalPtr
->vertical 
= 1; 
 601     } else if (strncmp(intervalPtr
->orientUid
, "horizontal", length
) == 0) { 
 602         intervalPtr
->vertical 
= 0; 
 604         Tcl_AppendResult(interp
, "bad orientation \"", intervalPtr
->orientUid
, 
 605                 "\": must be vertical or horizontal", (char *) NULL
); 
 609     if ((intervalPtr
->state 
!= tkNormalUid
) 
 610             && (intervalPtr
->state 
!= tkDisabledUid
)) { 
 611         Tcl_AppendResult(interp
, "bad state value \"", intervalPtr
->state
, 
 612                 "\":  must be normal or disabled", (char *) NULL
); 
 613         intervalPtr
->state 
= tkNormalUid
; 
 618      * Make sure that the tick interval has the right sign so that 
 619      * addition moves from fromValue to toValue. 
 622     if ((intervalPtr
->tickInterval 
< 0) 
 623             ^ ((intervalPtr
->toValue 
- intervalPtr
->fromValue
) <  0)) { 
 624         intervalPtr
->tickInterval 
= -intervalPtr
->tickInterval
; 
 628      * Set the interval mix and max values to themselves;  all this does is 
 629      * to make sure that the interval's value is within the new acceptable  
 630      * range for the interval. 
 633     SetInterval(intervalPtr
, intervalPtr
->minValue
, intervalPtr
->maxValue
, 0); 
 635     if (intervalPtr
->command 
!= NULL
) { 
 636         intervalPtr
->commandLength 
= strlen(intervalPtr
->command
); 
 638         intervalPtr
->commandLength 
= 0; 
 641     if (intervalPtr
->label 
!= NULL
) { 
 642         intervalPtr
->labelLength 
= strlen(intervalPtr
->label
); 
 644         intervalPtr
->labelLength 
= 0; 
 647     Tk_SetBackgroundFromBorder(intervalPtr
->tkwin
, intervalPtr
->bgBorder
); 
 649     gcValues
.font 
= intervalPtr
->fontPtr
->fid
; 
 650     gcValues
.foreground 
= intervalPtr
->textColorPtr
->pixel
; 
 651     newGC 
= Tk_GetGC(intervalPtr
->tkwin
, GCForeground
|GCFont
, &gcValues
); 
 652     if (intervalPtr
->textGC 
!= None
) { 
 653         Tk_FreeGC(intervalPtr
->textGC
); 
 655     intervalPtr
->textGC 
= newGC
; 
 657     if (intervalPtr
->relief 
!= TK_RELIEF_FLAT
) { 
 658         intervalPtr
->offset 
= intervalPtr
->borderWidth
; 
 660         intervalPtr
->offset 
= 0; 
 664      * Recompute display-related information, and let the geometry 
 665      * manager know how much space is needed now. 
 668     ComputeIntervalGeometry(intervalPtr
); 
 670     EventuallyRedrawInterval(intervalPtr
, REDRAW_ALL
); 
 675  *---------------------------------------------------------------------- 
 677  * ComputeIntervalGeometry -- 
 679  *      This procedure is called to compute various geometrical 
 680  *      information for a interval, such as where various things get 
 681  *      displayed.  It's called when the window is reconfigured. 
 687  *      Display-related numbers get changed in *scrollPtr.  The 
 688  *      geometry manager gets told about the window's preferred size. 
 690  *---------------------------------------------------------------------- 
 694 ComputeIntervalGeometry(intervalPtr
) 
 695     register Interval 
*intervalPtr
;             /* Information about widget. */ 
 698     char valueString
[30]; 
 699     int dummy
, lineHeight
; 
 702      * Horizontal intervals are simpler than vertical ones because 
 703      * all sizes are the same (the height of a line of text); 
 704      * handle them first and then quit. 
 707     if (!intervalPtr
->vertical
) { 
 708         lineHeight 
= intervalPtr
->fontPtr
->ascent 
+ intervalPtr
->fontPtr
->descent
; 
 709         if (intervalPtr
->tickInterval 
!= 0) { 
 710             intervalPtr
->tickPixels 
= lineHeight
; 
 712             intervalPtr
->tickPixels 
= 0; 
 714         if (intervalPtr
->showValue
) { 
 715             intervalPtr
->valuePixels 
= lineHeight 
+ SPACING
; 
 717             intervalPtr
->valuePixels 
= 0; 
 719         if (intervalPtr
->labelLength 
!= 0) { 
 720             intervalPtr
->labelPixels 
= lineHeight
; 
 722             intervalPtr
->labelPixels 
= 0; 
 725         Tk_GeometryRequest(intervalPtr
->tkwin
, 
 726                 intervalPtr
->length 
+ 2*intervalPtr
->offset
, 
 727                 intervalPtr
->tickPixels 
+ intervalPtr
->valuePixels
 
 728                 + intervalPtr
->width 
+ 2*intervalPtr
->borderWidth
 
 729                 + intervalPtr
->labelPixels 
+ 2*intervalPtr
->offset
); 
 730         Tk_SetInternalBorder(intervalPtr
->tkwin
, intervalPtr
->borderWidth
); 
 735      * Vertical interval:  compute the amount of space needed for tick marks 
 736      * and current value by formatting strings for the two end points; 
 737      * use whichever length is longer. 
 740     sprintf(valueString
, "%d", intervalPtr
->fromValue
); 
 741     XTextExtents(intervalPtr
->fontPtr
, valueString
, strlen(valueString
), 
 742             &dummy
, &dummy
, &dummy
, &bbox
); 
 743     intervalPtr
->tickPixels 
= bbox
.rbearing 
+ bbox
.lbearing
; 
 744     sprintf(valueString
, "%d", intervalPtr
->toValue
); 
 745     XTextExtents(intervalPtr
->fontPtr
, valueString
, strlen(valueString
), 
 746             &dummy
, &dummy
, &dummy
, &bbox
); 
 747     if (intervalPtr
->tickPixels 
< bbox
.rbearing 
+ bbox
.lbearing
) { 
 748         intervalPtr
->tickPixels 
= bbox
.rbearing 
+ bbox
.lbearing
; 
 752      * Pad the value with a bit of extra space for prettier printing. 
 755     intervalPtr
->tickPixels 
+= intervalPtr
->fontPtr
->ascent
/2; 
 756     intervalPtr
->valuePixels 
= intervalPtr
->tickPixels
; 
 757     if (intervalPtr
->tickInterval 
== 0) { 
 758         intervalPtr
->tickPixels 
= 0; 
 760     if (!intervalPtr
->showValue
) { 
 761         intervalPtr
->valuePixels 
= 0; 
 764     if (intervalPtr
->labelLength 
== 0) { 
 765         intervalPtr
->labelPixels 
= 0; 
 767         XTextExtents(intervalPtr
->fontPtr
, intervalPtr
->label
, 
 768                 intervalPtr
->labelLength
, &dummy
, &dummy
, &dummy
, &bbox
); 
 769         intervalPtr
->labelPixels 
= bbox
.rbearing 
+ bbox
.lbearing
 
 770                 + intervalPtr
->fontPtr
->ascent
; 
 772     Tk_GeometryRequest(intervalPtr
->tkwin
, 2*intervalPtr
->borderWidth
 
 773             + intervalPtr
->tickPixels 
+ intervalPtr
->valuePixels 
+ SPACING
 
 774             + intervalPtr
->width 
+ intervalPtr
->labelPixels
, 
 775             intervalPtr
->length
); 
 776     Tk_SetInternalBorder(intervalPtr
->tkwin
, intervalPtr
->borderWidth
); 
 780  *-------------------------------------------------------------- 
 782  * DisplayVerticalInterval -- 
 784  *      This procedure redraws the contents of a vertical interval 
 785  *      window.  It is invoked as a do-when-idle handler, so it only 
 786  *      runs when there's nothing else for the application to do. 
 792  *      Information appears on the screen. 
 794  *-------------------------------------------------------------- 
 798 DisplayVerticalInterval(clientData
) 
 799     ClientData clientData
;      /* Information about widget. */ 
 801     register Interval 
*intervalPtr 
= (Interval 
*) clientData
; 
 802     register Tk_Window tkwin 
= intervalPtr
->tkwin
; 
 803     int tickRightEdge
, valueRightEdge
, labelLeftEdge
, intervalLeftEdge
; 
 804     int totalPixels
, x
, width
, height
, tickValue
, min
, max
; 
 806     Tk_3DBorder sliderBorder
; 
 808     if ((intervalPtr
->tkwin 
== NULL
) || !Tk_IsMapped(tkwin
)) { 
 813      * Scanning from left to right across the window, the window 
 814      * will contain four columns:  ticks, value, interval, and label. 
 815      * Compute the x-coordinate for each of the columns. 
 818     totalPixels 
= intervalPtr
->tickPixels 
+ intervalPtr
->valuePixels
 
 819             + 2*intervalPtr
->borderWidth 
+ intervalPtr
->width
 
 820             + 2*SPACING 
+ intervalPtr
->labelPixels
; 
 821     tickRightEdge 
= (Tk_Width(tkwin
) - totalPixels
)/2 + intervalPtr
->tickPixels
; 
 822     valueRightEdge 
= tickRightEdge 
+ intervalPtr
->valuePixels
; 
 823     intervalLeftEdge 
= valueRightEdge 
+ SPACING
; 
 824     labelLeftEdge 
= intervalLeftEdge 
+ 2*intervalPtr
->borderWidth
 
 825             + intervalPtr
->width 
+ intervalPtr
->fontPtr
->ascent
/2; 
 828      * Display the information from left to right across the window. 
 831     if (intervalPtr
->flags 
& REDRAW_OTHER
) { 
 832         XClearWindow(Tk_Display(tkwin
), Tk_WindowId(tkwin
)); 
 835          * Display the tick marks. 
 838         if (intervalPtr
->tickPixels 
!= 0) { 
 839             for (tickValue 
= intervalPtr
->fromValue
; ; 
 840                     tickValue 
+= intervalPtr
->tickInterval
) { 
 841                 if (intervalPtr
->toValue 
> intervalPtr
->fromValue
) { 
 842                     if (tickValue 
> intervalPtr
->toValue
) { 
 846                     if (tickValue 
< intervalPtr
->toValue
) { 
 850                 DisplayVerticalValue(intervalPtr
, tickValue
, tickRightEdge
); 
 856      * Display the values, if they are desired.  If not redisplaying the 
 857      * entire window, clear the area of the value to get rid of the 
 858      * old value displayed there. 
 861     if (intervalPtr
->showValue
) { 
 862         if (!(intervalPtr
->flags 
& REDRAW_OTHER
)) { 
 863             XClearArea(Tk_Display(tkwin
), Tk_WindowId(tkwin
), 
 864                     valueRightEdge
-intervalPtr
->valuePixels
, intervalPtr
->offset
, 
 865                     intervalPtr
->valuePixels
, 
 866                     Tk_Height(tkwin
) - 2*intervalPtr
->offset
, False
); 
 868         DisplayVerticalValue(intervalPtr
, intervalPtr
->minValue
, valueRightEdge
); 
 869         DisplayVerticalValue(intervalPtr
, intervalPtr
->maxValue
, valueRightEdge
); 
 873      * Display the interval and the slider.  If not redisplaying the 
 874      * entire window, must clear the trench area to erase the old 
 875      * slider, but don't need to redraw the border. 
 878     if (intervalPtr
->flags 
& REDRAW_OTHER
) { 
 879         Tk_Draw3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
), 
 880                 intervalPtr
->bgBorder
, intervalLeftEdge
, intervalPtr
->offset
, 
 881                 intervalPtr
->width 
+ 2*intervalPtr
->borderWidth
, 
 882                 Tk_Height(tkwin
) - 2*intervalPtr
->offset
, intervalPtr
->borderWidth
, 
 885         XClearArea(Tk_Display(tkwin
), Tk_WindowId(tkwin
), 
 886                 intervalLeftEdge 
+ intervalPtr
->borderWidth
, 
 887                 intervalPtr
->offset 
+ intervalPtr
->borderWidth
, 
 889                 Tk_Height(tkwin
) - 2*intervalPtr
->offset
 
 890                 - 2*intervalPtr
->borderWidth
, False
); 
 892     if (intervalPtr
->flags 
& ACTIVE
) { 
 893         sliderBorder 
= intervalPtr
->activeBorder
; 
 895         sliderBorder 
= intervalPtr
->sliderBorder
; 
 897     width 
= intervalPtr
->width
; 
 899     min 
= ValueToPixel(intervalPtr
, intervalPtr
->minValue
); 
 900     max 
= ValueToPixel(intervalPtr
, intervalPtr
->maxValue
); 
 902     height 
= (max 
- min
) + (2 * intervalPtr
->borderWidth
); 
 904     x 
= intervalLeftEdge 
+ intervalPtr
->borderWidth
; 
 906     relief 
= (intervalPtr
->flags 
& BUTTON_PRESSED
) ? TK_RELIEF_SUNKEN
 
 908     Tk_Fill3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
), sliderBorder
, 
 909             x
, min
, width
, height
, intervalPtr
->borderWidth
, relief
); 
 912      * Draw the label to the right of the interval. 
 915     if ((intervalPtr
->flags 
& REDRAW_OTHER
) && (intervalPtr
->labelPixels 
!= 0)) { 
 916         XDrawString(Tk_Display(intervalPtr
->tkwin
), Tk_WindowId(intervalPtr
->tkwin
), 
 917             intervalPtr
->textGC
, labelLeftEdge
, 
 918             intervalPtr
->offset 
+ (3*intervalPtr
->fontPtr
->ascent
)/2, 
 919             intervalPtr
->label
, intervalPtr
->labelLength
); 
 923      * Draw the window border. 
 926     if ((intervalPtr
->flags 
& REDRAW_OTHER
) 
 927             && (intervalPtr
->relief 
!= TK_RELIEF_FLAT
)) { 
 928         Tk_Draw3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
), 
 929                 intervalPtr
->bgBorder
, 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
), 
 930                 intervalPtr
->borderWidth
, intervalPtr
->relief
); 
 934     intervalPtr
->flags 
&= ~REDRAW_ALL
; 
 938  *---------------------------------------------------------------------- 
 940  * DisplayVerticalValue -- 
 942  *      This procedure is called to display values (interval readings) 
 943  *      for vertically-oriented intervals. 
 949  *      The numerical value corresponding to value is displayed with 
 950  *      its right edge at "rightEdge", and at a vertical position in 
 951  *      the interval that corresponds to "value". 
 953  *---------------------------------------------------------------------- 
 957 DisplayVerticalValue(intervalPtr
, value
, rightEdge
) 
 958     register Interval 
*intervalPtr
;     /* Information about widget in which to 
 960     int value
;                  /* Y-coordinate of number to display, 
 961                                  * specified in application coords, not 
 962                                  * in pixels (we'll compute pixels). */ 
 963     int rightEdge
;              /* X-coordinate of right edge of text, 
 964                                  * specified in pixels. */ 
 966     register Tk_Window tkwin 
= intervalPtr
->tkwin
; 
 967     int y
, dummy
, length
; 
 968     char valueString
[30]; 
 971     y 
= ValueToPixel(intervalPtr
, value
) + intervalPtr
->fontPtr
->ascent
/2; 
 972     sprintf(valueString
, "%d", value
); 
 973     length 
= strlen(valueString
); 
 974     XTextExtents(intervalPtr
->fontPtr
, valueString
, length
, 
 975             &dummy
, &dummy
, &dummy
, &bbox
); 
 978      * Adjust the y-coordinate if necessary to keep the text entirely 
 982     if ((y 
- bbox
.ascent
) < intervalPtr
->offset
) { 
 983         y 
= intervalPtr
->offset 
+ bbox
.ascent
; 
 985     if ((y 
+ bbox
.descent
) > (Tk_Height(tkwin
) - intervalPtr
->offset
)) { 
 986         y 
= Tk_Height(tkwin
) - intervalPtr
->offset 
- bbox
.descent
; 
 988     XDrawString(Tk_Display(tkwin
), Tk_WindowId(tkwin
), 
 989             intervalPtr
->textGC
, rightEdge 
- bbox
.rbearing
, 
 990             y
, valueString
, length
); 
 994  *-------------------------------------------------------------- 
 996  * DisplayHorizontalInterval -- 
 998  *      This procedure redraws the contents of a horizontal interval 
 999  *      window.  It is invoked as a do-when-idle handler, so it only 
1000  *      runs when there's nothing else for the application to do. 
1006  *      Information appears on the screen. 
1008  *-------------------------------------------------------------- 
1012 DisplayHorizontalInterval(clientData
) 
1013     ClientData clientData
;      /* Information about widget. */ 
1015     register Interval 
*intervalPtr 
= (Interval 
*) clientData
; 
1016     register Tk_Window tkwin 
= intervalPtr
->tkwin
; 
1017     int tickBottom
, valueBottom
, labelBottom
, intervalBottom
; 
1018     int totalPixels
, y
, width
, height
, tickValue
, min
, max
; 
1020     Tk_3DBorder sliderBorder
; 
1022     if ((intervalPtr
->tkwin 
== NULL
) || !Tk_IsMapped(tkwin
)) { 
1027      * Scanning from bottom to top across the window, the window 
1028      * will contain four rows:  ticks, value, interval, and label. 
1029      * Compute the y-coordinate for each of the rows. 
1032     totalPixels 
= intervalPtr
->tickPixels 
+ intervalPtr
->valuePixels
 
1033             + 2*intervalPtr
->borderWidth 
+ intervalPtr
->width
 
1034             + intervalPtr
->labelPixels
; 
1035     tickBottom 
= (Tk_Height(tkwin
) + totalPixels
)/2 - 1; 
1036     valueBottom 
= tickBottom 
- intervalPtr
->tickPixels
; 
1037     intervalBottom 
= valueBottom 
- intervalPtr
->valuePixels
; 
1038     labelBottom 
= intervalBottom 
- 2*intervalPtr
->borderWidth 
- intervalPtr
->width
; 
1041      * Display the information from bottom to top across the window. 
1044     if (intervalPtr
->flags 
& REDRAW_OTHER
) { 
1045         XClearWindow(Tk_Display(tkwin
), Tk_WindowId(tkwin
)); 
1048          * Display the tick marks. 
1051         if (intervalPtr
->tickPixels 
!= 0) { 
1052             for (tickValue 
= intervalPtr
->fromValue
; ; 
1053                     tickValue 
+= intervalPtr
->tickInterval
) { 
1054                 if (intervalPtr
->toValue 
> intervalPtr
->fromValue
) { 
1055                     if (tickValue 
> intervalPtr
->toValue
) { 
1059                     if (tickValue 
< intervalPtr
->toValue
) { 
1063                 DisplayHorizontalValue(intervalPtr
, tickValue
, tickBottom
); 
1069      * Display the values, if they are desired.  If not redisplaying the 
1070      * entire window, clear the area of the value to get rid of the 
1071      * old value displayed there. 
1074     if (intervalPtr
->showValue
) { 
1075         if (!(intervalPtr
->flags 
& REDRAW_OTHER
)) { 
1076             XClearArea(Tk_Display(tkwin
), Tk_WindowId(tkwin
), 
1077                     intervalPtr
->offset
, intervalBottom 
+ 1, 
1078                     Tk_Width(tkwin
) - 2*intervalPtr
->offset
, 
1079                     valueBottom 
- intervalBottom
, False
); 
1081         DisplayHorizontalValue(intervalPtr
, intervalPtr
->minValue
, valueBottom
); 
1082         DisplayHorizontalValue(intervalPtr
, intervalPtr
->maxValue
, valueBottom
); 
1086      * Display the interval and the slider.  If not redisplaying the 
1087      * entire window, must clear the trench area to erase the old 
1088      * slider, but don't need to redraw the border. 
1091     y 
= intervalBottom 
- 2*intervalPtr
->borderWidth 
- intervalPtr
->width 
+ 1; 
1092     if (intervalPtr
->flags 
& REDRAW_OTHER
) { 
1093         Tk_Draw3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
), 
1094                 intervalPtr
->bgBorder
, intervalPtr
->offset
, y
, 
1095                 Tk_Width(tkwin
) - 2*intervalPtr
->offset
, 
1096                 intervalPtr
->width 
+ 2*intervalPtr
->borderWidth
, 
1097                 intervalPtr
->borderWidth
, TK_RELIEF_SUNKEN
); 
1099         XClearArea(Tk_Display(tkwin
), Tk_WindowId(tkwin
), 
1100                 intervalPtr
->offset 
+ intervalPtr
->borderWidth
, 
1101                 y 
+ intervalPtr
->borderWidth
, 
1102                 Tk_Width(tkwin
) - 2*intervalPtr
->offset 
- 2*intervalPtr
->borderWidth
, 
1103                 intervalPtr
->width
, False
); 
1105     if (intervalPtr
->flags 
& ACTIVE
) { 
1106         sliderBorder 
= intervalPtr
->activeBorder
; 
1108         sliderBorder 
= intervalPtr
->sliderBorder
; 
1110     height 
= intervalPtr
->width
; 
1112     min 
= ValueToPixel(intervalPtr
, intervalPtr
->minValue
); 
1113     max 
= ValueToPixel(intervalPtr
, intervalPtr
->maxValue
); 
1115     width 
= (max 
- min
) + (2 * intervalPtr
->borderWidth
); 
1117     y 
+= intervalPtr
->borderWidth
; 
1118     relief 
= (intervalPtr
->flags 
& BUTTON_PRESSED
) ? TK_RELIEF_SUNKEN
 
1120     Tk_Fill3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
), sliderBorder
, 
1121             min
, y
, width
, height
, intervalPtr
->borderWidth
, relief
); 
1124      * Draw the label to the top of the interval. 
1127     if ((intervalPtr
->flags 
& REDRAW_OTHER
) && (intervalPtr
->labelPixels 
!= 0)) { 
1128         XDrawString(Tk_Display(intervalPtr
->tkwin
), Tk_WindowId(intervalPtr
->tkwin
), 
1129             intervalPtr
->textGC
, intervalPtr
->offset 
+ intervalPtr
->fontPtr
->ascent
/2, 
1130             labelBottom 
- intervalPtr
->fontPtr
->descent
, 
1131             intervalPtr
->label
, intervalPtr
->labelLength
); 
1135      * Draw the window border. 
1138     if ((intervalPtr
->flags 
& REDRAW_OTHER
) 
1139             && (intervalPtr
->relief 
!= TK_RELIEF_FLAT
)) { 
1140         Tk_Draw3DRectangle(Tk_Display(tkwin
), Tk_WindowId(tkwin
), 
1141                 intervalPtr
->bgBorder
, 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
), 
1142                 intervalPtr
->borderWidth
, intervalPtr
->relief
); 
1146     intervalPtr
->flags 
&= ~REDRAW_ALL
; 
1150  *---------------------------------------------------------------------- 
1152  * DisplayHorizontalValue -- 
1154  *      This procedure is called to display values (interval readings) 
1155  *      for horizontally-oriented intervals. 
1161  *      The numerical value corresponding to value is displayed with 
1162  *      its bottom edge at "bottom", and at a horizontal position in 
1163  *      the interval that corresponds to "value". 
1165  *---------------------------------------------------------------------- 
1169 DisplayHorizontalValue(intervalPtr
, value
, bottom
) 
1170     register Interval 
*intervalPtr
;     /* Information about widget in which to 
1172     int value
;                  /* Y-coordinate of number to display, 
1173                                  * specified in application coords, not 
1174                                  * in pixels (we'll compute pixels). */ 
1175     int bottom
;                 /* Y-coordinate of bottom edge of text, 
1176                                  * specified in pixels. */ 
1178     register Tk_Window tkwin 
= intervalPtr
->tkwin
; 
1179     int x
, y
, dummy
, length
; 
1180     char valueString
[30]; 
1183     x 
= ValueToPixel(intervalPtr
, value
); 
1184     y 
= bottom 
- intervalPtr
->fontPtr
->descent
; 
1185     sprintf(valueString
, "%d", value
); 
1186     length 
= strlen(valueString
); 
1187     XTextExtents(intervalPtr
->fontPtr
, valueString
, length
, 
1188             &dummy
, &dummy
, &dummy
, &bbox
); 
1191      * Adjust the x-coordinate if necessary to keep the text entirely 
1192      * inside the window. 
1195     x 
-= (bbox
.lbearing 
+ bbox
.rbearing
)/2; 
1196     if ((x 
- bbox
.lbearing
) < intervalPtr
->offset
) { 
1197         x 
= intervalPtr
->offset 
+ bbox
.lbearing
; 
1199     if ((x 
+ bbox
.rbearing
) > (Tk_Width(tkwin
) - intervalPtr
->offset
)) { 
1200         x 
= Tk_Width(tkwin
) - intervalPtr
->offset 
- bbox
.rbearing
; 
1202     XDrawString(Tk_Display(tkwin
), Tk_WindowId(tkwin
), 
1203             intervalPtr
->textGC
, x
, y
, valueString
, length
); 
1207  *---------------------------------------------------------------------- 
1211  *      Given a pixel within a interval window, return the interval 
1212  *      reading corresponding to that pixel. 
1215  *      An integer interval reading. 
1220  *---------------------------------------------------------------------- 
1224 PixelToValue(intervalPtr
, x
, y
) 
1225     register Interval 
*intervalPtr
;             /* Information about widget. */ 
1226     int x
, y
;                           /* Coordinates of point within 
1229     int value
, pixelRange
; 
1231     if (intervalPtr
->vertical
) { 
1232         pixelRange 
= Tk_Height(intervalPtr
->tkwin
) 
1233                 - 2*intervalPtr
->offset 
- 4*intervalPtr
->borderWidth
; 
1236         pixelRange 
= Tk_Width(intervalPtr
->tkwin
) 
1237                 - 2*intervalPtr
->offset 
- 4*intervalPtr
->borderWidth
; 
1241     if (pixelRange 
<= 0) { 
1243          * Not enough room for the slider to actually slide:  just return 
1249     value 
-= intervalPtr
->offset 
+ intervalPtr
->borderWidth
; 
1254     if (value 
> pixelRange
) { 
1258     if (intervalPtr
->toValue 
> intervalPtr
->fromValue
) { 
1259         value 
= intervalPtr
->fromValue 
+ 
1260                 ((value 
* (intervalPtr
->toValue 
- intervalPtr
->fromValue
)) 
1261                 + pixelRange
/2)/pixelRange
; 
1263         value 
= intervalPtr
->toValue 
+ 
1264                 (((pixelRange 
- value
) 
1265                 * (intervalPtr
->fromValue 
- intervalPtr
->toValue
)) 
1266                 + pixelRange
/2)/pixelRange
; 
1272  *---------------------------------------------------------------------- 
1276  *      Given a reading of the interval, return the x-coordinate or 
1277  *      y-coordinate corresponding to that reading, depending on 
1278  *      whether the interval is vertical or horizontal, respectively. 
1281  *      An integer value giving the pixel location corresponding 
1282  *      to reading.  The value is restricted to lie within the 
1283  *      defined range for the interval. 
1288  *---------------------------------------------------------------------- 
1292 ValueToPixel(intervalPtr
, value
) 
1293     register Interval 
*intervalPtr
;             /* Information about widget. */ 
1294     int value
;                          /* Reading of the widget. */ 
1296     int y
, pixelRange
, valueRange
; 
1298     valueRange 
= intervalPtr
->toValue 
- intervalPtr
->fromValue
; 
1299     pixelRange 
= (intervalPtr
->vertical 
? Tk_Height(intervalPtr
->tkwin
) 
1300                                         : Tk_Width(intervalPtr
->tkwin
)) 
1301             - 2*intervalPtr
->offset 
- 4*intervalPtr
->borderWidth
; 
1302     y 
= ((value 
- intervalPtr
->fromValue
) * pixelRange
 
1303             + valueRange
/2) / valueRange
; 
1306     } else if (y 
> pixelRange
) { 
1309     y 
+= intervalPtr
->offset 
+ intervalPtr
->borderWidth
; 
1314  *-------------------------------------------------------------- 
1316  * IntervalEventProc -- 
1318  *      This procedure is invoked by the Tk dispatcher for various 
1319  *      events on intervals. 
1325  *      When the window gets deleted, internal structures get 
1326  *      cleaned up.  When it gets exposed, it is redisplayed. 
1328  *-------------------------------------------------------------- 
1332 IntervalEventProc(clientData
, eventPtr
) 
1333     ClientData clientData
;      /* Information about window. */ 
1334     XEvent 
*eventPtr
;           /* Information about event. */ 
1336     Interval 
*intervalPtr 
= (Interval 
*) clientData
; 
1338     if ((eventPtr
->type 
== Expose
) && (eventPtr
->xexpose
.count 
== 0)) { 
1339         EventuallyRedrawInterval(intervalPtr
, REDRAW_ALL
); 
1340     } else if (eventPtr
->type 
== DestroyNotify
) { 
1341         Tcl_DeleteCommand(intervalPtr
->interp
, Tk_PathName(intervalPtr
->tkwin
)); 
1342         intervalPtr
->tkwin 
= NULL
; 
1343         if (intervalPtr
->flags 
& REDRAW_ALL
) { 
1344             if (intervalPtr
->vertical
) { 
1345                 Tk_CancelIdleCall(DisplayVerticalInterval
, (ClientData
) intervalPtr
); 
1347                 Tk_CancelIdleCall(DisplayHorizontalInterval
, 
1348                         (ClientData
) intervalPtr
); 
1351         Tk_EventuallyFree((ClientData
) intervalPtr
, DestroyInterval
); 
1352     } else if (eventPtr
->type 
== ConfigureNotify
) { 
1353         ComputeIntervalGeometry(intervalPtr
); 
1358  *-------------------------------------------------------------- 
1360  * IntervalMouseProc -- 
1362  *      This procedure is called back by Tk in response to 
1363  *      mouse events such as window entry, window exit, mouse 
1364  *      motion, and button presses. 
1370  *      This procedure implements the "feel" of the interval by 
1371  *      issuing commands in response to button presses and mouse 
1374  *-------------------------------------------------------------- 
1378 IntervalMouseProc(clientData
, eventPtr
) 
1379     ClientData clientData
;              /* Information about window. */ 
1380     register XEvent 
*eventPtr
;          /* Information about event. */ 
1382     register Interval 
*intervalPtr 
= (Interval 
*) clientData
; 
1384     if (intervalPtr
->state 
!= tkNormalUid
) { 
1388     Tk_Preserve((ClientData
) intervalPtr
); 
1389     if (eventPtr
->type 
== EnterNotify
) { 
1390         intervalPtr
->flags 
|= ACTIVE
; 
1391         EventuallyRedrawInterval(intervalPtr
, REDRAW_SLIDER
); 
1392     } else if (eventPtr
->type 
== LeaveNotify
) { 
1393         intervalPtr
->flags 
&= ~ACTIVE
; 
1394         EventuallyRedrawInterval(intervalPtr
, REDRAW_SLIDER
); 
1395     } else if ((eventPtr
->type 
== MotionNotify
) 
1396             && (intervalPtr
->flags 
& BUTTON_PRESSED
)) { 
1397         TrackInterval(intervalPtr
,  PixelToValue(intervalPtr
, 
1398                 eventPtr
->xmotion
.x
, eventPtr
->xmotion
.y
)); 
1399     } else if ((eventPtr
->type 
== ButtonPress
) 
1400 /*          && (eventPtr->xbutton.button == Button1) */ 
1401             && (eventPtr
->xbutton
.state 
== 0)) { 
1402         intervalPtr
->flags 
|= BUTTON_PRESSED
; 
1403         StartTrackInterval(intervalPtr
, PixelToValue(intervalPtr
, 
1404                 eventPtr
->xbutton
.x
, eventPtr
->xbutton
.y
)); 
1405 /*      NotifyInterval(intervalPtr); */ 
1406         EventuallyRedrawInterval(intervalPtr
, REDRAW_SLIDER
); 
1407     } else if ((eventPtr
->type 
== ButtonRelease
) 
1408 /*          && (eventPtr->xbutton.button == Button1) */ 
1409             && (intervalPtr
->flags 
& BUTTON_PRESSED
)) { 
1410         intervalPtr
->flags 
&= ~BUTTON_PRESSED
; 
1411         TrackInterval(intervalPtr
,  PixelToValue(intervalPtr
, 
1412                 eventPtr
->xmotion
.x
, eventPtr
->xmotion
.y
)); 
1413 /*      NotifyInterval(intervalPtr); */ 
1414         EventuallyRedrawInterval(intervalPtr
, REDRAW_SLIDER
); 
1416     Tk_Release((ClientData
) intervalPtr
); 
1420  *-------------------------------------------------------------- 
1424  *      This procedure changes the value of a interval and invokes 
1425  *      a Tcl command to reflect the current position of a interval 
1431  *      A Tcl command is invoked, and an additional error-processing 
1432  *      command may also be invoked.  The interval's slider is redrawn. 
1434  *-------------------------------------------------------------- 
1438 StartTrackInterval(intervalPtr
, value
) 
1439     register Interval 
*intervalPtr
;     /* Info about widget. */ 
1440     int value
;                  /* New value for interval.  Gets 
1441                                  * adjusted if it's off the interval. */ 
1443     if ((value 
< intervalPtr
->fromValue
) 
1444             ^ (intervalPtr
->toValue 
< intervalPtr
->fromValue
)) { 
1445         value 
= intervalPtr
->fromValue
; 
1447     if ((value 
> intervalPtr
->toValue
) 
1448             ^ (intervalPtr
->toValue 
< intervalPtr
->fromValue
)) { 
1449         value 
= intervalPtr
->toValue
; 
1451     intervalPtr
->trackValue 
= value
; 
1452     intervalPtr
->trackWidth 
= intervalPtr
->maxValue 
- intervalPtr
->minValue
; 
1453     if (value 
<= intervalPtr
->minValue
) { 
1454       intervalPtr
->trackState 
= -1; 
1455     } else if (value 
>= intervalPtr
->maxValue
) { 
1456       intervalPtr
->trackState 
= 1; 
1458       intervalPtr
->trackState 
= 0; 
1460     SetInterval(intervalPtr
, intervalPtr
->minValue
, intervalPtr
->maxValue
, 1); 
1465 TrackInterval(intervalPtr
, value
) 
1466     register Interval 
*intervalPtr
;     /* Info about widget. */ 
1469     int min
, max
, delta
, lastmin
, lastmax
; 
1472     delta 
= value 
- intervalPtr
->trackValue
; 
1473     if (delta 
== 0) return; 
1475     intervalPtr
->trackValue 
= value
; 
1477     min 
= intervalPtr
->minValue
; 
1478     max 
= intervalPtr
->maxValue
; 
1480     switch (intervalPtr
->trackState
) { 
1481     case -1: /* left trench */ 
1483       if (min 
> max
) max 
= min
; 
1485     case 1: /* right trench */ 
1487       if (min 
> max
) min 
= max
; 
1489     case 0: /* center slider */ 
1490       lastmin 
= min
; lastmax 
= max
; 
1491       min 
+= delta
; max 
+= delta
; 
1492       if ((max 
- min
) != intervalPtr
->trackWidth
) { /* squished */ 
1493         if (lastmin 
== intervalPtr
->fromValue
) { 
1494           min 
= max 
- intervalPtr
->trackWidth
; 
1495         } else if (lastmax 
== intervalPtr
->toValue
) { 
1496           max 
= min 
+ intervalPtr
->trackWidth
; 
1502     SetInterval(intervalPtr
, min
, max
, 1); 
1507 SetInterval(intervalPtr
, min
, max
, notify
) 
1508     register Interval 
*intervalPtr
;     /* Info about widget. */ 
1509     int min
, max
, notify
; 
1517     if ((min 
< intervalPtr
->fromValue
) 
1518             ^ (intervalPtr
->toValue 
< intervalPtr
->fromValue
)) { 
1519         min 
= intervalPtr
->fromValue
; 
1521     if ((min 
> intervalPtr
->toValue
) 
1522             ^ (intervalPtr
->toValue 
< intervalPtr
->fromValue
)) { 
1523         min 
= intervalPtr
->toValue
; 
1525     if ((max 
< intervalPtr
->fromValue
) 
1526             ^ (intervalPtr
->toValue 
< intervalPtr
->fromValue
)) { 
1527         max 
= intervalPtr
->fromValue
; 
1529     if ((max 
> intervalPtr
->toValue
) 
1530             ^ (intervalPtr
->toValue 
< intervalPtr
->fromValue
)) { 
1531         max 
= intervalPtr
->toValue
; 
1534     if ((min 
== intervalPtr
->minValue
) && 
1535         (max 
== intervalPtr
->maxValue
)) { 
1539     intervalPtr
->minValue 
= min
; 
1540     intervalPtr
->maxValue 
= max
; 
1541     EventuallyRedrawInterval(intervalPtr
, REDRAW_SLIDER
); 
1544       NotifyInterval(intervalPtr
); 
1549 NotifyInterval(register Interval 
*intervalPtr
) 
1554     sprintf(string
, " %d %d", intervalPtr
->minValue
, intervalPtr
->maxValue
); 
1555     result 
= Tcl_VarEval(intervalPtr
->interp
, intervalPtr
->command
, string
, 
1557     if (result 
!= TCL_OK
) { 
1558         TkBindError(intervalPtr
->interp
); 
1565  *-------------------------------------------------------------- 
1567  * EventuallyRedrawInterval -- 
1569  *      Arrange for part or all of a interval widget to redrawn at 
1570  *      the next convenient time in the future. 
1576  *      If "what" is REDRAW_SLIDER then just the slider and the 
1577  *      value readout will be redrawn;  if "what" is REDRAW_ALL 
1578  *      then the entire widget will be redrawn. 
1580  *-------------------------------------------------------------- 
1584 EventuallyRedrawInterval(intervalPtr
, what
) 
1585     register Interval 
*intervalPtr
;     /* Information about widget. */ 
1586     int what
;                   /* What to redraw:  REDRAW_SLIDER 
1589     if ((what 
== 0) || (intervalPtr
->tkwin 
== NULL
) 
1590             || !Tk_IsMapped(intervalPtr
->tkwin
)) { 
1593     if ((intervalPtr
->flags 
& REDRAW_ALL
) == 0) { 
1594         if (intervalPtr
->vertical
) { 
1595             Tk_DoWhenIdle(DisplayVerticalInterval
, (ClientData
) intervalPtr
); 
1597             Tk_DoWhenIdle(DisplayHorizontalInterval
, (ClientData
) intervalPtr
); 
1600     intervalPtr
->flags 
|= what
;