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 *--------------------------------------------------------------
233 Tk_ScrollbarCmd(clientData
, interp
, argc
, argv
)
234 ClientData clientData
; /* Main window associated with
236 Tcl_Interp
*interp
; /* Current interpreter. */
237 int argc
; /* Number of arguments. */
238 char **argv
; /* Argument strings. */
240 Tk_Window tkwin
= (Tk_Window
) clientData
;
241 register Scrollbar
*scrollPtr
;
245 Tcl_AppendResult(interp
, "wrong # args: should be \"",
246 argv
[0], " pathName ?options?\"", (char *) NULL
);
250 new = Tk_CreateWindowFromPath(interp
, tkwin
, argv
[1], (char *) NULL
);
256 * Initialize fields that won't be initialized by ConfigureScrollbar,
257 * or which ConfigureScrollbar expects to have reasonable values
258 * (e.g. resource pointers).
261 scrollPtr
= (Scrollbar
*) ckalloc(sizeof(Scrollbar
));
262 scrollPtr
->tkwin
= new;
263 scrollPtr
->interp
= interp
;
264 scrollPtr
->command
= NULL
;
265 scrollPtr
->bgBorder
= NULL
;
266 scrollPtr
->fgBorder
= NULL
;
267 scrollPtr
->activeBorder
= NULL
;
268 scrollPtr
->copyGC
= None
;
269 scrollPtr
->mouseField
= OUTSIDE
;
270 scrollPtr
->pressField
= -1;
271 scrollPtr
->totalUnits
= 0;
272 scrollPtr
->windowUnits
= 0;
273 scrollPtr
->firstUnit
= 0;
274 scrollPtr
->lastUnit
= 0;
275 scrollPtr
->cursor
= None
;
276 scrollPtr
->autoRepeat
= NULL
;
277 scrollPtr
->flags
= 0;
279 Tk_SetClass(scrollPtr
->tkwin
, "Scrollbar");
280 Tk_CreateEventHandler(scrollPtr
->tkwin
, ExposureMask
|StructureNotifyMask
,
281 ScrollbarEventProc
, (ClientData
) scrollPtr
);
282 Tk_CreateEventHandler(scrollPtr
->tkwin
, EnterWindowMask
|LeaveWindowMask
283 |PointerMotionMask
|ButtonPressMask
|ButtonReleaseMask
,
284 ScrollbarMouseProc
, (ClientData
) scrollPtr
);
285 Tcl_CreateCommand(interp
, Tk_PathName(scrollPtr
->tkwin
), ScrollbarWidgetCmd
,
286 (ClientData
) scrollPtr
, (void (*)()) NULL
);
287 if (ConfigureScrollbar(interp
, scrollPtr
, argc
-2, argv
+2, 0) != TCL_OK
) {
291 interp
->result
= Tk_PathName(scrollPtr
->tkwin
);
295 Tk_DestroyWindow(scrollPtr
->tkwin
);
300 *--------------------------------------------------------------
302 * ScrollbarWidgetCmd --
304 * This procedure is invoked to process the Tcl command
305 * that corresponds to a widget managed by this module.
306 * See the user documentation for details on what it does.
309 * A standard Tcl result.
312 * See the user documentation.
314 *--------------------------------------------------------------
318 ScrollbarWidgetCmd(clientData
, interp
, argc
, argv
)
319 ClientData clientData
; /* Information about scrollbar
321 Tcl_Interp
*interp
; /* Current interpreter. */
322 int argc
; /* Number of arguments. */
323 char **argv
; /* Argument strings. */
325 register Scrollbar
*scrollPtr
= (Scrollbar
*) clientData
;
331 Tcl_AppendResult(interp
, "wrong # args: should be \"",
332 argv
[0], " option ?arg arg ...?\"", (char *) NULL
);
335 Tk_Preserve((ClientData
) scrollPtr
);
337 length
= strlen(argv
[1]);
338 if ((c
== 'c') && (strncmp(argv
[1], "configure", length
) == 0)) {
340 result
= Tk_ConfigureInfo(interp
, scrollPtr
->tkwin
, configSpecs
,
341 (char *) scrollPtr
, (char *) NULL
, 0);
342 } else if (argc
== 3) {
343 result
= Tk_ConfigureInfo(interp
, scrollPtr
->tkwin
, configSpecs
,
344 (char *) scrollPtr
, argv
[2], 0);
346 result
= ConfigureScrollbar(interp
, scrollPtr
, argc
-2, argv
+2,
347 TK_CONFIG_ARGV_ONLY
);
349 } else if ((c
== 'g') && (strncmp(argv
[1], "get", length
) == 0)) {
351 Tcl_AppendResult(interp
, "wrong # args: should be \"",
352 argv
[0], " get\"", (char *) NULL
);
355 sprintf(interp
->result
, "%d %d %d %d", scrollPtr
->totalUnits
,
356 scrollPtr
->windowUnits
, scrollPtr
->firstUnit
,
357 scrollPtr
->lastUnit
);
358 } else if ((c
== 's') && (strncmp(argv
[1], "set", length
) == 0)) {
359 int totalUnits
, windowUnits
, firstUnit
, lastUnit
;
362 Tcl_AppendResult(interp
, "wrong # args: should be \"",
364 " set totalUnits windowUnits firstUnit lastUnit\"",
368 if (Tcl_GetInt(interp
, argv
[2], &totalUnits
) != TCL_OK
) {
371 if (totalUnits
< 0) {
372 sprintf(interp
->result
, "illegal totalUnits %d", totalUnits
);
375 if (Tcl_GetInt(interp
, argv
[3], &windowUnits
) != TCL_OK
) {
378 if (windowUnits
< 0) {
379 sprintf(interp
->result
, "illegal windowUnits %d", windowUnits
);
382 if (Tcl_GetInt(interp
, argv
[4], &firstUnit
) != TCL_OK
) {
385 if (Tcl_GetInt(interp
, argv
[5], &lastUnit
) != TCL_OK
) {
388 if (totalUnits
> 0) {
389 if (lastUnit
< firstUnit
) {
390 sprintf(interp
->result
, "illegal lastUnit %d", lastUnit
);
394 firstUnit
= lastUnit
= 0;
396 scrollPtr
->totalUnits
= totalUnits
;
397 scrollPtr
->windowUnits
= windowUnits
;
398 scrollPtr
->firstUnit
= firstUnit
;
399 scrollPtr
->lastUnit
= lastUnit
;
400 ComputeScrollbarGeometry(scrollPtr
);
401 EventuallyRedraw(scrollPtr
);
403 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
404 "\": must be configure, get, or set", (char *) NULL
);
407 Tk_Release((ClientData
) scrollPtr
);
411 Tk_Release((ClientData
) scrollPtr
);
416 *----------------------------------------------------------------------
418 * DestroyScrollbar --
420 * This procedure is invoked by Tk_EventuallyFree or Tk_Release
421 * to clean up the internal structure of a scrollbar at a safe time
422 * (when no-one is using it anymore).
428 * Everything associated with the scrollbar is freed up.
430 *----------------------------------------------------------------------
434 DestroyScrollbar(clientData
)
435 ClientData clientData
; /* Info about scrollbar widget. */
437 register Scrollbar
*scrollPtr
= (Scrollbar
*) clientData
;
439 if (scrollPtr
->command
!= NULL
) {
440 ckfree(scrollPtr
->command
);
442 if (scrollPtr
->bgBorder
!= NULL
) {
443 Tk_Free3DBorder(scrollPtr
->bgBorder
);
445 if (scrollPtr
->fgBorder
!= NULL
) {
446 Tk_Free3DBorder(scrollPtr
->fgBorder
);
448 if (scrollPtr
->activeBorder
!= NULL
) {
449 Tk_Free3DBorder(scrollPtr
->activeBorder
);
451 if (scrollPtr
->copyGC
!= None
) {
452 Tk_FreeGC(scrollPtr
->copyGC
);
454 if (scrollPtr
->cursor
!= None
) {
455 Tk_FreeCursor(scrollPtr
->cursor
);
457 ckfree((char *) scrollPtr
);
461 *----------------------------------------------------------------------
463 * ConfigureScrollbar --
465 * This procedure is called to process an argv/argc list, plus
466 * the Tk option database, in order to configure (or
467 * reconfigure) a scrollbar widget.
470 * The return value is a standard Tcl result. If TCL_ERROR is
471 * returned, then interp->result contains an error message.
474 * Configuration information, such as colors, border width,
475 * etc. get set for scrollPtr; old resources get freed,
478 *----------------------------------------------------------------------
482 ConfigureScrollbar(interp
, scrollPtr
, argc
, argv
, flags
)
483 Tcl_Interp
*interp
; /* Used for error reporting. */
484 register Scrollbar
*scrollPtr
; /* Information about widget; may or
485 * may not already have values for
487 int argc
; /* Number of valid entries in argv. */
488 char **argv
; /* Arguments. */
489 int flags
; /* Flags to pass to
490 * Tk_ConfigureWidget. */
495 if (Tk_ConfigureWidget(interp
, scrollPtr
->tkwin
, configSpecs
,
496 argc
, argv
, (char *) scrollPtr
, flags
) != TCL_OK
) {
501 * A few options need special processing, such as parsing the
502 * orientation or setting the background from a 3-D border.
505 length
= strlen(scrollPtr
->orientUid
);
506 if (strncmp(scrollPtr
->orientUid
, "vertical", length
) == 0) {
507 scrollPtr
->vertical
= 1;
508 } else if (strncmp(scrollPtr
->orientUid
, "horizontal", length
) == 0) {
509 scrollPtr
->vertical
= 0;
511 Tcl_AppendResult(interp
, "bad orientation \"", scrollPtr
->orientUid
,
512 "\": must be vertical or horizontal", (char *) NULL
);
516 if (scrollPtr
->command
!= NULL
) {
517 scrollPtr
->commandSize
= strlen(scrollPtr
->command
);
519 scrollPtr
->commandSize
= 0;
522 Tk_SetBackgroundFromBorder(scrollPtr
->tkwin
, scrollPtr
->bgBorder
);
524 if (scrollPtr
->copyGC
== None
) {
525 gcValues
.graphics_exposures
= False
;
526 scrollPtr
->copyGC
= Tk_GetGC(scrollPtr
->tkwin
, GCGraphicsExposures
,
531 * Register the desired geometry for the window (leave enough space
532 * for the two arrows plus a minimum-size slider, plus border around
533 * the whole window, if any). Then arrange for the window to be
537 ComputeScrollbarGeometry(scrollPtr
);
538 EventuallyRedraw(scrollPtr
);
543 *--------------------------------------------------------------
545 * DisplayScrollbar --
547 * This procedure redraws the contents of a scrollbar window.
548 * It is invoked as a do-when-idle handler, so it only runs
549 * when there's nothing else for the application to do.
555 * Information appears on the screen.
557 *--------------------------------------------------------------
561 DisplayScrollbar(clientData
)
562 ClientData clientData
; /* Information about window. */
564 register Scrollbar
*scrollPtr
= (Scrollbar
*) clientData
;
565 register Tk_Window tkwin
= scrollPtr
->tkwin
;
568 int relief
, width
, fieldLength
;
571 if ((scrollPtr
->tkwin
== NULL
) || !Tk_IsMapped(tkwin
)) {
575 if (scrollPtr
->vertical
) {
576 width
= Tk_Width(tkwin
) - 2*scrollPtr
->offset
;
578 width
= Tk_Height(tkwin
) - 2*scrollPtr
->offset
;
582 * In order to avoid screen flashes, this procedure redraws
583 * the scrollbar in a pixmap, then copies the pixmap to the
584 * screen in a single operation. This means that there's no
585 * point in time where the on-sreen image has been cleared.
588 pixmap
= XCreatePixmap(Tk_Display(tkwin
), Tk_WindowId(tkwin
),
589 Tk_Width(tkwin
), Tk_Height(tkwin
),
590 Tk_DefaultDepth(Tk_Screen(tkwin
)));
591 Tk_Fill3DRectangle(Tk_Display(tkwin
), pixmap
, scrollPtr
->bgBorder
,
592 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
),
593 scrollPtr
->borderWidth
, scrollPtr
->relief
);
596 * Draw the top or left arrow. The coordinates of the polygon
597 * points probably seem odd, but they were carefully chosen with
598 * respect to X's rules for filling polygons. These point choices
599 * cause the arrows to just fill the narrow dimension of the
600 * scrollbar and be properly centered.
603 if (scrollPtr
->mouseField
== TOP_ARROW
) {
604 border
= scrollPtr
->activeBorder
;
605 relief
= scrollPtr
->pressField
== TOP_ARROW
? TK_RELIEF_SUNKEN
608 border
= scrollPtr
->fgBorder
;
609 relief
= TK_RELIEF_RAISED
;
611 if (scrollPtr
->vertical
) {
612 points
[0].x
= scrollPtr
->offset
- 1;
613 points
[0].y
= scrollPtr
->arrowLength
+ scrollPtr
->offset
;
614 points
[1].x
= width
+ scrollPtr
->offset
;
615 points
[1].y
= points
[0].y
;
616 points
[2].x
= width
/2 + scrollPtr
->offset
;
617 points
[2].y
= scrollPtr
->offset
- 1;
618 Tk_Fill3DPolygon(Tk_Display(tkwin
), pixmap
, border
,
619 points
, 3, scrollPtr
->borderWidth
, relief
);
621 points
[0].x
= scrollPtr
->arrowLength
+ scrollPtr
->offset
;
622 points
[0].y
= scrollPtr
->offset
- 1;
623 points
[1].x
= scrollPtr
->offset
;
624 points
[1].y
= width
/2 + scrollPtr
->offset
;
625 points
[2].x
= points
[0].x
;
626 points
[2].y
= width
+ scrollPtr
->offset
;
627 Tk_Fill3DPolygon(Tk_Display(tkwin
), pixmap
, border
,
628 points
, 3, scrollPtr
->borderWidth
, relief
);
632 * Display the bottom or right arrow.
635 if (scrollPtr
->mouseField
== BOTTOM_ARROW
) {
636 border
= scrollPtr
->activeBorder
;
637 relief
= scrollPtr
->pressField
== BOTTOM_ARROW
? TK_RELIEF_SUNKEN
640 border
= scrollPtr
->fgBorder
;
641 relief
= TK_RELIEF_RAISED
;
643 if (scrollPtr
->vertical
) {
644 points
[0].x
= scrollPtr
->offset
;
645 points
[0].y
= Tk_Height(tkwin
) - scrollPtr
->arrowLength
647 points
[1].x
= width
/2 + scrollPtr
->offset
;
648 points
[1].y
= Tk_Height(tkwin
) - scrollPtr
->offset
;
649 points
[2].x
= width
+ scrollPtr
->offset
;
650 points
[2].y
= points
[0].y
;
651 Tk_Fill3DPolygon(Tk_Display(tkwin
), pixmap
, border
,
652 points
, 3, scrollPtr
->borderWidth
, relief
);
654 points
[0].x
= Tk_Width(tkwin
) - scrollPtr
->arrowLength
656 points
[0].y
= scrollPtr
->offset
- 1;
657 points
[1].x
= points
[0].x
;
658 points
[1].y
= width
+ scrollPtr
->offset
;
659 points
[2].x
= Tk_Width(tkwin
) - scrollPtr
->offset
;
660 points
[2].y
= width
/2 + scrollPtr
->offset
;
661 Tk_Fill3DPolygon(Tk_Display(tkwin
), pixmap
, border
,
662 points
, 3, scrollPtr
->borderWidth
, relief
);
666 * Display the slider.
669 if (scrollPtr
->mouseField
== SLIDER
) {
670 border
= scrollPtr
->activeBorder
;
671 relief
= scrollPtr
->pressField
== SLIDER
? TK_RELIEF_SUNKEN
674 border
= scrollPtr
->fgBorder
;
675 relief
= TK_RELIEF_RAISED
;
677 fieldLength
= (scrollPtr
->vertical
? Tk_Height(tkwin
) : Tk_Width(tkwin
))
678 - 2*(scrollPtr
->arrowLength
+ scrollPtr
->offset
);
679 if (fieldLength
< 0) {
682 if (scrollPtr
->vertical
) {
683 Tk_Fill3DRectangle(Tk_Display(tkwin
), pixmap
, border
,
684 1 + scrollPtr
->offset
, scrollPtr
->sliderFirst
,
685 width
-2, scrollPtr
->sliderLast
- scrollPtr
->sliderFirst
,
686 scrollPtr
->borderWidth
, relief
);
688 Tk_Fill3DRectangle(Tk_Display(tkwin
), pixmap
, border
,
689 scrollPtr
->sliderFirst
, 1 + scrollPtr
->offset
,
690 scrollPtr
->sliderLast
- scrollPtr
->sliderFirst
, width
-2,
691 scrollPtr
->borderWidth
, relief
);
695 * Copy the information from the off-screen pixmap onto the screen,
696 * then delete the pixmap.
699 XCopyArea(Tk_Display(tkwin
), pixmap
, Tk_WindowId(tkwin
),
700 scrollPtr
->copyGC
, 0, 0, Tk_Width(tkwin
), Tk_Height(tkwin
), 0, 0);
701 XFreePixmap(Tk_Display(tkwin
), pixmap
);
704 scrollPtr
->flags
&= ~REDRAW_PENDING
;
708 *--------------------------------------------------------------
710 * ScrollbarEventProc --
712 * This procedure is invoked by the Tk dispatcher for various
713 * events on scrollbars.
719 * When the window gets deleted, internal structures get
720 * cleaned up. When it gets exposed, it is redisplayed.
722 *--------------------------------------------------------------
726 ScrollbarEventProc(clientData
, eventPtr
)
727 ClientData clientData
; /* Information about window. */
728 XEvent
*eventPtr
; /* Information about event. */
730 Scrollbar
*scrollPtr
= (Scrollbar
*) clientData
;
732 if ((eventPtr
->type
== Expose
) && (eventPtr
->xexpose
.count
== 0)) {
733 EventuallyRedraw(scrollPtr
);
734 } else if (eventPtr
->type
== DestroyNotify
) {
735 Tcl_DeleteCommand(scrollPtr
->interp
, Tk_PathName(scrollPtr
->tkwin
));
736 scrollPtr
->tkwin
= NULL
;
737 if (scrollPtr
->flags
& REDRAW_PENDING
) {
738 Tk_CancelIdleCall(DisplayScrollbar
, (ClientData
) scrollPtr
);
740 Tk_EventuallyFree((ClientData
) scrollPtr
, DestroyScrollbar
);
741 } else if (eventPtr
->type
== ConfigureNotify
) {
742 ComputeScrollbarGeometry(scrollPtr
);
747 *----------------------------------------------------------------------
749 * ComputeScrollbarGeometry --
751 * After changes in a scrollbar's size or configuration, this
752 * procedure recomputes various geometry information used in
753 * displaying the scrollbar.
759 * The scrollbar will be displayed differently.
761 *----------------------------------------------------------------------
765 ComputeScrollbarGeometry(scrollPtr
)
766 register Scrollbar
*scrollPtr
; /* Scrollbar whose geometry may
769 int width
, fieldLength
;
771 if (scrollPtr
->relief
== TK_RELIEF_FLAT
) {
772 scrollPtr
->offset
= 0;
774 scrollPtr
->offset
= scrollPtr
->borderWidth
;
776 width
= (scrollPtr
->vertical
) ? Tk_Width(scrollPtr
->tkwin
)
777 : Tk_Height(scrollPtr
->tkwin
);
778 scrollPtr
->arrowLength
=
779 (((width
- 2*scrollPtr
->offset
)*173) + 100) / 200;
780 fieldLength
= (scrollPtr
->vertical
? Tk_Height(scrollPtr
->tkwin
)
781 : Tk_Width(scrollPtr
->tkwin
))
782 - 2*(scrollPtr
->arrowLength
+ scrollPtr
->offset
);
783 if (fieldLength
< 0) {
786 if (scrollPtr
->totalUnits
<= 0) {
787 scrollPtr
->sliderFirst
= 0;
788 scrollPtr
->sliderLast
= fieldLength
;
790 scrollPtr
->sliderFirst
= (fieldLength
*scrollPtr
->firstUnit
791 + scrollPtr
->totalUnits
/2)/scrollPtr
->totalUnits
;
792 scrollPtr
->sliderLast
= (fieldLength
*(scrollPtr
->lastUnit
+1)
793 + scrollPtr
->totalUnits
/2)/scrollPtr
->totalUnits
;
796 * Adjust the slider so that some piece of it is always
797 * displayed in the scrollbar and so that it has at least
798 * a minimal width (so it can be grabbed with the mouse).
801 if (scrollPtr
->sliderFirst
> (fieldLength
- 2*scrollPtr
->borderWidth
)) {
802 scrollPtr
->sliderFirst
= fieldLength
- 2*scrollPtr
->borderWidth
;
804 if (scrollPtr
->sliderFirst
< 0) {
805 scrollPtr
->sliderFirst
= 0;
807 if (scrollPtr
->sliderLast
< (scrollPtr
->sliderFirst
808 + 2*scrollPtr
->borderWidth
)) {
809 scrollPtr
->sliderLast
= scrollPtr
->sliderFirst
810 + 2*scrollPtr
->borderWidth
;
812 if (scrollPtr
->sliderLast
> fieldLength
) {
813 scrollPtr
->sliderLast
= fieldLength
;
816 scrollPtr
->sliderFirst
+= scrollPtr
->arrowLength
+ scrollPtr
->offset
;
817 scrollPtr
->sliderLast
+= scrollPtr
->arrowLength
+ scrollPtr
->offset
;
820 * Register the desired geometry for the window (leave enough space
821 * for the two arrows plus a minimum-size slider, plus border around
822 * the whole window, if any). Then arrange for the window to be
826 if (scrollPtr
->vertical
) {
827 Tk_GeometryRequest(scrollPtr
->tkwin
,
828 scrollPtr
->width
+ 2*scrollPtr
->offset
,
829 2*(scrollPtr
->arrowLength
+ scrollPtr
->borderWidth
830 + scrollPtr
->offset
));
832 Tk_GeometryRequest(scrollPtr
->tkwin
,
833 2*(scrollPtr
->arrowLength
+ scrollPtr
->borderWidth
834 + scrollPtr
->offset
), scrollPtr
->width
+ 2*scrollPtr
->offset
);
836 Tk_SetInternalBorder(scrollPtr
->tkwin
, scrollPtr
->borderWidth
);
841 *--------------------------------------------------------------
843 * ScrollbarPosition --
845 * Determine the scrollbar element corresponding to a
849 * One of TOP_ARROW, TOP_GAP, etc., indicating which element
850 * of the scrollbar covers the position given by (x, y). If
851 * (x,y) is outside the scrollbar entirely, then OUTSIDE is
857 *--------------------------------------------------------------
861 ScrollbarPosition(scrollPtr
, x
, y
)
862 register Scrollbar
*scrollPtr
; /* Scrollbar widget record. */
863 int x
, y
; /* Coordinates within scrollPtr's
866 int length
, width
, tmp
;
868 if (scrollPtr
->vertical
) {
869 length
= Tk_Height(scrollPtr
->tkwin
);
870 width
= Tk_Width(scrollPtr
->tkwin
);
875 length
= Tk_Width(scrollPtr
->tkwin
);
876 width
= Tk_Height(scrollPtr
->tkwin
);
879 if ((x
< 0) || (x
> width
) || (y
< 0)) {
884 * All of the calculations in this procedure mirror those in
885 * DisplayScrollbar. Be sure to keep the two consistent.
888 if (y
< (scrollPtr
->offset
+ scrollPtr
->arrowLength
)) {
891 if (y
< scrollPtr
->sliderFirst
) {
894 if (y
< scrollPtr
->sliderLast
) {
897 if (y
>= (length
- (scrollPtr
->arrowLength
+ scrollPtr
->offset
))) {
904 *--------------------------------------------------------------
906 * ScrollbarMouseProc --
908 * This procedure is called back by Tk in response to
909 * mouse events such as window entry, window exit, mouse
910 * motion, and button presses.
916 * This procedure implements the "feel" of the scrollbar
917 * by issuing scrolling commands in response to button presses
920 *--------------------------------------------------------------
924 ScrollbarMouseProc(clientData
, eventPtr
)
925 ClientData clientData
; /* Information about window. */
926 register XEvent
*eventPtr
; /* Information about event. */
928 register Scrollbar
*scrollPtr
= (Scrollbar
*) clientData
;
930 Tk_Preserve((ClientData
) scrollPtr
);
931 if (eventPtr
->type
== EnterNotify
) {
932 if (scrollPtr
->pressField
== -1) {
933 ScrollbarNewField(scrollPtr
,
934 ScrollbarPosition(scrollPtr
, eventPtr
->xcrossing
.x
,
935 eventPtr
->xcrossing
.y
));
937 } else if (eventPtr
->type
== LeaveNotify
) {
938 if (scrollPtr
->pressField
== -1) {
939 ScrollbarNewField(scrollPtr
, OUTSIDE
);
941 } else if (eventPtr
->type
== MotionNotify
) {
942 if (scrollPtr
->pressField
== SLIDER
) {
943 int delta
, length
, newFirst
;
945 if (scrollPtr
->vertical
) {
946 delta
= eventPtr
->xmotion
.y
- scrollPtr
->pressPos
;
947 length
= Tk_Height(scrollPtr
->tkwin
)
948 - 2*(scrollPtr
->arrowLength
+ scrollPtr
->offset
);
950 delta
= eventPtr
->xmotion
.x
- scrollPtr
->pressPos
;
951 length
= Tk_Width(scrollPtr
->tkwin
)
952 - 2*(scrollPtr
->arrowLength
+ scrollPtr
->offset
);
956 * Do the division with positive numbers to avoid
957 * differences in negative-number truncation on different
962 newFirst
= scrollPtr
->pressFirstUnit
963 + ((delta
* scrollPtr
->totalUnits
) + (length
/2))
966 newFirst
= scrollPtr
->pressFirstUnit
967 - (((-delta
) * scrollPtr
->totalUnits
) + (length
/2))
970 ScrollCmd(scrollPtr
, newFirst
);
971 } else if (scrollPtr
->pressField
== -1) {
972 ScrollbarNewField(scrollPtr
,
973 ScrollbarPosition(scrollPtr
, eventPtr
->xmotion
.x
,
974 eventPtr
->xmotion
.y
));
976 } else if ((eventPtr
->type
== ButtonPress
)
977 && ((eventPtr
->xbutton
.state
& ALL_BUTTONS
) == 0)) {
978 scrollPtr
->pressField
= scrollPtr
->mouseField
;
979 if (scrollPtr
->pressField
!= SLIDER
) {
980 scrollPtr
->autoRepeat
= Tk_CreateTimerHandler(
981 scrollPtr
->repeatDelay
,
982 ScrollbarTimerProc
, (ClientData
) scrollPtr
);
984 if (scrollPtr
->vertical
) {
985 scrollPtr
->pressPos
= eventPtr
->xbutton
.y
;
987 scrollPtr
->pressPos
= eventPtr
->xbutton
.x
;
989 scrollPtr
->pressFirstUnit
= scrollPtr
->firstUnit
;
990 if (scrollPtr
->pressFirstUnit
<= -scrollPtr
->windowUnits
) {
991 scrollPtr
->pressFirstUnit
= 1-scrollPtr
->windowUnits
;
993 if (scrollPtr
->pressFirstUnit
>= scrollPtr
->totalUnits
) {
994 scrollPtr
->pressFirstUnit
= scrollPtr
->totalUnits
-1;
996 EventuallyRedraw(scrollPtr
);
997 } else if (eventPtr
->type
== ButtonRelease
) {
998 if (scrollPtr
->pressField
== scrollPtr
->mouseField
) {
999 switch (scrollPtr
->pressField
) {
1001 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
-1);
1004 if (scrollPtr
->windowUnits
<= 1) {
1005 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
- 1);
1007 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
1008 - (scrollPtr
->windowUnits
-1));
1012 if (scrollPtr
->windowUnits
<= 1) {
1013 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
+ 1);
1015 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
1016 + (scrollPtr
->windowUnits
-1));
1021 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
+1);
1025 if (scrollPtr
->autoRepeat
!= NULL
) {
1026 Tk_DeleteTimerHandler(scrollPtr
->autoRepeat
);
1027 scrollPtr
->autoRepeat
= NULL
;
1029 EventuallyRedraw(scrollPtr
);
1030 scrollPtr
->pressField
= -1;
1031 ScrollbarNewField(scrollPtr
,
1032 ScrollbarPosition(scrollPtr
, eventPtr
->xbutton
.x
,
1033 eventPtr
->xbutton
.y
));
1035 Tk_Release((ClientData
) scrollPtr
);
1039 *--------------------------------------------------------------
1043 * This procedure takes care of invoking a scrolling Tcl
1044 * command and reporting any error that occurs in it.
1050 * A Tcl command is invoked, and an additional error-processing
1051 * command may also be invoked.
1053 *--------------------------------------------------------------
1057 ScrollCmd(scrollPtr
, unit
)
1058 register Scrollbar
*scrollPtr
; /* Scrollbar from which to issue
1060 int unit
; /* Unit position within thing being
1061 * being displayed that should appear
1062 * at top or right of screen. */
1067 if ((unit
== scrollPtr
->firstUnit
) || (scrollPtr
->command
== NULL
)) {
1070 sprintf(string
, " %d", unit
);
1071 result
= Tcl_VarEval(scrollPtr
->interp
, scrollPtr
->command
, string
,
1073 if (result
!= TCL_OK
) {
1074 TkBindError(scrollPtr
->interp
);
1079 *--------------------------------------------------------------
1081 * EventuallyRedraw --
1083 * Arrange for one or more of the fields of a scrollbar
1092 *--------------------------------------------------------------
1096 EventuallyRedraw(scrollPtr
)
1097 register Scrollbar
*scrollPtr
; /* Information about widget. */
1099 if ((scrollPtr
->tkwin
== NULL
) || (!Tk_IsMapped(scrollPtr
->tkwin
))) {
1102 if ((scrollPtr
->flags
& REDRAW_PENDING
) == 0) {
1103 Tk_DoWhenIdle(DisplayScrollbar
, (ClientData
) scrollPtr
);
1104 scrollPtr
->flags
|= REDRAW_PENDING
;
1109 *--------------------------------------------------------------
1111 * ScrollbarNewField --
1113 * This procedure is called to declare that the mouse is in
1114 * a particular field of the scrollbar (e.g. top arrow), so
1115 * that the field can be highlighed and the previous field
1116 * can be returned to normal display.
1122 * Fields may be redisplayed.
1124 *--------------------------------------------------------------
1128 ScrollbarNewField(scrollPtr
, field
)
1129 register Scrollbar
*scrollPtr
; /* Information about widget. */
1130 int field
; /* Identifies field under mouse,
1131 * e.g. TOP_ARROW. */
1133 if (field
== scrollPtr
->mouseField
) {
1136 EventuallyRedraw(scrollPtr
);
1137 scrollPtr
->mouseField
= field
;
1141 *--------------------------------------------------------------
1143 * ScrollbarTimerProc --
1145 * This procedure is invoked as a Tk timer handler for actions
1146 * that auto-repeat (mouse presses in an arrow or gap). It
1147 * performs the auto-repeat action.
1153 * Whatever action corresponds to the current mouse button
1154 * is repeated, and this procedure is rescheduled to execute
1157 *--------------------------------------------------------------
1161 ScrollbarTimerProc(clientData
)
1162 ClientData clientData
; /* Information about widget. */
1164 register Scrollbar
*scrollPtr
= (Scrollbar
*) clientData
;
1166 Tk_Preserve((ClientData
) scrollPtr
);
1167 switch(scrollPtr
->pressField
) {
1169 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
-1);
1172 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
1173 - (scrollPtr
->windowUnits
-1));
1176 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
1177 + (scrollPtr
->windowUnits
-1));
1181 ScrollCmd(scrollPtr
, scrollPtr
->firstUnit
+1);
1184 if (scrollPtr
->tkwin
!= NULL
) {
1185 scrollPtr
->autoRepeat
= Tk_CreateTimerHandler(
1186 scrollPtr
->repeatInterval
, ScrollbarTimerProc
,
1187 (ClientData
) scrollPtr
);
1189 Tk_Release((ClientData
) scrollPtr
);