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.
9 * Copyright 1990-1992 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.
20 static char rcsid
[] = "$Header: /user6/ouster/wish/RCS/tkScrollbar.c,v 1.35 92/05/22 16:57:27 ouster Exp $ SPRITE (Berkeley)";
28 * A data structure of the following type is kept for each scrollbar
29 * widget managed by this file:
33 Tk_Window tkwin
; /* Window that embodies the scrollbar. NULL
34 * means that the window has been destroyed
35 * but the data structures haven't yet been
37 Tcl_Interp
*interp
; /* Interpreter associated with scrollbar. */
38 Tk_Uid orientUid
; /* Orientation for window ("vertical" or
40 int vertical
; /* Non-zero means vertical orientation
41 * requested, zero means horizontal. */
42 int width
; /* Desired narrow dimension of scrollbar,
44 char *command
; /* Command prefix to use when invoking
45 * scrolling commands. NULL means don't
46 * invoke commands. Malloc'ed. */
47 int commandSize
; /* Number of non-NULL bytes in command. */
48 int repeatDelay
; /* How long to wait before auto-repeating
49 * on scrolling actions (in ms). */
50 int repeatInterval
; /* Interval between autorepeats (in ms). */
53 * Information used when displaying widget:
56 int borderWidth
; /* Width of 3-D borders. */
57 Tk_3DBorder bgBorder
; /* Used for drawing background. */
58 Tk_3DBorder fgBorder
; /* For drawing foreground shapes. */
59 Tk_3DBorder activeBorder
; /* For drawing foreground shapes when
60 * active (i.e. when mouse is positioned
61 * over element). NULL means use fgBorder. */
62 GC copyGC
; /* Used for copying from pixmap onto screen. */
63 int relief
; /* Indicates whether window as a whole is
64 * raised, sunken, or flat. */
65 int offset
; /* Zero if relief is TK_RELIEF_FLAT,
66 * borderWidth otherwise. Indicates how
67 * much interior stuff must be offset from
68 * outside edges to leave room for border. */
69 int arrowLength
; /* Length of arrows along long dimension of
70 * scrollbar. Recomputed on window size
72 int sliderFirst
; /* Pixel coordinate of top or left edge
73 * of slider area, including border. */
74 int sliderLast
; /* Coordinate of pixel just after bottom
75 * or right edge of slider area, including
77 int mouseField
; /* Indicates which scrollbar element is
78 * under mouse (e.g. TOP_ARROW; see below
79 * for possible values). */
80 int pressField
; /* Field in which button was pressed, or -1
81 * if no button is down. */
82 int pressPos
; /* Position of mouse when button was
83 * pressed (y for vertical scrollbar, x
85 int pressFirstUnit
; /* Value of "firstUnit" when mouse button
89 * Information describing the application related to the scrollbar.
90 * This information is provided by the application by invoking the
91 * "set" widget command.
94 int totalUnits
; /* Total dimension of application, in
96 int windowUnits
; /* Maximum number of units that can
97 * be displayed in the window at
99 int firstUnit
; /* Number of last unit visible in
100 * application's window. */
101 int lastUnit
; /* Index of last unit visible in window. */
104 * Miscellaneous information:
107 Cursor cursor
; /* Current cursor for window, or None. */
108 Tk_TimerToken autoRepeat
; /* Token for auto-repeat that's
109 * currently in progress. NULL means no
110 * auto-repeat in progress. */
111 int flags
; /* Various flags; see below for
116 * Legal values for "mouseField" field of Scrollbar structures. These
117 * are also the return values from the ScrollbarPosition procedure.
124 #define BOTTOM_ARROW 5
128 * Flag bits for scrollbars:
130 * REDRAW_PENDING: Non-zero means a DoWhenIdle handler
131 * has already been queued to redraw
135 #define REDRAW_PENDING 1
138 * Information used for argv parsing.
142 static Tk_ConfigSpec configSpecs
[] = {
143 {TK_CONFIG_BORDER
, "-activeforeground", "activeForeground", "Background",
144 DEF_SCROLLBAR_ACTIVE_FG_COLOR
, Tk_Offset(Scrollbar
, activeBorder
),
145 TK_CONFIG_COLOR_ONLY
},
146 {TK_CONFIG_BORDER
, "-activeforeground", "activeForeground", "Background",
147 DEF_SCROLLBAR_ACTIVE_FG_MONO
, Tk_Offset(Scrollbar
, activeBorder
),
148 TK_CONFIG_MONO_ONLY
},
149 {TK_CONFIG_BORDER
, "-background", "background", "Background",
150 DEF_SCROLLBAR_BG_COLOR
, Tk_Offset(Scrollbar
, bgBorder
),
151 TK_CONFIG_COLOR_ONLY
},
152 {TK_CONFIG_BORDER
, "-background", "background", "Background",
153 DEF_SCROLLBAR_BG_MONO
, Tk_Offset(Scrollbar
, bgBorder
),
154 TK_CONFIG_MONO_ONLY
},
155 {TK_CONFIG_SYNONYM
, "-bd", "borderWidth", (char *) NULL
,
156 (char *) NULL
, 0, 0},
157 {TK_CONFIG_SYNONYM
, "-bg", "background", (char *) NULL
,
158 (char *) NULL
, 0, 0},
159 {TK_CONFIG_PIXELS
, "-borderwidth", "borderWidth", "BorderWidth",
160 DEF_SCROLLBAR_BORDER_WIDTH
, Tk_Offset(Scrollbar
, borderWidth
), 0},
161 {TK_CONFIG_STRING
, "-command", "command", "Command",
162 DEF_SCROLLBAR_COMMAND
, Tk_Offset(Scrollbar
, command
), 0},
163 {TK_CONFIG_ACTIVE_CURSOR
, "-cursor", "cursor", "Cursor",
164 DEF_SCROLLBAR_CURSOR
, Tk_Offset(Scrollbar
, cursor
), TK_CONFIG_NULL_OK
},
165 {TK_CONFIG_SYNONYM
, "-fg", "foreground", (char *) NULL
,
166 (char *) NULL
, 0, 0},
167 {TK_CONFIG_BORDER
, "-foreground", "foreground", "Foreground",
168 DEF_SCROLLBAR_FG_COLOR
, Tk_Offset(Scrollbar
, fgBorder
),
169 TK_CONFIG_COLOR_ONLY
},
170 {TK_CONFIG_BORDER
, "-foreground", "foreground", "Foreground",
171 DEF_SCROLLBAR_FG_MONO
, Tk_Offset(Scrollbar
, fgBorder
),
172 TK_CONFIG_MONO_ONLY
},
173 {TK_CONFIG_UID
, "-orient", "orient", "Orient",
174 DEF_SCROLLBAR_ORIENT
, Tk_Offset(Scrollbar
, orientUid
), 0},
175 {TK_CONFIG_RELIEF
, "-relief", "relief", "Relief",
176 DEF_SCROLLBAR_RELIEF
, Tk_Offset(Scrollbar
, relief
), 0},
177 {TK_CONFIG_INT
, "-repeatdelay", "repeatDelay", "RepeatDelay",
178 DEF_SCROLLBAR_REPEAT_DELAY
, Tk_Offset(Scrollbar
, repeatDelay
), 0},
179 {TK_CONFIG_INT
, "-repeatinterval", "repeatInterval", "RepeatInterval",
180 DEF_SCROLLBAR_REPEAT_INTERVAL
, Tk_Offset(Scrollbar
, repeatInterval
), 0},
181 {TK_CONFIG_PIXELS
, "-width", "width", "Width",
182 DEF_SCROLLBAR_WIDTH
, Tk_Offset(Scrollbar
, width
), 0},
183 {TK_CONFIG_END
, (char *) NULL
, (char *) NULL
, (char *) NULL
,
188 * Forward declarations for procedures defined later in this file:
191 static void ComputeScrollbarGeometry
_ANSI_ARGS_((
192 Scrollbar
*scrollPtr
));
193 static int ConfigureScrollbar
_ANSI_ARGS_((Tcl_Interp
*interp
,
194 Scrollbar
*scrollPtr
, int argc
, char **argv
,
196 static void DestroyScrollbar
_ANSI_ARGS_((ClientData clientData
));
197 static void DisplayScrollbar
_ANSI_ARGS_((ClientData clientData
));
198 static void EventuallyRedraw
_ANSI_ARGS_((Scrollbar
*scrollPtr
));
199 static void ScrollbarEventProc
_ANSI_ARGS_((ClientData clientData
,
201 static void ScrollbarMouseProc
_ANSI_ARGS_((ClientData clientData
,
203 static void ScrollbarNewField
_ANSI_ARGS_((Scrollbar
*scrollPtr
,
205 static int ScrollbarPosition
_ANSI_ARGS_((Scrollbar
*scrollPtr
,
207 static void ScrollbarTimerProc
_ANSI_ARGS_((
208 ClientData clientData
));
209 static int ScrollbarWidgetCmd
_ANSI_ARGS_((ClientData clientData
,
210 Tcl_Interp
*, int argc
, char **argv
));
211 static void ScrollCmd
_ANSI_ARGS_((Scrollbar
*scrollPtr
,
215 *--------------------------------------------------------------
219 * This procedure is invoked to process the "scrollbar" Tcl
220 * command. See the user documentation for details on what
224 * A standard Tcl result.
227 * See the user documentation.
229 *--------------------------------------------------------------
234 ClientData clientData
, /* Main window associated with
236 Tcl_Interp
*interp
, /* Current interpreter. */
237 int argc
, /* Number of arguments. */
238 char **argv
/* Argument strings. */
241 Tk_Window tkwin
= (Tk_Window
) clientData
;
242 register Scrollbar
*scrollPtr
;
246 Tcl_AppendResult(interp
, "wrong # args: should be \"",
247 argv
[0], " pathName ?options?\"", (char *) NULL
);
251 new = Tk_CreateWindowFromPath(interp
, tkwin
, argv
[1], (char *) NULL
);
257 * Initialize fields that won't be initialized by ConfigureScrollbar,
258 * or which ConfigureScrollbar expects to have reasonable values
259 * (e.g. resource pointers).
262 scrollPtr
= (Scrollbar
*) ckalloc(sizeof(Scrollbar
));
263 scrollPtr
->tkwin
= new;
264 scrollPtr
->interp
= interp
;
265 scrollPtr
->command
= NULL
;
266 scrollPtr
->bgBorder
= NULL
;
267 scrollPtr
->fgBorder
= NULL
;
268 scrollPtr
->activeBorder
= NULL
;
269 scrollPtr
->copyGC
= None
;
270 scrollPtr
->mouseField
= OUTSIDE
;
271 scrollPtr
->pressField
= -1;
272 scrollPtr
->totalUnits
= 0;
273 scrollPtr
->windowUnits
= 0;
274 scrollPtr
->firstUnit
= 0;
275 scrollPtr
->lastUnit
= 0;
276 scrollPtr
->cursor
= None
;
277 scrollPtr
->autoRepeat
= NULL
;
278 scrollPtr
->flags
= 0;
280 Tk_SetClass(scrollPtr
->tkwin
, "Scrollbar");
281 Tk_CreateEventHandler(scrollPtr
->tkwin
, ExposureMask
|StructureNotifyMask
,
282 ScrollbarEventProc
, (ClientData
) scrollPtr
);
283 Tk_CreateEventHandler(scrollPtr
->tkwin
, EnterWindowMask
|LeaveWindowMask
284 |PointerMotionMask
|ButtonPressMask
|ButtonReleaseMask
,
285 ScrollbarMouseProc
, (ClientData
) scrollPtr
);
286 Tcl_CreateCommand(interp
, Tk_PathName(scrollPtr
->tkwin
), ScrollbarWidgetCmd
,
287 (ClientData
) scrollPtr
, (void (*)(int *)) NULL
);
288 if (ConfigureScrollbar(interp
, scrollPtr
, argc
-2, argv
+2, 0) != TCL_OK
) {
292 interp
->result
= Tk_PathName(scrollPtr
->tkwin
);
296 Tk_DestroyWindow(scrollPtr
->tkwin
);
301 *--------------------------------------------------------------
303 * ScrollbarWidgetCmd --
305 * This procedure is invoked to process the Tcl command
306 * that corresponds to a widget managed by this module.
307 * See the user documentation for details on what it does.
310 * A standard Tcl result.
313 * See the user documentation.
315 *--------------------------------------------------------------
320 ClientData clientData
, /* Information about scrollbar
322 Tcl_Interp
*interp
, /* Current interpreter. */
323 int argc
, /* Number of arguments. */
324 char **argv
/* Argument strings. */
327 register Scrollbar
*scrollPtr
= (Scrollbar
*) clientData
;
333 Tcl_AppendResult(interp
, "wrong # args: should be \"",
334 argv
[0], " option ?arg arg ...?\"", (char *) NULL
);
337 Tk_Preserve((ClientData
) scrollPtr
);
339 length
= strlen(argv
[1]);
340 if ((c
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)) {
342 result
= Tk_ConfigureInfo(interp
, scrollPtr
->tkwin
, configSpecs
,
343 (char *) scrollPtr
, (char *) NULL
, 0);
344 } else if (argc
== 3) {
345 result
= Tk_ConfigureInfo(interp
, scrollPtr
->tkwin
, configSpecs
,
346 (char *) scrollPtr
, argv
[2], 0);
348 result
= ConfigureScrollbar(interp
, scrollPtr
, argc
-2, argv
+2,
349 TK_CONFIG_ARGV_ONLY
);
351 } else if ((c
== 'g') && (strncmp(argv
[1], "get", length
) == 0)) {
353 Tcl_AppendResult(interp
, "wrong # args: should be \"",
354 argv
[0], " get\"", (char *) NULL
);
357 sprintf(interp
->result
, "%d %d %d %d", scrollPtr
->totalUnits
,
358 scrollPtr
->windowUnits
, scrollPtr
->firstUnit
,
359 scrollPtr
->lastUnit
);
360 } else if ((c
== 's') && (strncmp(argv
[1], "set", length
) == 0)) {
361 int totalUnits
, windowUnits
, firstUnit
, lastUnit
;
364 Tcl_AppendResult(interp
, "wrong # args: should be \"",
366 " set totalUnits windowUnits firstUnit lastUnit\"",
370 if (Tcl_GetInt(interp
, argv
[2], &totalUnits
) != TCL_OK
) {
373 if (totalUnits
< 0) {
374 sprintf(interp
->result
, "illegal totalUnits %d", totalUnits
);
377 if (Tcl_GetInt(interp
, argv
[3], &windowUnits
) != TCL_OK
) {
380 if (windowUnits
< 0) {
381 sprintf(interp
->result
, "illegal windowUnits %d", windowUnits
);
384 if (Tcl_GetInt(interp
, argv
[4], &firstUnit
) != TCL_OK
) {
387 if (Tcl_GetInt(interp
, argv
[5], &lastUnit
) != TCL_OK
) {
390 if (totalUnits
> 0) {
391 if (lastUnit
< firstUnit
) {
392 sprintf(interp
->result
, "illegal lastUnit %d", lastUnit
);
396 firstUnit
= lastUnit
= 0;
398 scrollPtr
->totalUnits
= totalUnits
;
399 scrollPtr
->windowUnits
= windowUnits
;
400 scrollPtr
->firstUnit
= firstUnit
;
401 scrollPtr
->lastUnit
= lastUnit
;
402 ComputeScrollbarGeometry(scrollPtr
);
403 EventuallyRedraw(scrollPtr
);
405 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
406 "\": must be configure, get, or set", (char *) NULL
);
409 Tk_Release((ClientData
) scrollPtr
);
413 Tk_Release((ClientData
) scrollPtr
);
418 *----------------------------------------------------------------------
420 * DestroyScrollbar --
422 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
423 * to clean up the internal structure of a scrollbar at a safe time
424 * (when no-one is using it anymore).
430 * Everything associated with the scrollbar is freed up.
432 *----------------------------------------------------------------------
437 ClientData clientData
/* Info about scrollbar widget. */
440 register Scrollbar
*scrollPtr
= (Scrollbar
*) clientData
;
442 if (scrollPtr
->command
!= NULL
) {
443 ckfree(scrollPtr
->command
);
445 if (scrollPtr
->bgBorder
!= NULL
) {
446 Tk_Free3DBorder(scrollPtr
->bgBorder
);
448 if (scrollPtr
->fgBorder
!= NULL
) {
449 Tk_Free3DBorder(scrollPtr
->fgBorder
);
451 if (scrollPtr
->activeBorder
!= NULL
) {
452 Tk_Free3DBorder(scrollPtr
->activeBorder
);
454 if (scrollPtr
->copyGC
!= None
) {
455 Tk_FreeGC(scrollPtr
->copyGC
);
457 if (scrollPtr
->cursor
!= None
) {
458 Tk_FreeCursor(scrollPtr
->cursor
);
460 ckfree((char *) scrollPtr
);
464 *----------------------------------------------------------------------
466 * ConfigureScrollbar --
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.
473 * The return value is a standard Tcl result. If TCL_ERROR is
474 * returned, then interp->result contains an error message.
477 * Configuration information, such as colors, border width,
478 * etc. get set for scrollPtr; old resources get freed,
481 *----------------------------------------------------------------------
486 Tcl_Interp
*interp
, /* Used for error reporting. */
487 register Scrollbar
*scrollPtr
, /* Information about widget; may or
488 * may not already have values for
490 int argc
, /* Number of valid entries in argv. */
491 char **argv
, /* Arguments. */
492 int flags
/* Flags to pass to
493 * Tk_ConfigureWidget. */
499 if (Tk_ConfigureWidget(interp
, scrollPtr
->tkwin
, configSpecs
,
500 argc
, argv
, (char *) scrollPtr
, flags
) != TCL_OK
) {
505 * A few options need special processing, such as parsing the
506 * orientation or setting the background from a 3-D border.
509 length
= strlen(scrollPtr
->orientUid
);
510 if (strncmp(scrollPtr
->orientUid
, "vertical", length
) == 0) {
511 scrollPtr
->vertical
= 1;
512 } else if (strncmp(scrollPtr
->orientUid
, "horizontal", length
) == 0) {
513 scrollPtr
->vertical
= 0;
515 Tcl_AppendResult(interp
, "bad orientation \"", scrollPtr
->orientUid
,
516 "\": must be vertical or horizontal", (char *) NULL
);
520 if (scrollPtr
->command
!= NULL
) {
521 scrollPtr
->commandSize
= strlen(scrollPtr
->command
);
523 scrollPtr
->commandSize
= 0;
526 Tk_SetBackgroundFromBorder(scrollPtr
->tkwin
, scrollPtr
->bgBorder
);
528 if (scrollPtr
->copyGC
== None
) {
529 gcValues
.graphics_exposures
= False
;
530 scrollPtr
->copyGC
= Tk_GetGC(scrollPtr
->tkwin
, GCGraphicsExposures
,
535 * Register the desired geometry for the window (leave enough space
536 * for the two arrows plus a minimum-size slider, plus border around
537 * the whole window, if any). Then arrange for the window to be
541 ComputeScrollbarGeometry(scrollPtr
);
542 EventuallyRedraw(scrollPtr
);
547 *--------------------------------------------------------------
549 * DisplayScrollbar --
551 * This procedure redraws the contents of a scrollbar window.
552 * It is invoked as a do-when-idle handler, so it only runs
553 * when there's nothing else for the application to do.
559 * Information appears on the screen.
561 *--------------------------------------------------------------
566 ClientData clientData
/* Information about window. */
569 register Scrollbar
*scrollPtr
= (Scrollbar
*) clientData
;
570 register Tk_Window tkwin
= scrollPtr
->tkwin
;
573 int relief
, width
, fieldLength
;
576 if ((scrollPtr
->tkwin
== NULL
) || !Tk_IsMapped(tkwin
)) {
580 if (scrollPtr
->vertical
) {
581 width
= Tk_Width(tkwin
) - 2*scrollPtr
->offset
;
583 width
= Tk_Height(tkwin
) - 2*scrollPtr
->offset
;
587 * In order to avoid screen flashes, this procedure redraws
588 * the scrollbar in a pixmap, then copies the pixmap to the
589 * screen in a single operation. This means that there's no
590 * point in time where the on-sreen image has been cleared.
593 pixmap
= XCreatePixmap(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
594 Tk_Width(tkwin
), Tk_Height(tkwin
),
595 Tk_DefaultDepth(Tk_Screen(tkwin
)));
596 Tk_Fill3DRectangle(Tk_Display(tkwin
), pixmap
, scrollPtr
->bgBorder
,
597 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
),
598 scrollPtr
->borderWidth
, scrollPtr
->relief
);
601 * Draw the top or left arrow. The coordinates of the polygon
602 * points probably seem odd, but they were carefully chosen with
603 * respect to X's rules for filling polygons. These point choices
604 * cause the arrows to just fill the narrow dimension of the
605 * scrollbar and be properly centered.
608 if (scrollPtr
->mouseField
== TOP_ARROW
) {
609 border
= scrollPtr
->activeBorder
;
610 relief
= scrollPtr
->pressField
== TOP_ARROW
? TK_RELIEF_SUNKEN
613 border
= scrollPtr
->fgBorder
;
614 relief
= TK_RELIEF_RAISED
;
616 if (scrollPtr
->vertical
) {
617 points
[0].x
= scrollPtr
->offset
- 1;
618 points
[0].y
= scrollPtr
->arrowLength
+ scrollPtr
->offset
;
619 points
[1].x
= width
+ scrollPtr
->offset
;
620 points
[1].y
= points
[0].y
;
621 points
[2].x
= width
/2 + scrollPtr
->offset
;
622 points
[2].y
= scrollPtr
->offset
- 1;
623 Tk_Fill3DPolygon(Tk_Display(tkwin
), pixmap
, border
,
624 points
, 3, scrollPtr
->borderWidth
, relief
);
626 points
[0].x
= scrollPtr
->arrowLength
+ scrollPtr
->offset
;
627 points
[0].y
= scrollPtr
->offset
- 1;
628 points
[1].x
= scrollPtr
->offset
;
629 points
[1].y
= width
/2 + scrollPtr
->offset
;
630 points
[2].x
= points
[0].x
;
631 points
[2].y
= width
+ scrollPtr
->offset
;
632 Tk_Fill3DPolygon(Tk_Display(tkwin
), pixmap
, border
,
633 points
, 3, scrollPtr
->borderWidth
, relief
);
637 * Display the bottom or right arrow.
640 if (scrollPtr
->mouseField
== BOTTOM_ARROW
) {
641 border
= scrollPtr
->activeBorder
;
642 relief
= scrollPtr
->pressField
== BOTTOM_ARROW
? TK_RELIEF_SUNKEN
645 border
= scrollPtr
->fgBorder
;
646 relief
= TK_RELIEF_RAISED
;
648 if (scrollPtr
->vertical
) {
649 points
[0].x
= scrollPtr
->offset
;
650 points
[0].y
= Tk_Height(tkwin
) - scrollPtr
->arrowLength
652 points
[1].x
= width
/2 + scrollPtr
->offset
;
653 points
[1].y
= Tk_Height(tkwin
) - scrollPtr
->offset
;
654 points
[2].x
= width
+ scrollPtr
->offset
;
655 points
[2].y
= points
[0].y
;
656 Tk_Fill3DPolygon(Tk_Display(tkwin
), pixmap
, border
,
657 points
, 3, scrollPtr
->borderWidth
, relief
);
659 points
[0].x
= Tk_Width(tkwin
) - scrollPtr
->arrowLength
661 points
[0].y
= scrollPtr
->offset
- 1;
662 points
[1].x
= points
[0].x
;
663 points
[1].y
= width
+ scrollPtr
->offset
;
664 points
[2].x
= Tk_Width(tkwin
) - scrollPtr
->offset
;
665 points
[2].y
= width
/2 + scrollPtr
->offset
;
666 Tk_Fill3DPolygon(Tk_Display(tkwin
), pixmap
, border
,
667 points
, 3, scrollPtr
->borderWidth
, relief
);
671 * Display the slider.
674 if (scrollPtr
->mouseField
== SLIDER
) {
675 border
= scrollPtr
->activeBorder
;
676 relief
= scrollPtr
->pressField
== SLIDER
? TK_RELIEF_SUNKEN
679 border
= scrollPtr
->fgBorder
;
680 relief
= TK_RELIEF_RAISED
;
682 fieldLength
= (scrollPtr
->vertical
? Tk_Height(tkwin
) : Tk_Width(tkwin
))
683 - 2*(scrollPtr
->arrowLength
+ scrollPtr
->offset
);
684 if (fieldLength
< 0) {
687 if (scrollPtr
->vertical
) {
688 Tk_Fill3DRectangle(Tk_Display(tkwin
), pixmap
, border
,
689 1 + scrollPtr
->offset
, scrollPtr
->sliderFirst
,
690 width
-2, scrollPtr
->sliderLast
- scrollPtr
->sliderFirst
,
691 scrollPtr
->borderWidth
, relief
);
693 Tk_Fill3DRectangle(Tk_Display(tkwin
), pixmap
, border
,
694 scrollPtr
->sliderFirst
, 1 + scrollPtr
->offset
,
695 scrollPtr
->sliderLast
- scrollPtr
->sliderFirst
, width
-2,
696 scrollPtr
->borderWidth
, relief
);
700 * Copy the information from the off-screen pixmap onto the screen,
701 * then delete the pixmap.
704 XCopyArea(Tk_Display(tkwin
), pixmap
, Tk_WindowId(tkwin
),
705 scrollPtr
->copyGC
, 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
), 0, 0);
706 XFreePixmap(Tk_Display(tkwin
), pixmap
);
709 scrollPtr
->flags
&= ~REDRAW_PENDING
;
713 *--------------------------------------------------------------
715 * ScrollbarEventProc --
717 * This procedure is invoked by the Tk dispatcher for various
718 * events on scrollbars.
724 * When the window gets deleted, internal structures get
725 * cleaned up. When it gets exposed, it is redisplayed.
727 *--------------------------------------------------------------
732 ClientData clientData
, /* Information about window. */
733 XEvent
*eventPtr
/* Information about event. */
736 Scrollbar
*scrollPtr
= (Scrollbar
*) clientData
;
738 if ((eventPtr
->type
== Expose
) && (eventPtr
->xexpose
.count
== 0)) {
739 EventuallyRedraw(scrollPtr
);
740 } else if (eventPtr
->type
== DestroyNotify
) {
741 Tcl_DeleteCommand(scrollPtr
->interp
, Tk_PathName(scrollPtr
->tkwin
));
742 scrollPtr
->tkwin
= NULL
;
743 if (scrollPtr
->flags
& REDRAW_PENDING
) {
744 Tk_CancelIdleCall(DisplayScrollbar
, (ClientData
) scrollPtr
);
746 Tk_EventuallyFree((ClientData
) scrollPtr
, DestroyScrollbar
);
747 } else if (eventPtr
->type
== ConfigureNotify
) {
748 ComputeScrollbarGeometry(scrollPtr
);
753 *----------------------------------------------------------------------
755 * ComputeScrollbarGeometry --
757 * After changes in a scrollbar's size or configuration, this
758 * procedure recomputes various geometry information used in
759 * displaying the scrollbar.
765 * The scrollbar will be displayed differently.
767 *----------------------------------------------------------------------
771 ComputeScrollbarGeometry (
772 register Scrollbar
*scrollPtr
/* Scrollbar whose geometry may
776 int width
, fieldLength
;
778 if (scrollPtr
->relief
== TK_RELIEF_FLAT
) {
779 scrollPtr
->offset
= 0;
781 scrollPtr
->offset
= scrollPtr
->borderWidth
;
783 width
= (scrollPtr
->vertical
) ? Tk_Width(scrollPtr
->tkwin
)
784 : Tk_Height(scrollPtr
->tkwin
);
785 scrollPtr
->arrowLength
=
786 (((width
- 2*scrollPtr
->offset
)*173) + 100) / 200;
787 fieldLength
= (scrollPtr
->vertical
? Tk_Height(scrollPtr
->tkwin
)
788 : Tk_Width(scrollPtr
->tkwin
))
789 - 2*(scrollPtr
->arrowLength
+ scrollPtr
->offset
);
790 if (fieldLength
< 0) {
793 if (scrollPtr
->totalUnits
<= 0) {
794 scrollPtr
->sliderFirst
= 0;
795 scrollPtr
->sliderLast
= fieldLength
;
797 scrollPtr
->sliderFirst
= (fieldLength
*scrollPtr
->firstUnit
798 + scrollPtr
->totalUnits
/2)/scrollPtr
->totalUnits
;
799 scrollPtr
->sliderLast
= (fieldLength
*(scrollPtr
->lastUnit
+1)
800 + scrollPtr
->totalUnits
/2)/scrollPtr
->totalUnits
;
803 * Adjust the slider so that some piece of it is always
804 * displayed in the scrollbar and so that it has at least
805 * a minimal width (so it can be grabbed with the mouse).
808 if (scrollPtr
->sliderFirst
> (fieldLength
- 2*scrollPtr
->borderWidth
)) {
809 scrollPtr
->sliderFirst
= fieldLength
- 2*scrollPtr
->borderWidth
;
811 if (scrollPtr
->sliderFirst
< 0) {
812 scrollPtr
->sliderFirst
= 0;
814 if (scrollPtr
->sliderLast
< (scrollPtr
->sliderFirst
815 + 2*scrollPtr
->borderWidth
)) {
816 scrollPtr
->sliderLast
= scrollPtr
->sliderFirst
817 + 2*scrollPtr
->borderWidth
;
819 if (scrollPtr
->sliderLast
> fieldLength
) {
820 scrollPtr
->sliderLast
= fieldLength
;
823 scrollPtr
->sliderFirst
+= scrollPtr
->arrowLength
+ scrollPtr
->offset
;
824 scrollPtr
->sliderLast
+= scrollPtr
->arrowLength
+ scrollPtr
->offset
;
827 * Register the desired geometry for the window (leave enough space
828 * for the two arrows plus a minimum-size slider, plus border around
829 * the whole window, if any). Then arrange for the window to be
833 if (scrollPtr
->vertical
) {
834 Tk_GeometryRequest(scrollPtr
->tkwin
,
835 scrollPtr
->width
+ 2*scrollPtr
->offset
,
836 2*(scrollPtr
->arrowLength
+ scrollPtr
->borderWidth
837 + scrollPtr
->offset
));
839 Tk_GeometryRequest(scrollPtr
->tkwin
,
840 2*(scrollPtr
->arrowLength
+ scrollPtr
->borderWidth
841 + scrollPtr
->offset
), scrollPtr
->width
+ 2*scrollPtr
->offset
);
843 Tk_SetInternalBorder(scrollPtr
->tkwin
, scrollPtr
->borderWidth
);
848 *--------------------------------------------------------------
850 * ScrollbarPosition --
852 * Determine the scrollbar element corresponding to a
856 * One of TOP_ARROW, TOP_GAP, etc., indicating which element
857 * of the scrollbar covers the position given by (x, y). If
858 * (x,y) is outside the scrollbar entirely, then OUTSIDE is
864 *--------------------------------------------------------------
869 register Scrollbar
*scrollPtr
, /* Scrollbar widget record. */
871 int y
/* Coordinates within scrollPtr's
875 int length
, width
, tmp
;
877 if (scrollPtr
->vertical
) {
878 length
= Tk_Height(scrollPtr
->tkwin
);
879 width
= Tk_Width(scrollPtr
->tkwin
);
884 length
= Tk_Width(scrollPtr
->tkwin
);
885 width
= Tk_Height(scrollPtr
->tkwin
);
888 if ((x
< 0) || (x
> width
) || (y
< 0)) {
893 * All of the calculations in this procedure mirror those in
894 * DisplayScrollbar. Be sure to keep the two consistent.
897 if (y
< (scrollPtr
->offset
+ scrollPtr
->arrowLength
)) {
900 if (y
< scrollPtr
->sliderFirst
) {
903 if (y
< scrollPtr
->sliderLast
) {
906 if (y
>= (length
- (scrollPtr
->arrowLength
+ scrollPtr
->offset
))) {
913 *--------------------------------------------------------------
915 * ScrollbarMouseProc --
917 * This procedure is called back by Tk in response to
918 * mouse events such as window entry, window exit, mouse
919 * motion, and button presses.
925 * This procedure implements the "feel" of the scrollbar
926 * by issuing scrolling commands in response to button presses
929 *--------------------------------------------------------------
934 ClientData clientData
, /* Information about window. */
935 register XEvent
*eventPtr
/* Information about event. */
938 register Scrollbar
*scrollPtr
= (Scrollbar
*) clientData
;
940 Tk_Preserve((ClientData
) scrollPtr
);
941 if (eventPtr
->type
== EnterNotify
) {
942 if (scrollPtr
->pressField
== -1) {
943 ScrollbarNewField(scrollPtr
,
944 ScrollbarPosition(scrollPtr
, eventPtr
->xcrossing
.x
,
945 eventPtr
->xcrossing
.y
));
947 } else if (eventPtr
->type
== LeaveNotify
) {
948 if (scrollPtr
->pressField
== -1) {
949 ScrollbarNewField(scrollPtr
, OUTSIDE
);
951 } else if (eventPtr
->type
== MotionNotify
) {
952 if (scrollPtr
->pressField
== SLIDER
) {
953 int delta
, length
, newFirst
;
955 if (scrollPtr
->vertical
) {
956 delta
= eventPtr
->xmotion
.y
- scrollPtr
->pressPos
;
957 length
= Tk_Height(scrollPtr
->tkwin
)
958 - 2*(scrollPtr
->arrowLength
+ scrollPtr
->offset
);
960 delta
= eventPtr
->xmotion
.x
- scrollPtr
->pressPos
;
961 length
= Tk_Width(scrollPtr
->tkwin
)
962 - 2*(scrollPtr
->arrowLength
+ scrollPtr
->offset
);
966 * Do the division with positive numbers to avoid
967 * differences in negative-number truncation on different
972 newFirst
= scrollPtr
->pressFirstUnit
973 + ((delta
* scrollPtr
->totalUnits
) + (length
/2))
976 newFirst
= scrollPtr
->pressFirstUnit
977 - (((-delta
) * scrollPtr
->totalUnits
) + (length
/2))
980 ScrollCmd(scrollPtr
, newFirst
);
981 } else if (scrollPtr
->pressField
== -1) {
982 ScrollbarNewField(scrollPtr
,
983 ScrollbarPosition(scrollPtr
, eventPtr
->xmotion
.x
,
984 eventPtr
->xmotion
.y
));
986 } else if ((eventPtr
->type
== ButtonPress
)
987 && ((eventPtr
->xbutton
.state
& ALL_BUTTONS
) == 0)) {
988 scrollPtr
->pressField
= scrollPtr
->mouseField
;
989 if (scrollPtr
->pressField
!= SLIDER
) {
990 scrollPtr
->autoRepeat
= Tk_CreateTimerHandler(
991 scrollPtr
->repeatDelay
,
992 ScrollbarTimerProc
, (ClientData
) scrollPtr
);
994 if (scrollPtr
->vertical
) {
995 scrollPtr
->pressPos
= eventPtr
->xbutton
.y
;
997 scrollPtr
->pressPos
= eventPtr
->xbutton
.x
;
999 scrollPtr
->pressFirstUnit
= scrollPtr
->firstUnit
;
1000 if (scrollPtr
->pressFirstUnit
<= -scrollPtr
->windowUnits
) {
1001 scrollPtr
->pressFirstUnit
= 1-scrollPtr
->windowUnits
;
1003 if (scrollPtr
->pressFirstUnit
>= scrollPtr
->totalUnits
) {
1004 scrollPtr
->pressFirstUnit
= scrollPtr
->totalUnits
-1;
1006 EventuallyRedraw(scrollPtr
);
1007 } else if (eventPtr
->type
== ButtonRelease
) {
1008 if (scrollPtr
->pressField
== scrollPtr
->mouseField
) {
1009 switch (scrollPtr
->pressField
) {
1011 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
-1);
1014 if (scrollPtr
->windowUnits
<= 1) {
1015 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
- 1);
1017 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
1018 - (scrollPtr
->windowUnits
-1));
1022 if (scrollPtr
->windowUnits
<= 1) {
1023 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
+ 1);
1025 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
1026 + (scrollPtr
->windowUnits
-1));
1031 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
+1);
1035 if (scrollPtr
->autoRepeat
!= NULL
) {
1036 Tk_DeleteTimerHandler(scrollPtr
->autoRepeat
);
1037 scrollPtr
->autoRepeat
= NULL
;
1039 EventuallyRedraw(scrollPtr
);
1040 scrollPtr
->pressField
= -1;
1041 ScrollbarNewField(scrollPtr
,
1042 ScrollbarPosition(scrollPtr
, eventPtr
->xbutton
.x
,
1043 eventPtr
->xbutton
.y
));
1045 Tk_Release((ClientData
) scrollPtr
);
1049 *--------------------------------------------------------------
1053 * This procedure takes care of invoking a scrolling Tcl
1054 * command and reporting any error that occurs in it.
1060 * A Tcl command is invoked, and an additional error-processing
1061 * command may also be invoked.
1063 *--------------------------------------------------------------
1068 register Scrollbar
*scrollPtr
, /* Scrollbar from which to issue
1070 int unit
/* Unit position within thing being
1071 * being displayed that should appear
1072 * at top or right of screen. */
1078 if ((unit
== scrollPtr
->firstUnit
) || (scrollPtr
->command
== NULL
)) {
1081 sprintf(string
, " %d", unit
);
1082 result
= Tcl_VarEval(scrollPtr
->interp
, scrollPtr
->command
, string
,
1084 if (result
!= TCL_OK
) {
1085 TkBindError(scrollPtr
->interp
);
1090 *--------------------------------------------------------------
1092 * EventuallyRedraw --
1094 * Arrange for one or more of the fields of a scrollbar
1103 *--------------------------------------------------------------
1108 register Scrollbar
*scrollPtr
/* Information about widget. */
1111 if ((scrollPtr
->tkwin
== NULL
) || (!Tk_IsMapped(scrollPtr
->tkwin
))) {
1114 if ((scrollPtr
->flags
& REDRAW_PENDING
) == 0) {
1115 Tk_DoWhenIdle(DisplayScrollbar
, (ClientData
) scrollPtr
);
1116 scrollPtr
->flags
|= REDRAW_PENDING
;
1121 *--------------------------------------------------------------
1123 * ScrollbarNewField --
1125 * This procedure is called to declare that the mouse is in
1126 * a particular field of the scrollbar (e.g. top arrow), so
1127 * that the field can be highlighed and the previous field
1128 * can be returned to normal display.
1134 * Fields may be redisplayed.
1136 *--------------------------------------------------------------
1141 register Scrollbar
*scrollPtr
, /* Information about widget. */
1142 int field
/* Identifies field under mouse,
1143 * e.g. TOP_ARROW. */
1146 if (field
== scrollPtr
->mouseField
) {
1149 EventuallyRedraw(scrollPtr
);
1150 scrollPtr
->mouseField
= field
;
1154 *--------------------------------------------------------------
1156 * ScrollbarTimerProc --
1158 * This procedure is invoked as a Tk timer handler for actions
1159 * that auto-repeat (mouse presses in an arrow or gap). It
1160 * performs the auto-repeat action.
1166 * Whatever action corresponds to the current mouse button
1167 * is repeated, and this procedure is rescheduled to execute
1170 *--------------------------------------------------------------
1174 ScrollbarTimerProc (
1175 ClientData clientData
/* Information about widget. */
1178 register Scrollbar
*scrollPtr
= (Scrollbar
*) clientData
;
1180 Tk_Preserve((ClientData
) scrollPtr
);
1181 switch(scrollPtr
->pressField
) {
1183 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
-1);
1186 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
1187 - (scrollPtr
->windowUnits
-1));
1190 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
1191 + (scrollPtr
->windowUnits
-1));
1195 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
+1);
1198 if (scrollPtr
->tkwin
!= NULL
) {
1199 scrollPtr
->autoRepeat
= Tk_CreateTimerHandler(
1200 scrollPtr
->repeatInterval
, ScrollbarTimerProc
,
1201 (ClientData
) scrollPtr
);
1203 Tk_Release((ClientData
) scrollPtr
);