home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / tcl / tk3.3b1 / tkScrollbar.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-16  |  36.8 KB  |  1,205 lines

  1. /* 
  2.  * tkScrollbar.c --
  3.  *
  4.  *    This module implements a scrollbar widgets for the Tk
  5.  *    toolkit.  A scrollbar displays a slider and two arrows;
  6.  *    mouse clicks on features within the scrollbar cause
  7.  *    scrolling commands to be invoked.
  8.  *
  9.  * Copyright (c) 1990-1993 The Regents of the University of California.
  10.  * All rights reserved.
  11.  *
  12.  * Permission is hereby granted, without written agreement and without
  13.  * license or royalty fees, to use, copy, modify, and distribute this
  14.  * software and its documentation for any purpose, provided that the
  15.  * above copyright notice and the following two paragraphs appear in
  16.  * all copies of this software.
  17.  * 
  18.  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  19.  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  20.  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
  21.  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  22.  *
  23.  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  24.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  25.  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  26.  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
  27.  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  28.  */
  29.  
  30. #ifndef lint
  31. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkScrollbar.c,v 1.42 93/06/16 17:14:57 ouster Exp $ SPRITE (Berkeley)";
  32. #endif
  33.  
  34. #include "tkConfig.h"
  35. #include "default.h"
  36. #include "tkInt.h"
  37.  
  38. /*
  39.  * A data structure of the following type is kept for each scrollbar
  40.  * widget managed by this file:
  41.  */
  42.  
  43. typedef struct {
  44.     Tk_Window tkwin;        /* Window that embodies the scrollbar.  NULL
  45.                  * means that the window has been destroyed
  46.                  * but the data structures haven't yet been
  47.                  * cleaned up.*/
  48.     Display *display;        /* Display containing widget.  Used, among
  49.                  * other things, so that resources can be
  50.                  * freed even after tkwin has gone away. */
  51.     Tcl_Interp *interp;        /* Interpreter associated with scrollbar. */
  52.     Tk_Uid orientUid;        /* Orientation for window ("vertical" or
  53.                  * "horizontal"). */
  54.     int vertical;        /* Non-zero means vertical orientation
  55.                  * requested, zero means horizontal. */
  56.     int width;            /* Desired narrow dimension of scrollbar,
  57.                  * in pixels. */
  58.     char *command;        /* Command prefix to use when invoking
  59.                  * scrolling commands.  NULL means don't
  60.                  * invoke commands.  Malloc'ed. */
  61.     int commandSize;        /* Number of non-NULL bytes in command. */
  62.     int repeatDelay;        /* How long to wait before auto-repeating
  63.                  * on scrolling actions (in ms). */
  64.     int repeatInterval;        /* Interval between autorepeats (in ms). */
  65.  
  66.     /*
  67.      * Information used when displaying widget:
  68.      */
  69.  
  70.     int borderWidth;        /* Width of 3-D borders. */
  71.     Tk_3DBorder bgBorder;    /* Used for drawing background. */
  72.     Tk_3DBorder fgBorder;    /* For drawing foreground shapes. */
  73.     Tk_3DBorder activeBorder;    /* For drawing foreground shapes when
  74.                  * active (i.e. when mouse is positioned
  75.                  * over element).  NULL means use fgBorder. */
  76.     GC copyGC;            /* Used for copying from pixmap onto screen. */
  77.     int relief;            /* Indicates whether window as a whole is
  78.                  * raised, sunken, or flat. */
  79.     int offset;            /* Zero if relief is TK_RELIEF_FLAT,
  80.                  * borderWidth otherwise.   Indicates how
  81.                  * much interior stuff must be offset from
  82.                  * outside edges to leave room for border. */
  83.     int arrowLength;        /* Length of arrows along long dimension of
  84.                  * scrollbar.  Recomputed on window size
  85.                  * changes. */
  86.     int sliderFirst;        /* Pixel coordinate of top or left edge
  87.                  * of slider area, including border. */
  88.     int sliderLast;        /* Coordinate of pixel just after bottom
  89.                  * or right edge of slider area, including
  90.                  * border. */
  91.     int mouseField;        /* Indicates which scrollbar element is
  92.                  * under mouse (e.g. TOP_ARROW;  see below
  93.                  * for possible values). */
  94.     int pressField;        /* Field in which button was pressed, or -1
  95.                  * if no button is down. */
  96.     int pressPos;        /* Position of mouse when button was
  97.                  * pressed (y for vertical scrollbar, x
  98.                  * for horizontal). */
  99.     int pressFirstUnit;        /* Value of "firstUnit" when mouse button
  100.                  * was pressed. */
  101.  
  102.     /*
  103.      * Information describing the application related to the scrollbar.
  104.      * This information is provided by the application by invoking the
  105.      * "set" widget command.
  106.      */
  107.  
  108.     int totalUnits;        /* Total dimension of application, in
  109.                  * units. */
  110.     int windowUnits;        /* Maximum number of units that can
  111.                  * be displayed in the window at
  112.                  * once. */
  113.     int firstUnit;        /* Number of last unit visible in
  114.                  * application's window. */
  115.     int lastUnit;        /* Index of last unit visible in window. */
  116.  
  117.     /*
  118.      * Miscellaneous information:
  119.      */
  120.  
  121.     Cursor cursor;        /* Current cursor for window, or None. */
  122.     Tk_TimerToken autoRepeat;    /* Token for auto-repeat that's
  123.                  * currently in progress.  NULL means no
  124.                  * auto-repeat in progress. */
  125.     int flags;            /* Various flags;  see below for
  126.                  * definitions. */
  127. } Scrollbar;
  128.  
  129. /*
  130.  * Legal values for "mouseField" field of Scrollbar structures.  These
  131.  * are also the return values from the ScrollbarPosition procedure.
  132.  */
  133.  
  134. #define TOP_ARROW    1
  135. #define TOP_GAP        2
  136. #define SLIDER        3
  137. #define BOTTOM_GAP    4
  138. #define BOTTOM_ARROW    5
  139. #define OUTSIDE        6
  140.  
  141. /*
  142.  * Flag bits for scrollbars:
  143.  * 
  144.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler
  145.  *                has already been queued to redraw
  146.  *                this window.
  147.  */
  148.  
  149. #define REDRAW_PENDING        1
  150.  
  151. /*
  152.  * Information used for argv parsing.
  153.  */
  154.  
  155.  
  156. static Tk_ConfigSpec configSpecs[] = {
  157.     {TK_CONFIG_BORDER, "-activeforeground", "activeForeground", "Background",
  158.     DEF_SCROLLBAR_ACTIVE_FG_COLOR, Tk_Offset(Scrollbar, activeBorder),
  159.     TK_CONFIG_COLOR_ONLY},
  160.     {TK_CONFIG_BORDER, "-activeforeground", "activeForeground", "Background",
  161.     DEF_SCROLLBAR_ACTIVE_FG_MONO, Tk_Offset(Scrollbar, activeBorder),
  162.     TK_CONFIG_MONO_ONLY},
  163.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  164.     DEF_SCROLLBAR_BG_COLOR, Tk_Offset(Scrollbar, bgBorder),
  165.     TK_CONFIG_COLOR_ONLY},
  166.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  167.     DEF_SCROLLBAR_BG_MONO, Tk_Offset(Scrollbar, bgBorder),
  168.     TK_CONFIG_MONO_ONLY},
  169.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  170.     (char *) NULL, 0, 0},
  171.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  172.     (char *) NULL, 0, 0},
  173.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  174.     DEF_SCROLLBAR_BORDER_WIDTH, Tk_Offset(Scrollbar, borderWidth), 0},
  175.     {TK_CONFIG_STRING, "-command", "command", "Command",
  176.     DEF_SCROLLBAR_COMMAND, Tk_Offset(Scrollbar, command),
  177.     TK_CONFIG_NULL_OK},
  178.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  179.     DEF_SCROLLBAR_CURSOR, Tk_Offset(Scrollbar, cursor), TK_CONFIG_NULL_OK},
  180.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  181.     (char *) NULL, 0, 0},
  182.     {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground",
  183.     DEF_SCROLLBAR_FG_COLOR, Tk_Offset(Scrollbar, fgBorder),
  184.     TK_CONFIG_COLOR_ONLY},
  185.     {TK_CONFIG_BORDER, "-foreground", "foreground", "Foreground",
  186.     DEF_SCROLLBAR_FG_MONO, Tk_Offset(Scrollbar, fgBorder),
  187.     TK_CONFIG_MONO_ONLY},
  188.     {TK_CONFIG_UID, "-orient", "orient", "Orient",
  189.     DEF_SCROLLBAR_ORIENT, Tk_Offset(Scrollbar, orientUid), 0},
  190.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  191.     DEF_SCROLLBAR_RELIEF, Tk_Offset(Scrollbar, relief), 0},
  192.     {TK_CONFIG_INT, "-repeatdelay", "repeatDelay", "RepeatDelay",
  193.     DEF_SCROLLBAR_REPEAT_DELAY, Tk_Offset(Scrollbar, repeatDelay), 0},
  194.     {TK_CONFIG_INT, "-repeatinterval", "repeatInterval", "RepeatInterval",
  195.     DEF_SCROLLBAR_REPEAT_INTERVAL, Tk_Offset(Scrollbar, repeatInterval), 0},
  196.     {TK_CONFIG_PIXELS, "-width", "width", "Width",
  197.     DEF_SCROLLBAR_WIDTH, Tk_Offset(Scrollbar, width), 0},
  198.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  199.     (char *) NULL, 0, 0}
  200. };
  201.  
  202. /*
  203.  * Forward declarations for procedures defined later in this file:
  204.  */
  205.  
  206. static void        ComputeScrollbarGeometry _ANSI_ARGS_((
  207.                 Scrollbar *scrollPtr));
  208. static int        ConfigureScrollbar _ANSI_ARGS_((Tcl_Interp *interp,
  209.                 Scrollbar *scrollPtr, int argc, char **argv,
  210.                 int flags));
  211. static void        DestroyScrollbar _ANSI_ARGS_((ClientData clientData));
  212. static void        DisplayScrollbar _ANSI_ARGS_((ClientData clientData));
  213. static void        EventuallyRedraw _ANSI_ARGS_((Scrollbar *scrollPtr));
  214. static void        ScrollbarEventProc _ANSI_ARGS_((ClientData clientData,
  215.                 XEvent *eventPtr));
  216. static void        ScrollbarMouseProc _ANSI_ARGS_((ClientData clientData,
  217.                 XEvent *eventPtr));
  218. static void        ScrollbarNewField _ANSI_ARGS_((Scrollbar *scrollPtr,
  219.                 int field));
  220. static int        ScrollbarPosition _ANSI_ARGS_((Scrollbar *scrollPtr,
  221.                 int x, int y));
  222. static void        ScrollbarTimerProc _ANSI_ARGS_((
  223.                 ClientData clientData));
  224. static int        ScrollbarWidgetCmd _ANSI_ARGS_((ClientData clientData,
  225.                 Tcl_Interp *, int argc, char **argv));
  226. static void        ScrollCmd _ANSI_ARGS_((Scrollbar *scrollPtr,
  227.                 int unit));
  228.  
  229. /*
  230.  *--------------------------------------------------------------
  231.  *
  232.  * Tk_ScrollbarCmd --
  233.  *
  234.  *    This procedure is invoked to process the "scrollbar" Tcl
  235.  *    command.  See the user documentation for details on what
  236.  *    it does.
  237.  *
  238.  * Results:
  239.  *    A standard Tcl result.
  240.  *
  241.  * Side effects:
  242.  *    See the user documentation.
  243.  *
  244.  *--------------------------------------------------------------
  245.  */
  246.  
  247. int
  248. Tk_ScrollbarCmd(clientData, interp, argc, argv)
  249.     ClientData clientData;    /* Main window associated with
  250.                  * interpreter. */
  251.     Tcl_Interp *interp;        /* Current interpreter. */
  252.     int argc;            /* Number of arguments. */
  253.     char **argv;        /* Argument strings. */
  254. {
  255.     Tk_Window tkwin = (Tk_Window) clientData;
  256.     register Scrollbar *scrollPtr;
  257.     Tk_Window new;
  258.  
  259.     if (argc < 2) {
  260.     Tcl_AppendResult(interp, "wrong # args:  should be \"",
  261.         argv[0], " pathName ?options?\"", (char *) NULL);
  262.     return TCL_ERROR;
  263.     }
  264.  
  265.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  266.     if (new == NULL) {
  267.     return TCL_ERROR;
  268.     }
  269.  
  270.     /*
  271.      * Initialize fields that won't be initialized by ConfigureScrollbar,
  272.      * or which ConfigureScrollbar expects to have reasonable values
  273.      * (e.g. resource pointers).
  274.      */
  275.  
  276.     scrollPtr = (Scrollbar *) ckalloc(sizeof(Scrollbar));
  277.     scrollPtr->tkwin = new;
  278.     scrollPtr->display = Tk_Display(new);
  279.     scrollPtr->interp = interp;
  280.     scrollPtr->copyGC = None;
  281.     scrollPtr->mouseField = OUTSIDE;
  282.     scrollPtr->pressField = -1;
  283.     scrollPtr->totalUnits = 0;
  284.     scrollPtr->windowUnits = 0;
  285.     scrollPtr->firstUnit = 0;
  286.     scrollPtr->lastUnit = 0;
  287.     scrollPtr->autoRepeat = NULL;
  288.     scrollPtr->flags = 0;
  289.  
  290.     Tk_SetClass(scrollPtr->tkwin, "Scrollbar");
  291.     Tk_CreateEventHandler(scrollPtr->tkwin, ExposureMask|StructureNotifyMask,
  292.         ScrollbarEventProc, (ClientData) scrollPtr);
  293.     Tk_CreateEventHandler(scrollPtr->tkwin, EnterWindowMask|LeaveWindowMask
  294.         |PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
  295.         ScrollbarMouseProc, (ClientData) scrollPtr);
  296.     Tcl_CreateCommand(interp, Tk_PathName(scrollPtr->tkwin), ScrollbarWidgetCmd,
  297.         (ClientData) scrollPtr, (void (*)()) NULL);
  298.     if (ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2, 0) != TCL_OK) {
  299.     goto error;
  300.     }
  301.  
  302.     interp->result = Tk_PathName(scrollPtr->tkwin);
  303.     return TCL_OK;
  304.  
  305.     error:
  306.     Tk_DestroyWindow(scrollPtr->tkwin);
  307.     return TCL_ERROR;
  308. }
  309.  
  310. /*
  311.  *--------------------------------------------------------------
  312.  *
  313.  * ScrollbarWidgetCmd --
  314.  *
  315.  *    This procedure is invoked to process the Tcl command
  316.  *    that corresponds to a widget managed by this module.
  317.  *    See the user documentation for details on what it does.
  318.  *
  319.  * Results:
  320.  *    A standard Tcl result.
  321.  *
  322.  * Side effects:
  323.  *    See the user documentation.
  324.  *
  325.  *--------------------------------------------------------------
  326.  */
  327.  
  328. static int
  329. ScrollbarWidgetCmd(clientData, interp, argc, argv)
  330.     ClientData clientData;    /* Information about scrollbar
  331.                      * widget. */
  332.     Tcl_Interp *interp;            /* Current interpreter. */
  333.     int argc;                /* Number of arguments. */
  334.     char **argv;            /* Argument strings. */
  335. {
  336.     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
  337.     int result = TCL_OK;
  338.     int length;
  339.     char c;
  340.  
  341.     if (argc < 2) {
  342.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  343.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  344.     return TCL_ERROR;
  345.     }
  346.     Tk_Preserve((ClientData) scrollPtr);
  347.     c = argv[1][0];
  348.     length = strlen(argv[1]);
  349.     if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  350.     if (argc == 2) {
  351.         result = Tk_ConfigureInfo(interp, scrollPtr->tkwin, configSpecs,
  352.             (char *) scrollPtr, (char *) NULL, 0);
  353.     } else if (argc == 3) {
  354.         result = Tk_ConfigureInfo(interp, scrollPtr->tkwin, configSpecs,
  355.             (char *) scrollPtr, argv[2], 0);
  356.     } else {
  357.         result = ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2,
  358.             TK_CONFIG_ARGV_ONLY);
  359.     }
  360.     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
  361.     if (argc != 2) {
  362.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  363.             argv[0], " get\"", (char *) NULL);
  364.         goto error;
  365.     }
  366.     sprintf(interp->result, "%d %d %d %d", scrollPtr->totalUnits,
  367.         scrollPtr->windowUnits, scrollPtr->firstUnit,
  368.         scrollPtr->lastUnit);
  369.     } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
  370.     int totalUnits, windowUnits, firstUnit, lastUnit;
  371.  
  372.     if (argc != 6) {
  373.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  374.             argv[0],
  375.             " set totalUnits windowUnits firstUnit lastUnit\"",
  376.             (char *) NULL);
  377.         goto error;
  378.     }
  379.     if (Tcl_GetInt(interp, argv[2], &totalUnits) != TCL_OK) {
  380.         goto error;
  381.     }
  382.     if (totalUnits < 0) {
  383.         sprintf(interp->result, "illegal totalUnits %d", totalUnits);
  384.         goto error;
  385.     }
  386.     if (Tcl_GetInt(interp, argv[3], &windowUnits) != TCL_OK) {
  387.         goto error;
  388.     }
  389.     if (windowUnits < 0) {
  390.         sprintf(interp->result, "illegal windowUnits %d", windowUnits);
  391.         goto error;
  392.     }
  393.     if (Tcl_GetInt(interp, argv[4], &firstUnit) != TCL_OK) {
  394.         goto error;
  395.     }
  396.     if (Tcl_GetInt(interp, argv[5], &lastUnit) != TCL_OK) {
  397.         goto error;
  398.     }
  399.     if (totalUnits > 0) {
  400.         if (lastUnit < firstUnit) {
  401.         sprintf(interp->result, "illegal lastUnit %d", lastUnit);
  402.         goto error;
  403.         }
  404.     } else {
  405.         firstUnit = lastUnit = 0;
  406.     }
  407.     scrollPtr->totalUnits = totalUnits;
  408.     scrollPtr->windowUnits = windowUnits;
  409.     scrollPtr->firstUnit = firstUnit;
  410.     scrollPtr->lastUnit = lastUnit;
  411.     ComputeScrollbarGeometry(scrollPtr);
  412.     EventuallyRedraw(scrollPtr);
  413.     } else {
  414.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  415.         "\":  must be configure, get, or set", (char *) NULL);
  416.     goto error;
  417.     }
  418.     Tk_Release((ClientData) scrollPtr);
  419.     return result;
  420.  
  421.     error:
  422.     Tk_Release((ClientData) scrollPtr);
  423.     return TCL_ERROR;
  424. }
  425.  
  426. /*
  427.  *----------------------------------------------------------------------
  428.  *
  429.  * DestroyScrollbar --
  430.  *
  431.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  432.  *    to clean up the internal structure of a scrollbar at a safe time
  433.  *    (when no-one is using it anymore).
  434.  *
  435.  * Results:
  436.  *    None.
  437.  *
  438.  * Side effects:
  439.  *    Everything associated with the scrollbar is freed up.
  440.  *
  441.  *----------------------------------------------------------------------
  442.  */
  443.  
  444. static void
  445. DestroyScrollbar(clientData)
  446.     ClientData clientData;    /* Info about scrollbar widget. */
  447. {
  448.     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
  449.  
  450.     /*
  451.      * Free up all the stuff that requires special handling, then
  452.      * let Tk_FreeOptions handle all the standard option-related
  453.      * stuff.
  454.      */
  455.  
  456.     if (scrollPtr->copyGC != None) {
  457.     Tk_FreeGC(scrollPtr->display, scrollPtr->copyGC);
  458.     }
  459.     Tk_FreeOptions(configSpecs, (char *) scrollPtr, scrollPtr->display, 0);
  460.     ckfree((char *) scrollPtr);
  461. }
  462.  
  463. /*
  464.  *----------------------------------------------------------------------
  465.  *
  466.  * ConfigureScrollbar --
  467.  *
  468.  *    This procedure is called to process an argv/argc list, plus
  469.  *    the Tk option database, in order to configure (or
  470.  *    reconfigure) a scrollbar widget.
  471.  *
  472.  * Results:
  473.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  474.  *    returned, then interp->result contains an error message.
  475.  *
  476.  * Side effects:
  477.  *    Configuration information, such as colors, border width,
  478.  *    etc. get set for scrollPtr;  old resources get freed,
  479.  *    if there were any.
  480.  *
  481.  *----------------------------------------------------------------------
  482.  */
  483.  
  484. static int
  485. ConfigureScrollbar(interp, scrollPtr, argc, argv, flags)
  486.     Tcl_Interp *interp;            /* Used for error reporting. */
  487.     register Scrollbar *scrollPtr;    /* Information about widget;  may or
  488.                      * may not already have values for
  489.                      * some fields. */
  490.     int argc;                /* Number of valid entries in argv. */
  491.     char **argv;            /* Arguments. */
  492.     int flags;                /* Flags to pass to
  493.                      * Tk_ConfigureWidget. */
  494. {
  495.     int length;
  496.     XGCValues gcValues;
  497.  
  498.     if (Tk_ConfigureWidget(interp, scrollPtr->tkwin, configSpecs,
  499.         argc, argv, (char *) scrollPtr, flags) != TCL_OK) {
  500.     return TCL_ERROR;
  501.     }
  502.  
  503.     /*
  504.      * A few options need special processing, such as parsing the
  505.      * orientation or setting the background from a 3-D border.
  506.      */
  507.  
  508.     length = strlen(scrollPtr->orientUid);
  509.     if (strncmp(scrollPtr->orientUid, "vertical", length) == 0) {
  510.     scrollPtr->vertical = 1;
  511.     } else if (strncmp(scrollPtr->orientUid, "horizontal", length) == 0) {
  512.     scrollPtr->vertical = 0;
  513.     } else {
  514.     Tcl_AppendResult(interp, "bad orientation \"", scrollPtr->orientUid,
  515.         "\": must be vertical or horizontal", (char *) NULL);
  516.     return TCL_ERROR;
  517.     }
  518.  
  519.     if (scrollPtr->command != NULL) {
  520.     scrollPtr->commandSize = strlen(scrollPtr->command);
  521.     } else {
  522.     scrollPtr->commandSize = 0;
  523.     }
  524.  
  525.     Tk_SetBackgroundFromBorder(scrollPtr->tkwin, scrollPtr->bgBorder);
  526.  
  527.     if (scrollPtr->copyGC == None) {
  528.     gcValues.graphics_exposures = False;
  529.     scrollPtr->copyGC = Tk_GetGC(scrollPtr->tkwin, GCGraphicsExposures,
  530.         &gcValues);
  531.     }
  532.  
  533.     /*
  534.      * Register the desired geometry for the window (leave enough space
  535.      * for the two arrows plus a minimum-size slider, plus border around
  536.      * the whole window, if any).  Then arrange for the window to be
  537.      * redisplayed.
  538.      */
  539.  
  540.     ComputeScrollbarGeometry(scrollPtr);
  541.     EventuallyRedraw(scrollPtr);
  542.     return TCL_OK;
  543. }
  544.  
  545. /*
  546.  *--------------------------------------------------------------
  547.  *
  548.  * DisplayScrollbar --
  549.  *
  550.  *    This procedure redraws the contents of a scrollbar window.
  551.  *    It is invoked as a do-when-idle handler, so it only runs
  552.  *    when there's nothing else for the application to do.
  553.  *
  554.  * Results:
  555.  *    None.
  556.  *
  557.  * Side effects:
  558.  *    Information appears on the screen.
  559.  *
  560.  *--------------------------------------------------------------
  561.  */
  562.  
  563. static void
  564. DisplayScrollbar(clientData)
  565.     ClientData clientData;    /* Information about window. */
  566. {
  567.     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
  568.     register Tk_Window tkwin = scrollPtr->tkwin;
  569.     XPoint points[7];
  570.     Tk_3DBorder border;
  571.     int relief, width, fieldLength;
  572.     Pixmap pixmap;
  573.  
  574.     if ((scrollPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  575.     goto done;
  576.     }
  577.  
  578.     if (scrollPtr->vertical) {
  579.     width = Tk_Width(tkwin) - 2*scrollPtr->offset;
  580.     } else {
  581.     width = Tk_Height(tkwin) - 2*scrollPtr->offset;
  582.     }
  583.  
  584.     /*
  585.      * In order to avoid screen flashes, this procedure redraws
  586.      * the scrollbar in a pixmap, then copies the pixmap to the
  587.      * screen in a single operation.  This means that there's no
  588.      * point in time where the on-sreen image has been cleared.
  589.      */
  590.  
  591.     pixmap = XCreatePixmap(scrollPtr->display, Tk_WindowId(tkwin),
  592.         Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  593.                
  594.     Tk_Fill3DRectangle(scrollPtr->display, pixmap, scrollPtr->bgBorder,
  595.         0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  596.         scrollPtr->borderWidth, scrollPtr->relief);
  597.  
  598.     /*
  599.      * Draw the top or left arrow.  The coordinates of the polygon
  600.      * points probably seem odd, but they were carefully chosen with
  601.      * respect to X's rules for filling polygons.  These point choices
  602.      * cause the arrows to just fill the narrow dimension of the
  603.      * scrollbar and be properly centered.
  604.      */
  605.  
  606.     if (scrollPtr->mouseField == TOP_ARROW) {
  607.     border = scrollPtr->activeBorder;
  608.     relief = scrollPtr->pressField == TOP_ARROW ? TK_RELIEF_SUNKEN
  609.         : TK_RELIEF_RAISED;
  610.     } else {
  611.     border = scrollPtr->fgBorder;
  612.     relief = TK_RELIEF_RAISED;
  613.     }
  614.     if (scrollPtr->vertical) {
  615.     points[0].x = scrollPtr->offset - 1;
  616.     points[0].y = scrollPtr->arrowLength + scrollPtr->offset;
  617.     points[1].x = width + scrollPtr->offset;
  618.     points[1].y = points[0].y;
  619.     points[2].x = width/2 + scrollPtr->offset;
  620.     points[2].y = scrollPtr->offset - 1;
  621.     Tk_Fill3DPolygon(scrollPtr->display, pixmap, border,
  622.         points, 3, scrollPtr->borderWidth, relief);
  623.     } else {
  624.     points[0].x = scrollPtr->arrowLength + scrollPtr->offset;
  625.     points[0].y = scrollPtr->offset - 1;
  626.     points[1].x = scrollPtr->offset;
  627.     points[1].y = width/2 + scrollPtr->offset;
  628.     points[2].x = points[0].x;
  629.     points[2].y = width + scrollPtr->offset;
  630.     Tk_Fill3DPolygon(scrollPtr->display, pixmap, border,
  631.         points, 3, scrollPtr->borderWidth, relief);
  632.     }
  633.  
  634.     /*
  635.      * Display the bottom or right arrow.
  636.      */
  637.  
  638.     if (scrollPtr->mouseField == BOTTOM_ARROW) {
  639.     border = scrollPtr->activeBorder;
  640.     relief = scrollPtr->pressField == BOTTOM_ARROW ? TK_RELIEF_SUNKEN
  641.         : TK_RELIEF_RAISED;
  642.     } else {
  643.     border = scrollPtr->fgBorder;
  644.     relief = TK_RELIEF_RAISED;
  645.     }
  646.     if (scrollPtr->vertical) {
  647.     points[0].x = scrollPtr->offset;
  648.     points[0].y = Tk_Height(tkwin) - scrollPtr->arrowLength
  649.         - scrollPtr->offset;
  650.     points[1].x = width/2 + scrollPtr->offset;
  651.     points[1].y = Tk_Height(tkwin) - scrollPtr->offset;
  652.     points[2].x = width + scrollPtr->offset;
  653.     points[2].y = points[0].y;
  654.     Tk_Fill3DPolygon(scrollPtr->display, pixmap, border,
  655.         points, 3, scrollPtr->borderWidth, relief);
  656.     } else {
  657.     points[0].x = Tk_Width(tkwin) - scrollPtr->arrowLength
  658.         - scrollPtr->offset;
  659.     points[0].y = scrollPtr->offset - 1;
  660.     points[1].x = points[0].x;
  661.     points[1].y = width + scrollPtr->offset;
  662.     points[2].x = Tk_Width(tkwin) - scrollPtr->offset;
  663.     points[2].y = width/2 + scrollPtr->offset;
  664.     Tk_Fill3DPolygon(scrollPtr->display, pixmap, border,
  665.         points, 3, scrollPtr->borderWidth, relief);
  666.     }
  667.  
  668.     /*
  669.      * Display the slider.
  670.      */
  671.  
  672.     if (scrollPtr->mouseField == SLIDER) {
  673.     border = scrollPtr->activeBorder;
  674.     relief = scrollPtr->pressField == SLIDER ? TK_RELIEF_SUNKEN
  675.         : TK_RELIEF_RAISED;
  676.     } else {
  677.     border = scrollPtr->fgBorder;
  678.     relief = TK_RELIEF_RAISED;
  679.     }
  680.     fieldLength = (scrollPtr->vertical ? Tk_Height(tkwin) : Tk_Width(tkwin))
  681.         - 2*(scrollPtr->arrowLength + scrollPtr->offset);
  682.     if (fieldLength < 0) {
  683.     fieldLength = 0;
  684.     }
  685.     if (scrollPtr->vertical) {
  686.     Tk_Fill3DRectangle(scrollPtr->display, pixmap, border,
  687.         1 + scrollPtr->offset, scrollPtr->sliderFirst,
  688.         width-2, scrollPtr->sliderLast - scrollPtr->sliderFirst,
  689.         scrollPtr->borderWidth, relief);
  690.     } else {
  691.     Tk_Fill3DRectangle(scrollPtr->display, pixmap, border,
  692.         scrollPtr->sliderFirst, 1 + scrollPtr->offset,
  693.         scrollPtr->sliderLast - scrollPtr->sliderFirst, width-2,
  694.         scrollPtr->borderWidth, relief);
  695.     }
  696.  
  697.     /*
  698.      * Copy the information from the off-screen pixmap onto the screen,
  699.      * then delete the pixmap.
  700.      */
  701.  
  702.     XCopyArea(scrollPtr->display, pixmap, Tk_WindowId(tkwin),
  703.     scrollPtr->copyGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin), 0, 0);
  704.     XFreePixmap(scrollPtr->display, pixmap);
  705.  
  706.     done:
  707.     scrollPtr->flags &= ~REDRAW_PENDING;
  708. }
  709.  
  710. /*
  711.  *--------------------------------------------------------------
  712.  *
  713.  * ScrollbarEventProc --
  714.  *
  715.  *    This procedure is invoked by the Tk dispatcher for various
  716.  *    events on scrollbars.
  717.  *
  718.  * Results:
  719.  *    None.
  720.  *
  721.  * Side effects:
  722.  *    When the window gets deleted, internal structures get
  723.  *    cleaned up.  When it gets exposed, it is redisplayed.
  724.  *
  725.  *--------------------------------------------------------------
  726.  */
  727.  
  728. static void
  729. ScrollbarEventProc(clientData, eventPtr)
  730.     ClientData clientData;    /* Information about window. */
  731.     XEvent *eventPtr;        /* Information about event. */
  732. {
  733.     Scrollbar *scrollPtr = (Scrollbar *) clientData;
  734.  
  735.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
  736.     EventuallyRedraw(scrollPtr);
  737.     } else if (eventPtr->type == DestroyNotify) {
  738.     Tcl_DeleteCommand(scrollPtr->interp, Tk_PathName(scrollPtr->tkwin));
  739.     scrollPtr->tkwin = NULL;
  740.     if (scrollPtr->flags & REDRAW_PENDING) {
  741.         Tk_CancelIdleCall(DisplayScrollbar, (ClientData) scrollPtr);
  742.     }
  743.     Tk_EventuallyFree((ClientData) scrollPtr, DestroyScrollbar);
  744.     } else if (eventPtr->type == ConfigureNotify) {
  745.     ComputeScrollbarGeometry(scrollPtr);
  746.     }
  747. }
  748.  
  749. /*
  750.  *----------------------------------------------------------------------
  751.  *
  752.  * ComputeScrollbarGeometry --
  753.  *
  754.  *    After changes in a scrollbar's size or configuration, this
  755.  *    procedure recomputes various geometry information used in
  756.  *    displaying the scrollbar.
  757.  *
  758.  * Results:
  759.  *    None.
  760.  *
  761.  * Side effects:
  762.  *    The scrollbar will be displayed differently.
  763.  *
  764.  *----------------------------------------------------------------------
  765.  */
  766.  
  767. static void
  768. ComputeScrollbarGeometry(scrollPtr)
  769.     register Scrollbar *scrollPtr;    /* Scrollbar whose geometry may
  770.                      * have changed. */
  771. {
  772.     int width, fieldLength;
  773.  
  774.     if (scrollPtr->relief == TK_RELIEF_FLAT) {
  775.     scrollPtr->offset = 0;
  776.     } else {
  777.     scrollPtr->offset = scrollPtr->borderWidth;
  778.     }
  779.     width = (scrollPtr->vertical) ? Tk_Width(scrollPtr->tkwin)
  780.         : Tk_Height(scrollPtr->tkwin);
  781.     scrollPtr->arrowLength =
  782.         (((width - 2*scrollPtr->offset)*173) + 100) / 200;
  783.     fieldLength = (scrollPtr->vertical ? Tk_Height(scrollPtr->tkwin)
  784.         : Tk_Width(scrollPtr->tkwin))
  785.         - 2*(scrollPtr->arrowLength + scrollPtr->offset);
  786.     if (fieldLength < 0) {
  787.     fieldLength = 0;
  788.     }
  789.     if (scrollPtr->totalUnits <= 0) {
  790.     scrollPtr->sliderFirst = 0;
  791.     scrollPtr->sliderLast = fieldLength;
  792.     } else {
  793.     scrollPtr->sliderFirst = (fieldLength*scrollPtr->firstUnit
  794.         + scrollPtr->totalUnits/2)/scrollPtr->totalUnits;
  795.     scrollPtr->sliderLast = (fieldLength*(scrollPtr->lastUnit+1)
  796.         + scrollPtr->totalUnits/2)/scrollPtr->totalUnits;
  797.  
  798.     /*
  799.      * Adjust the slider so that some piece of it is always
  800.      * displayed in the scrollbar and so that it has at least
  801.      * a minimal width (so it can be grabbed with the mouse).
  802.      */
  803.  
  804.     if (scrollPtr->sliderFirst > (fieldLength - 2*scrollPtr->borderWidth)) {
  805.         scrollPtr->sliderFirst = fieldLength - 2*scrollPtr->borderWidth;
  806.     }
  807.     if (scrollPtr->sliderFirst < 0) {
  808.         scrollPtr->sliderFirst = 0;
  809.     }
  810.     if (scrollPtr->sliderLast < (scrollPtr->sliderFirst
  811.         + 2*scrollPtr->borderWidth)) {
  812.         scrollPtr->sliderLast = scrollPtr->sliderFirst
  813.             + 2*scrollPtr->borderWidth;
  814.     }
  815.     if (scrollPtr->sliderLast > fieldLength) {
  816.         scrollPtr->sliderLast = fieldLength;
  817.     }
  818.     }
  819.     scrollPtr->sliderFirst += scrollPtr->arrowLength + scrollPtr->offset;
  820.     scrollPtr->sliderLast += scrollPtr->arrowLength + scrollPtr->offset;
  821.  
  822.     /*
  823.      * Register the desired geometry for the window (leave enough space
  824.      * for the two arrows plus a minimum-size slider, plus border around
  825.      * the whole window, if any).  Then arrange for the window to be
  826.      * redisplayed.
  827.      */
  828.  
  829.     if (scrollPtr->vertical) {
  830.     Tk_GeometryRequest(scrollPtr->tkwin,
  831.         scrollPtr->width + 2*scrollPtr->offset,
  832.         2*(scrollPtr->arrowLength + scrollPtr->borderWidth
  833.         + scrollPtr->offset));
  834.     } else {
  835.     Tk_GeometryRequest(scrollPtr->tkwin,
  836.         2*(scrollPtr->arrowLength + scrollPtr->borderWidth
  837.         + scrollPtr->offset), scrollPtr->width + 2*scrollPtr->offset);
  838.     }
  839.     Tk_SetInternalBorder(scrollPtr->tkwin, scrollPtr->borderWidth);
  840.  
  841. }
  842.  
  843. /*
  844.  *--------------------------------------------------------------
  845.  *
  846.  * ScrollbarPosition --
  847.  *
  848.  *    Determine the scrollbar element corresponding to a
  849.  *    given position.
  850.  *
  851.  * Results:
  852.  *    One of TOP_ARROW, TOP_GAP, etc., indicating which element
  853.  *    of the scrollbar covers the position given by (x, y).  If
  854.  *    (x,y) is outside the scrollbar entirely, then OUTSIDE is
  855.  *    returned.
  856.  *
  857.  * Side effects:
  858.  *    None.
  859.  *
  860.  *--------------------------------------------------------------
  861.  */
  862.  
  863. static int
  864. ScrollbarPosition(scrollPtr, x, y)
  865.     register Scrollbar *scrollPtr;    /* Scrollbar widget record. */
  866.     int x, y;                /* Coordinates within scrollPtr's
  867.                      * window. */
  868. {
  869.     int length, width, tmp;
  870.  
  871.     if (scrollPtr->vertical) {
  872.     length = Tk_Height(scrollPtr->tkwin);
  873.     width = Tk_Width(scrollPtr->tkwin);
  874.     } else {
  875.     tmp = x;
  876.     x = y;
  877.     y = tmp;
  878.     length = Tk_Width(scrollPtr->tkwin);
  879.     width = Tk_Height(scrollPtr->tkwin);
  880.     }
  881.  
  882.     if ((x < 0) || (x > width) || (y < 0)) {
  883.     return OUTSIDE;
  884.     }
  885.  
  886.     /*
  887.      * All of the calculations in this procedure mirror those in
  888.      * DisplayScrollbar.  Be sure to keep the two consistent.
  889.      */
  890.  
  891.     if (y < (scrollPtr->offset + scrollPtr->arrowLength)) {
  892.     return TOP_ARROW;
  893.     }
  894.     if (y < scrollPtr->sliderFirst) {
  895.     return TOP_GAP;
  896.     }
  897.     if (y < scrollPtr->sliderLast) {
  898.     return SLIDER;
  899.     }
  900.     if (y >= (length - (scrollPtr->arrowLength + scrollPtr->offset))) {
  901.     return BOTTOM_ARROW;
  902.     }
  903.     return BOTTOM_GAP;
  904. }
  905.  
  906. /*
  907.  *--------------------------------------------------------------
  908.  *
  909.  * ScrollbarMouseProc --
  910.  *
  911.  *    This procedure is called back by Tk in response to
  912.  *    mouse events such as window entry, window exit, mouse
  913.  *    motion, and button presses.
  914.  *
  915.  * Results:
  916.  *    None.
  917.  *
  918.  * Side effects:
  919.  *    This procedure implements the "feel" of the scrollbar
  920.  *    by issuing scrolling commands in response to button presses
  921.  *    and mouse motion.
  922.  *
  923.  *--------------------------------------------------------------
  924.  */
  925.  
  926. static void
  927. ScrollbarMouseProc(clientData, eventPtr)
  928.     ClientData clientData;        /* Information about window. */
  929.     register XEvent *eventPtr;        /* Information about event. */
  930. {
  931.     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
  932.  
  933.     Tk_Preserve((ClientData) scrollPtr);
  934.     if (eventPtr->type == EnterNotify) {
  935.     if (scrollPtr->pressField == -1) {
  936.         ScrollbarNewField(scrollPtr,
  937.             ScrollbarPosition(scrollPtr, eventPtr->xcrossing.x,
  938.             eventPtr->xcrossing.y));
  939.     }
  940.     } else if (eventPtr->type == LeaveNotify) {
  941.     if (scrollPtr->pressField == -1) {
  942.         ScrollbarNewField(scrollPtr, OUTSIDE);
  943.     }
  944.     } else if (eventPtr->type == MotionNotify) {
  945.     if (scrollPtr->pressField == SLIDER) {
  946.         int delta, length, newFirst;
  947.  
  948.         if (scrollPtr->vertical) {
  949.         delta = eventPtr->xmotion.y - scrollPtr->pressPos;
  950.         length = Tk_Height(scrollPtr->tkwin)
  951.             - 2*(scrollPtr->arrowLength + scrollPtr->offset);
  952.         } else {
  953.         delta = eventPtr->xmotion.x - scrollPtr->pressPos;
  954.         length = Tk_Width(scrollPtr->tkwin)
  955.             - 2*(scrollPtr->arrowLength + scrollPtr->offset);
  956.         }
  957.  
  958.         /*
  959.          * Do the division with positive numbers to avoid
  960.          * differences in negative-number truncation on different
  961.          * machines.
  962.          */
  963.  
  964.         if (delta >= 0) {
  965.         newFirst = scrollPtr->pressFirstUnit
  966.             + ((delta * scrollPtr->totalUnits) + (length/2))
  967.             / length;
  968.         } else {
  969.         newFirst = scrollPtr->pressFirstUnit
  970.             - (((-delta) * scrollPtr->totalUnits) + (length/2))
  971.             / length;
  972.         }
  973.         ScrollCmd(scrollPtr, newFirst);
  974.     } else if (scrollPtr->pressField == -1) {
  975.         ScrollbarNewField(scrollPtr,
  976.             ScrollbarPosition(scrollPtr, eventPtr->xmotion.x,
  977.             eventPtr->xmotion.y));
  978.     }
  979.     } else if ((eventPtr->type == ButtonPress)
  980.         && (eventPtr->xbutton.state == 0)) {
  981.     /*
  982.      * The following procedure call is only necessary for OpenWindows 2.0
  983.      * because it seems to send ButtonPress events to scrollbars
  984.      * without an EnterNotify or MotionNotify event first (9/23/92).
  985.      */
  986.  
  987.     ScrollbarNewField(scrollPtr,
  988.         ScrollbarPosition(scrollPtr, eventPtr->xbutton.x,
  989.         eventPtr->xbutton.y));
  990.     scrollPtr->pressField = scrollPtr->mouseField;
  991.     if (scrollPtr->pressField != SLIDER) {
  992.         scrollPtr->autoRepeat = Tk_CreateTimerHandler(
  993.             scrollPtr->repeatDelay,
  994.             ScrollbarTimerProc, (ClientData) scrollPtr);
  995.     }
  996.     if (scrollPtr->vertical) {
  997.         scrollPtr->pressPos = eventPtr->xbutton.y;
  998.     } else {
  999.         scrollPtr->pressPos = eventPtr->xbutton.x;
  1000.     }
  1001.     scrollPtr->pressFirstUnit = scrollPtr->firstUnit;
  1002.     if (scrollPtr->pressFirstUnit <= -scrollPtr->windowUnits) {
  1003.         scrollPtr->pressFirstUnit = 1-scrollPtr->windowUnits;
  1004.     }
  1005.     if (scrollPtr->pressFirstUnit >= scrollPtr->totalUnits) {
  1006.         scrollPtr->pressFirstUnit = scrollPtr->totalUnits-1;
  1007.     }
  1008.     EventuallyRedraw(scrollPtr);
  1009.     } else if (eventPtr->type == ButtonRelease) {
  1010.     if (scrollPtr->pressField == scrollPtr->mouseField) {
  1011.         switch (scrollPtr->pressField) {
  1012.         case TOP_ARROW:
  1013.             ScrollCmd(scrollPtr, scrollPtr->firstUnit-1);
  1014.             break;
  1015.         case TOP_GAP:
  1016.             if (scrollPtr->windowUnits <= 1) {
  1017.             ScrollCmd(scrollPtr, scrollPtr->firstUnit - 1);
  1018.             } else {
  1019.             ScrollCmd(scrollPtr, scrollPtr->firstUnit
  1020.                 - (scrollPtr->windowUnits-1));
  1021.             }
  1022.             break;
  1023.         case BOTTOM_GAP: {
  1024.             if (scrollPtr->windowUnits <= 1) {
  1025.             ScrollCmd(scrollPtr, scrollPtr->firstUnit + 1);
  1026.             } else {
  1027.             ScrollCmd(scrollPtr, scrollPtr->firstUnit
  1028.                 + (scrollPtr->windowUnits-1));
  1029.             }
  1030.             break;
  1031.         }
  1032.         case BOTTOM_ARROW:
  1033.             ScrollCmd(scrollPtr, scrollPtr->firstUnit+1);
  1034.             break;
  1035.         }
  1036.     }
  1037.     if (scrollPtr->autoRepeat != NULL) {
  1038.         Tk_DeleteTimerHandler(scrollPtr->autoRepeat);
  1039.         scrollPtr->autoRepeat = NULL;
  1040.     }
  1041.     EventuallyRedraw(scrollPtr);
  1042.     scrollPtr->pressField = -1;
  1043.     ScrollbarNewField(scrollPtr,
  1044.         ScrollbarPosition(scrollPtr, eventPtr->xbutton.x,
  1045.         eventPtr->xbutton.y));
  1046.     }
  1047.     Tk_Release((ClientData) scrollPtr);
  1048. }
  1049.  
  1050. /*
  1051.  *--------------------------------------------------------------
  1052.  *
  1053.  * ScrollCmd --
  1054.  *
  1055.  *    This procedure takes care of invoking a scrolling Tcl
  1056.  *    command and reporting any error that occurs in it.
  1057.  *
  1058.  * Results:
  1059.  *    None.
  1060.  *
  1061.  * Side effects:
  1062.  *    A Tcl command is invoked, and an additional error-processing
  1063.  *    command may also be invoked.
  1064.  *
  1065.  *--------------------------------------------------------------
  1066.  */
  1067.  
  1068. static void
  1069. ScrollCmd(scrollPtr, unit)
  1070.     register Scrollbar *scrollPtr;    /* Scrollbar from which to issue
  1071.                      * command. */
  1072.     int unit;                /* Unit position within thing being
  1073.                      * being displayed that should appear
  1074.                      * at top or right of screen. */
  1075. {
  1076.     char string[20];
  1077.     int result;
  1078.  
  1079.     if ((unit == scrollPtr->firstUnit) || (scrollPtr->command == NULL)) {
  1080.     return;
  1081.     }
  1082.     sprintf(string, " %d", unit);
  1083.     result = Tcl_VarEval(scrollPtr->interp, scrollPtr->command, string,
  1084.         (char *) NULL);
  1085.     if (result != TCL_OK) {
  1086.     Tcl_AddErrorInfo(scrollPtr->interp,
  1087.         "\n    (scrolling command executed by scrollbar)");
  1088.     Tk_BackgroundError(scrollPtr->interp);
  1089.     }
  1090. }
  1091.  
  1092. /*
  1093.  *--------------------------------------------------------------
  1094.  *
  1095.  * EventuallyRedraw --
  1096.  *
  1097.  *    Arrange for one or more of the fields of a scrollbar
  1098.  *    to be redrawn.
  1099.  *
  1100.  * Results:
  1101.  *    None.
  1102.  *
  1103.  * Side effects:
  1104.  *    None.
  1105.  *
  1106.  *--------------------------------------------------------------
  1107.  */
  1108.  
  1109. static void
  1110. EventuallyRedraw(scrollPtr)
  1111.     register Scrollbar *scrollPtr;    /* Information about widget. */
  1112. {
  1113.     if ((scrollPtr->tkwin == NULL) || (!Tk_IsMapped(scrollPtr->tkwin))) {
  1114.     return;
  1115.     }
  1116.     if ((scrollPtr->flags & REDRAW_PENDING) == 0) {
  1117.     Tk_DoWhenIdle(DisplayScrollbar, (ClientData) scrollPtr);
  1118.     scrollPtr->flags |= REDRAW_PENDING;
  1119.     }
  1120. }
  1121.  
  1122. /*
  1123.  *--------------------------------------------------------------
  1124.  *
  1125.  * ScrollbarNewField --
  1126.  *
  1127.  *    This procedure is called to declare that the mouse is in
  1128.  *    a particular field of the scrollbar (e.g. top arrow), so
  1129.  *    that the field can be highlighed and the previous field
  1130.  *    can be returned to normal display.
  1131.  *
  1132.  * Results:
  1133.  *    None.
  1134.  *
  1135.  * Side effects:
  1136.  *    Fields may be redisplayed.
  1137.  *
  1138.  *--------------------------------------------------------------
  1139.  */
  1140.  
  1141. static void
  1142. ScrollbarNewField(scrollPtr, field)
  1143.     register Scrollbar *scrollPtr;    /* Information about widget. */
  1144.     int field;                /* Identifies field under mouse,
  1145.                      * e.g. TOP_ARROW. */
  1146. {
  1147.     if (field == scrollPtr->mouseField) {
  1148.     return;
  1149.     }
  1150.     EventuallyRedraw(scrollPtr);
  1151.     scrollPtr->mouseField = field;
  1152. }
  1153.  
  1154. /*
  1155.  *--------------------------------------------------------------
  1156.  *
  1157.  * ScrollbarTimerProc --
  1158.  *
  1159.  *    This procedure is invoked as a Tk timer handler for actions
  1160.  *    that auto-repeat (mouse presses in an arrow or gap).  It
  1161.  *    performs the auto-repeat action.
  1162.  *
  1163.  * Results:
  1164.  *    None.
  1165.  *
  1166.  * Side effects:
  1167.  *    Whatever action corresponds to the current mouse button
  1168.  *    is repeated, and this procedure is rescheduled to execute
  1169.  *    again later.
  1170.  *
  1171.  *--------------------------------------------------------------
  1172.  */
  1173.  
  1174. static void
  1175. ScrollbarTimerProc(clientData)
  1176.     ClientData clientData;    /* Information about widget. */
  1177. {
  1178.     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
  1179.  
  1180.     Tk_Preserve((ClientData) scrollPtr);
  1181.     switch(scrollPtr->pressField) {
  1182.     case TOP_ARROW:
  1183.         ScrollCmd(scrollPtr, scrollPtr->firstUnit-1);
  1184.         break;
  1185.     case TOP_GAP:
  1186.         ScrollCmd(scrollPtr, scrollPtr->firstUnit
  1187.             - (scrollPtr->windowUnits-1));
  1188.         break;
  1189.     case BOTTOM_GAP: {
  1190.         ScrollCmd(scrollPtr, scrollPtr->firstUnit
  1191.             + (scrollPtr->windowUnits-1));
  1192.         break;
  1193.     }
  1194.     case BOTTOM_ARROW:
  1195.         ScrollCmd(scrollPtr, scrollPtr->firstUnit+1);
  1196.         break;
  1197.     }
  1198.     if (scrollPtr->tkwin != NULL) {
  1199.     scrollPtr->autoRepeat = Tk_CreateTimerHandler(
  1200.         scrollPtr->repeatInterval, ScrollbarTimerProc,
  1201.         (ClientData) scrollPtr);
  1202.     }
  1203.     Tk_Release((ClientData) scrollPtr);
  1204. }
  1205.