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 (c) 1990-1994 The Regents of the University of California.
10 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
12 * See the file "license.terms" for information on usage and redistribution
13 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
19 #include "tkScrollbar.h"
23 * Custom option for handling "-orient"
26 static Tk_CustomOption orientOption = {
27 (Tk_OptionParseProc *) TkOrientParseProc,
33 * Information used for argv parsing.
36 Tk_ConfigSpec tkpScrollbarConfigSpecs[] = {
37 {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
38 DEF_SCROLLBAR_ACTIVE_BG_COLOR, Tk_Offset(TkScrollbar, activeBorder),
39 TK_CONFIG_COLOR_ONLY},
40 {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
41 DEF_SCROLLBAR_ACTIVE_BG_MONO, Tk_Offset(TkScrollbar, activeBorder),
43 {TK_CONFIG_RELIEF, "-activerelief", "activeRelief", "Relief",
44 DEF_SCROLLBAR_ACTIVE_RELIEF, Tk_Offset(TkScrollbar, activeRelief), 0},
45 {TK_CONFIG_BORDER, "-background", "background", "Background",
46 DEF_SCROLLBAR_BG_COLOR, Tk_Offset(TkScrollbar, bgBorder),
47 TK_CONFIG_COLOR_ONLY},
48 {TK_CONFIG_BORDER, "-background", "background", "Background",
49 DEF_SCROLLBAR_BG_MONO, Tk_Offset(TkScrollbar, bgBorder),
51 {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
53 {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
55 {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
56 DEF_SCROLLBAR_BORDER_WIDTH, Tk_Offset(TkScrollbar, borderWidth), 0},
57 {TK_CONFIG_STRING, "-command", "command", "Command",
58 DEF_SCROLLBAR_COMMAND, Tk_Offset(TkScrollbar, command),
60 {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
61 DEF_SCROLLBAR_CURSOR, Tk_Offset(TkScrollbar, cursor), TK_CONFIG_NULL_OK},
62 {TK_CONFIG_PIXELS, "-elementborderwidth", "elementBorderWidth",
63 "BorderWidth", DEF_SCROLLBAR_EL_BORDER_WIDTH,
64 Tk_Offset(TkScrollbar, elementBorderWidth), 0},
65 {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
66 "HighlightBackground", DEF_SCROLLBAR_HIGHLIGHT_BG,
67 Tk_Offset(TkScrollbar, highlightBgColorPtr), 0},
68 {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
69 DEF_SCROLLBAR_HIGHLIGHT,
70 Tk_Offset(TkScrollbar, highlightColorPtr), 0},
71 {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
73 DEF_SCROLLBAR_HIGHLIGHT_WIDTH, Tk_Offset(TkScrollbar, highlightWidth), 0},
74 {TK_CONFIG_BOOLEAN, "-jump", "jump", "Jump",
75 DEF_SCROLLBAR_JUMP, Tk_Offset(TkScrollbar, jump), 0},
76 {TK_CONFIG_CUSTOM, "-orient", "orient", "Orient",
77 DEF_SCROLLBAR_ORIENT, Tk_Offset(TkScrollbar, vertical), 0,
79 {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
80 DEF_SCROLLBAR_RELIEF, Tk_Offset(TkScrollbar, relief), 0},
81 {TK_CONFIG_INT, "-repeatdelay", "repeatDelay", "RepeatDelay",
82 DEF_SCROLLBAR_REPEAT_DELAY, Tk_Offset(TkScrollbar, repeatDelay), 0},
83 {TK_CONFIG_INT, "-repeatinterval", "repeatInterval", "RepeatInterval",
84 DEF_SCROLLBAR_REPEAT_INTERVAL, Tk_Offset(TkScrollbar, repeatInterval), 0},
85 {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
86 DEF_SCROLLBAR_TAKE_FOCUS, Tk_Offset(TkScrollbar, takeFocus),
88 {TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
89 DEF_SCROLLBAR_TROUGH_COLOR, Tk_Offset(TkScrollbar, troughColorPtr),
90 TK_CONFIG_COLOR_ONLY},
91 {TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
92 DEF_SCROLLBAR_TROUGH_MONO, Tk_Offset(TkScrollbar, troughColorPtr),
94 {TK_CONFIG_PIXELS, "-width", "width", "Width",
95 DEF_SCROLLBAR_WIDTH, Tk_Offset(TkScrollbar, width), 0},
96 {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
101 * Forward declarations for procedures defined later in this file:
104 static int ConfigureScrollbar _ANSI_ARGS_((Tcl_Interp *interp,
105 TkScrollbar *scrollPtr, int argc, char **argv,
107 static void ScrollbarCmdDeletedProc _ANSI_ARGS_((
108 ClientData clientData));
109 static int ScrollbarWidgetCmd _ANSI_ARGS_((ClientData clientData,
110 Tcl_Interp *, int argc, char **argv));
113 *--------------------------------------------------------------
117 * This procedure is invoked to process the "scrollbar" Tcl
118 * command. See the user documentation for details on what
122 * A standard Tcl result.
125 * See the user documentation.
127 *--------------------------------------------------------------
131 Tk_ScrollbarCmd(clientData, interp, argc, argv)
132 ClientData clientData; /* Main window associated with
134 Tcl_Interp *interp; /* Current interpreter. */
135 int argc; /* Number of arguments. */
136 char **argv; /* Argument strings. */
138 Tk_Window tkwin = (Tk_Window) clientData;
139 register TkScrollbar *scrollPtr;
143 Tcl_AppendResult(interp, "wrong # args: should be \"",
144 argv[0], " pathName ?options?\"", (char *) NULL);
148 new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
153 Tk_SetClass(new, "Scrollbar");
154 scrollPtr = TkpCreateScrollbar(new);
156 TkSetClassProcs(new, &tkpScrollbarProcs, (ClientData) scrollPtr);
159 * Initialize fields that won't be initialized by ConfigureScrollbar,
160 * or which ConfigureScrollbar expects to have reasonable values
161 * (e.g. resource pointers).
164 scrollPtr->tkwin = new;
165 scrollPtr->display = Tk_Display(new);
166 scrollPtr->interp = interp;
167 scrollPtr->widgetCmd = Tcl_CreateCommand(interp,
168 Tk_PathName(scrollPtr->tkwin), ScrollbarWidgetCmd,
169 (ClientData) scrollPtr, ScrollbarCmdDeletedProc);
170 scrollPtr->vertical = 0;
171 scrollPtr->width = 0;
172 scrollPtr->command = NULL;
173 scrollPtr->commandSize = 0;
174 scrollPtr->repeatDelay = 0;
175 scrollPtr->repeatInterval = 0;
176 scrollPtr->borderWidth = 0;
177 scrollPtr->bgBorder = NULL;
178 scrollPtr->activeBorder = NULL;
179 scrollPtr->troughColorPtr = NULL;
180 scrollPtr->relief = TK_RELIEF_FLAT;
181 scrollPtr->highlightWidth = 0;
182 scrollPtr->highlightBgColorPtr = NULL;
183 scrollPtr->highlightColorPtr = NULL;
184 scrollPtr->inset = 0;
185 scrollPtr->elementBorderWidth = -1;
186 scrollPtr->arrowLength = 0;
187 scrollPtr->sliderFirst = 0;
188 scrollPtr->sliderLast = 0;
189 scrollPtr->activeField = 0;
190 scrollPtr->activeRelief = TK_RELIEF_RAISED;
191 scrollPtr->totalUnits = 0;
192 scrollPtr->windowUnits = 0;
193 scrollPtr->firstUnit = 0;
194 scrollPtr->lastUnit = 0;
195 scrollPtr->firstFraction = 0.0;
196 scrollPtr->lastFraction = 0.0;
197 scrollPtr->cursor = None;
198 scrollPtr->takeFocus = NULL;
199 scrollPtr->flags = 0;
201 if (ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2, 0) != TCL_OK) {
202 Tk_DestroyWindow(scrollPtr->tkwin);
206 Tcl_SetResult(interp, Tk_PathName(scrollPtr->tkwin), TCL_STATIC);
211 *--------------------------------------------------------------
213 * ScrollbarWidgetCmd --
215 * This procedure is invoked to process the Tcl command
216 * that corresponds to a widget managed by this module.
217 * See the user documentation for details on what it does.
220 * A standard Tcl result.
223 * See the user documentation.
225 *--------------------------------------------------------------
229 ScrollbarWidgetCmd(clientData, interp, argc, argv)
230 ClientData clientData; /* Information about scrollbar
232 Tcl_Interp *interp; /* Current interpreter. */
233 int argc; /* Number of arguments. */
234 char **argv; /* Argument strings. */
236 register TkScrollbar *scrollPtr = (TkScrollbar *) clientData;
242 Tcl_AppendResult(interp, "wrong # args: should be \"",
243 argv[0], " option ?arg arg ...?\"", (char *) NULL);
246 Tcl_Preserve((ClientData) scrollPtr);
248 length = strlen(argv[1]);
249 if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) {
252 switch (scrollPtr->activeField) {
254 Tcl_SetResult(interp, "arrow1", TCL_STATIC);
257 Tcl_SetResult(interp, "slider", TCL_STATIC);
260 Tcl_SetResult(interp, "arrow2", TCL_STATIC);
266 Tcl_AppendResult(interp, "wrong # args: should be \"",
267 argv[0], " activate element\"", (char *) NULL);
271 length = strlen(argv[2]);
272 oldActiveField = scrollPtr->activeField;
273 if ((c == 'a') && (strcmp(argv[2], "arrow1") == 0)) {
274 scrollPtr->activeField = TOP_ARROW;
275 } else if ((c == 'a') && (strcmp(argv[2], "arrow2") == 0)) {
276 scrollPtr->activeField = BOTTOM_ARROW;
277 } else if ((c == 's') && (strncmp(argv[2], "slider", length) == 0)) {
278 scrollPtr->activeField = SLIDER;
280 scrollPtr->activeField = OUTSIDE;
282 if (oldActiveField != scrollPtr->activeField) {
283 TkScrollbarEventuallyRedraw(scrollPtr);
285 } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
288 Tcl_AppendResult(interp, "wrong # args: should be \"",
289 argv[0], " cget option\"",
293 result = Tk_ConfigureValue(interp, scrollPtr->tkwin,
294 tkpScrollbarConfigSpecs, (char *) scrollPtr, argv[2], 0);
295 } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
298 result = Tk_ConfigureInfo(interp, scrollPtr->tkwin,
299 tkpScrollbarConfigSpecs, (char *) scrollPtr,
301 } else if (argc == 3) {
302 result = Tk_ConfigureInfo(interp, scrollPtr->tkwin,
303 tkpScrollbarConfigSpecs, (char *) scrollPtr, argv[2], 0);
305 result = ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2,
306 TK_CONFIG_ARGV_ONLY);
308 } else if ((c == 'd') && (strncmp(argv[1], "delta", length) == 0)) {
309 int xDelta, yDelta, pixels, length;
311 char buf[TCL_DOUBLE_SPACE];
314 Tcl_AppendResult(interp, "wrong # args: should be \"",
315 argv[0], " delta xDelta yDelta\"", (char *) NULL);
318 if ((Tcl_GetInt(interp, argv[2], &xDelta) != TCL_OK)
319 || (Tcl_GetInt(interp, argv[3], &yDelta) != TCL_OK)) {
322 if (scrollPtr->vertical) {
324 length = Tk_Height(scrollPtr->tkwin) - 1
325 - 2*(scrollPtr->arrowLength + scrollPtr->inset);
328 length = Tk_Width(scrollPtr->tkwin) - 1
329 - 2*(scrollPtr->arrowLength + scrollPtr->inset);
334 fraction = ((double) pixels / (double) length);
336 sprintf(buf, "%g", fraction);
337 Tcl_SetResult(interp, buf, TCL_VOLATILE);
338 } else if ((c == 'f') && (strncmp(argv[1], "fraction", length) == 0)) {
339 int x, y, pos, length;
341 char buf[TCL_DOUBLE_SPACE];
344 Tcl_AppendResult(interp, "wrong # args: should be \"",
345 argv[0], " fraction x y\"", (char *) NULL);
348 if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
349 || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
352 if (scrollPtr->vertical) {
353 pos = y - (scrollPtr->arrowLength + scrollPtr->inset);
354 length = Tk_Height(scrollPtr->tkwin) - 1
355 - 2*(scrollPtr->arrowLength + scrollPtr->inset);
357 pos = x - (scrollPtr->arrowLength + scrollPtr->inset);
358 length = Tk_Width(scrollPtr->tkwin) - 1
359 - 2*(scrollPtr->arrowLength + scrollPtr->inset);
364 fraction = ((double) pos / (double) length);
368 } else if (fraction > 1.0) {
371 sprintf(buf, "%g", fraction);
372 Tcl_SetResult(interp, buf, TCL_VOLATILE);
373 } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
375 Tcl_AppendResult(interp, "wrong # args: should be \"",
376 argv[0], " get\"", (char *) NULL);
379 if (scrollPtr->flags & NEW_STYLE_COMMANDS) {
380 char first[TCL_DOUBLE_SPACE], last[TCL_DOUBLE_SPACE];
382 Tcl_PrintDouble(interp, scrollPtr->firstFraction, first);
383 Tcl_PrintDouble(interp, scrollPtr->lastFraction, last);
384 Tcl_AppendResult(interp, first, " ", last, (char *) NULL);
386 char buf[TCL_INTEGER_SPACE * 4];
388 sprintf(buf, "%d %d %d %d", scrollPtr->totalUnits,
389 scrollPtr->windowUnits, scrollPtr->firstUnit,
390 scrollPtr->lastUnit);
391 Tcl_SetResult(interp, buf, TCL_VOLATILE);
393 } else if ((c == 'i') && (strncmp(argv[1], "identify", length) == 0)) {
397 Tcl_AppendResult(interp, "wrong # args: should be \"",
398 argv[0], " identify x y\"", (char *) NULL);
401 if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
402 || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
405 thing = TkpScrollbarPosition(scrollPtr, x,y);
408 Tcl_SetResult(interp, "arrow1", TCL_STATIC);
411 Tcl_SetResult(interp, "trough1", TCL_STATIC);
414 Tcl_SetResult(interp, "slider", TCL_STATIC);
417 Tcl_SetResult(interp, "trough2", TCL_STATIC);
420 Tcl_SetResult(interp, "arrow2", TCL_STATIC);
423 } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
424 int totalUnits, windowUnits, firstUnit, lastUnit;
429 if (Tcl_GetDouble(interp, argv[2], &first) != TCL_OK) {
432 if (Tcl_GetDouble(interp, argv[3], &last) != TCL_OK) {
436 scrollPtr->firstFraction = 0;
437 } else if (first > 1.0) {
438 scrollPtr->firstFraction = 1.0;
440 scrollPtr->firstFraction = first;
442 if (last < scrollPtr->firstFraction) {
443 scrollPtr->lastFraction = scrollPtr->firstFraction;
444 } else if (last > 1.0) {
445 scrollPtr->lastFraction = 1.0;
447 scrollPtr->lastFraction = last;
449 scrollPtr->flags |= NEW_STYLE_COMMANDS;
450 } else if (argc == 6) {
451 if (Tcl_GetInt(interp, argv[2], &totalUnits) != TCL_OK) {
454 if (totalUnits < 0) {
457 if (Tcl_GetInt(interp, argv[3], &windowUnits) != TCL_OK) {
460 if (windowUnits < 0) {
463 if (Tcl_GetInt(interp, argv[4], &firstUnit) != TCL_OK) {
466 if (Tcl_GetInt(interp, argv[5], &lastUnit) != TCL_OK) {
469 if (totalUnits > 0) {
470 if (lastUnit < firstUnit) {
471 lastUnit = firstUnit;
474 firstUnit = lastUnit = 0;
476 scrollPtr->totalUnits = totalUnits;
477 scrollPtr->windowUnits = windowUnits;
478 scrollPtr->firstUnit = firstUnit;
479 scrollPtr->lastUnit = lastUnit;
480 if (scrollPtr->totalUnits == 0) {
481 scrollPtr->firstFraction = 0.0;
482 scrollPtr->lastFraction = 1.0;
484 scrollPtr->firstFraction = ((double) firstUnit)/totalUnits;
485 scrollPtr->lastFraction = ((double) (lastUnit+1))/totalUnits;
487 scrollPtr->flags &= ~NEW_STYLE_COMMANDS;
489 Tcl_AppendResult(interp, "wrong # args: should be \"",
490 argv[0], " set firstFraction lastFraction\" or \"",
492 " set totalUnits windowUnits firstUnit lastUnit\"",
496 TkpComputeScrollbarGeometry(scrollPtr);
497 TkScrollbarEventuallyRedraw(scrollPtr);
499 Tcl_AppendResult(interp, "bad option \"", argv[1],
500 "\": must be activate, cget, configure, delta, fraction, ",
501 "get, identify, or set", (char *) NULL);
505 Tcl_Release((ClientData) scrollPtr);
509 Tcl_Release((ClientData) scrollPtr);
514 *----------------------------------------------------------------------
516 * ConfigureScrollbar --
518 * This procedure is called to process an argv/argc list, plus
519 * the Tk option database, in order to configure (or
520 * reconfigure) a scrollbar widget.
523 * The return value is a standard Tcl result. If TCL_ERROR is
524 * returned, then the interp's result contains an error message.
527 * Configuration information, such as colors, border width,
528 * etc. get set for scrollPtr; old resources get freed,
531 *----------------------------------------------------------------------
535 ConfigureScrollbar(interp, scrollPtr, argc, argv, flags)
536 Tcl_Interp *interp; /* Used for error reporting. */
537 register TkScrollbar *scrollPtr; /* Information about widget; may or
538 * may not already have values for
540 int argc; /* Number of valid entries in argv. */
541 char **argv; /* Arguments. */
542 int flags; /* Flags to pass to
543 * Tk_ConfigureWidget. */
545 if (Tk_ConfigureWidget(interp, scrollPtr->tkwin, tkpScrollbarConfigSpecs,
546 argc, argv, (char *) scrollPtr, flags) != TCL_OK) {
551 * A few options need special processing, such as setting the
552 * background from a 3-D border.
555 if (scrollPtr->command != NULL) {
556 scrollPtr->commandSize = strlen(scrollPtr->command);
558 scrollPtr->commandSize = 0;
562 * Configure platform specific options.
565 TkpConfigureScrollbar(scrollPtr);
568 * Register the desired geometry for the window (leave enough space
569 * for the two arrows plus a minimum-size slider, plus border around
570 * the whole window, if any). Then arrange for the window to be
574 TkpComputeScrollbarGeometry(scrollPtr);
575 TkScrollbarEventuallyRedraw(scrollPtr);
580 *--------------------------------------------------------------
582 * TkScrollbarEventProc --
584 * This procedure is invoked by the Tk dispatcher for various
585 * events on scrollbars.
591 * When the window gets deleted, internal structures get
592 * cleaned up. When it gets exposed, it is redisplayed.
594 *--------------------------------------------------------------
598 TkScrollbarEventProc(clientData, eventPtr)
599 ClientData clientData; /* Information about window. */
600 XEvent *eventPtr; /* Information about event. */
602 TkScrollbar *scrollPtr = (TkScrollbar *) clientData;
604 if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
605 TkScrollbarEventuallyRedraw(scrollPtr);
606 } else if (eventPtr->type == DestroyNotify) {
607 TkpDestroyScrollbar(scrollPtr);
608 if (scrollPtr->tkwin != NULL) {
609 scrollPtr->tkwin = NULL;
610 Tcl_DeleteCommandFromToken(scrollPtr->interp,
611 scrollPtr->widgetCmd);
613 if (scrollPtr->flags & REDRAW_PENDING) {
614 Tcl_CancelIdleCall(TkpDisplayScrollbar, (ClientData) scrollPtr);
617 * Free up all the stuff that requires special handling, then
618 * let Tk_FreeOptions handle all the standard option-related
622 Tk_FreeOptions(tkpScrollbarConfigSpecs, (char *) scrollPtr,
623 scrollPtr->display, 0);
624 Tcl_EventuallyFree((ClientData) scrollPtr, TCL_DYNAMIC);
625 } else if (eventPtr->type == ConfigureNotify) {
626 TkpComputeScrollbarGeometry(scrollPtr);
627 TkScrollbarEventuallyRedraw(scrollPtr);
628 } else if (eventPtr->type == FocusIn) {
629 if (eventPtr->xfocus.detail != NotifyInferior) {
630 scrollPtr->flags |= GOT_FOCUS;
631 if (scrollPtr->highlightWidth > 0) {
632 TkScrollbarEventuallyRedraw(scrollPtr);
635 } else if (eventPtr->type == FocusOut) {
636 if (eventPtr->xfocus.detail != NotifyInferior) {
637 scrollPtr->flags &= ~GOT_FOCUS;
638 if (scrollPtr->highlightWidth > 0) {
639 TkScrollbarEventuallyRedraw(scrollPtr);
646 *----------------------------------------------------------------------
648 * ScrollbarCmdDeletedProc --
650 * This procedure is invoked when a widget command is deleted. If
651 * the widget isn't already in the process of being destroyed,
652 * this command destroys it.
658 * The widget is destroyed.
660 *----------------------------------------------------------------------
664 ScrollbarCmdDeletedProc(clientData)
665 ClientData clientData; /* Pointer to widget record for widget. */
667 TkScrollbar *scrollPtr = (TkScrollbar *) clientData;
668 Tk_Window tkwin = scrollPtr->tkwin;
671 * This procedure could be invoked either because the window was
672 * destroyed and the command was then deleted (in which case tkwin
673 * is NULL) or because the command was deleted, and then this procedure
674 * destroys the widget.
678 scrollPtr->tkwin = NULL;
679 Tk_DestroyWindow(tkwin);
684 *--------------------------------------------------------------
686 * TkScrollbarEventuallyRedraw --
688 * Arrange for one or more of the fields of a scrollbar
697 *--------------------------------------------------------------
701 TkScrollbarEventuallyRedraw(scrollPtr)
702 register TkScrollbar *scrollPtr; /* Information about widget. */
704 if ((scrollPtr->tkwin == NULL) || (!Tk_IsMapped(scrollPtr->tkwin))) {
707 if ((scrollPtr->flags & REDRAW_PENDING) == 0) {
708 Tcl_DoWhenIdle(TkpDisplayScrollbar, (ClientData) scrollPtr);
709 scrollPtr->flags |= REDRAW_PENDING;