1 
2 /*
3  * tkScrollbar.c --
4  *
5  *	This module implements a scrollbar widgets for the Tk
6  *	toolkit.  A scrollbar displays a slider and two arrows;
7  *	mouse clicks on features within the scrollbar cause
8  *	scrolling commands to be invoked.
9  *
10  * Copyright (c) 1990-1994 The Regents of the University of California.
11  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
12  *
13  * See the file "license.terms" for information on usage and redistribution
14  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15  *
16  * SCCS: @(#) tkScrollbar.c 1.79 96/02/15 18:52:40
17  */
18 
19 #include "bltInt.h"
20 
21 #ifndef NO_TILESCROLLBAR
22 
23 #include "bltTile.h"
24 
25 extern Tk_CustomOption bltTileOption;
26 
27 #define NORMAL_BG	"#d9d9d9"
28 #define ACTIVE_BG	"#ececec"
29 #define SELECT_BG	"#c3c3c3"
30 #define TROUGH		"#c3c3c3"
31 #define INDICATOR	"#b03060"
32 #define DISABLED	"#a3a3a3"
33 
34 /*
35  * Defaults for scrollbars:
36  */
37 #define DEF_SCROLLBAR_ACTIVE_BACKGROUND	ACTIVE_BG
38 #define DEF_SCROLLBAR_ACTIVE_BG_MONO	RGB_BLACK
39 #define DEF_SCROLLBAR_ACTIVE_RELIEF	"raised"
40 #define DEF_SCROLLBAR_BACKGROUND		NORMAL_BG
41 #define DEF_SCROLLBAR_BG_MONO		RGB_WHITE
42 #define DEF_SCROLLBAR_BORDERWIDTH	"2"
43 #define DEF_SCROLLBAR_COMMAND		""
44 #define DEF_SCROLLBAR_CURSOR		""
45 #define DEF_SCROLLBAR_EL_BORDERWIDTH	"-1"
46 #define DEF_SCROLLBAR_HIGHLIGHT_BG	NORMAL_BG
47 #define DEF_SCROLLBAR_HIGHLIGHT		RGB_BLACK
48 #define DEF_SCROLLBAR_HIGHLIGHT_WIDTH	"2"
49 #define DEF_SCROLLBAR_JUMP		"0"
50 #define DEF_SCROLLBAR_ORIENT		"vertical"
51 #define DEF_SCROLLBAR_RELIEF		"sunken"
52 #define DEF_SCROLLBAR_REPEAT_DELAY	"300"
53 #define DEF_SCROLLBAR_REPEAT_INTERVAL	"100"
54 #define DEF_SCROLLBAR_TAKE_FOCUS	(char *)NULL
55 #define DEF_SCROLLBAR_TROUGH_COLOR	TROUGH
56 #define DEF_SCROLLBAR_TROUGH_MONO	RGB_WHITE
57 #define DEF_SCROLLBAR_WIDTH		"15"
58 
59 
60 /*
61  * A data structure of the following type is kept for each scrollbar
62  * widget managed by this file:
63  */
64 
65 typedef struct {
66     Tk_Window tkwin;		/* Window that embodies the scrollbar.  NULL
67 				 * means that the window has been destroyed
68 				 * but the data structures haven't yet been
69 				 * cleaned up.*/
70     Display *display;		/* Display containing widget.  Used, among
71 				 * other things, so that resources can be
72 				 * freed even after tkwin has gone away. */
73     Tcl_Interp *interp;		/* Interpreter associated with scrollbar. */
74     Tcl_Command widgetCmd;	/* Token for scrollbar's widget command. */
75     Tk_Uid orientUid;		/* Orientation for window ("vertical" or
76 				 * "horizontal"). */
77     int vertical;		/* Non-zero means vertical orientation
78 				 * requested, zero means horizontal. */
79     int width;			/* Desired narrow dimension of scrollbar,
80 				 * in pixels. */
81     char *command;		/* Command prefix to use when invoking
82 				 * scrolling commands.  NULL means don't
83 				 * invoke commands.  Malloc'ed. */
84     int commandSize;		/* Number of non-NULL bytes in command. */
85     int repeatDelay;		/* How long to wait before auto-repeating
86 				 * on scrolling actions (in ms). */
87     int repeatInterval;		/* Interval between autorepeats (in ms). */
88     int jump;			/* Value of -jump option. */
89 
90     /*
91      * Information used when displaying widget:
92      */
93 
94     int borderWidth;		/* Width of 3-D borders. */
95     Tk_3DBorder bgBorder;	/* Used for drawing background (all flat
96 				 * surfaces except for trough). */
97     Tk_3DBorder activeBorder;	/* For drawing backgrounds when active (i.e.
98 				 * when mouse is positioned over element). */
99     XColor *troughColorPtr;	/* Color for drawing trough. */
100     GC troughGC;		/* For drawing trough. */
101     GC copyGC;			/* Used for copying from pixmap onto screen. */
102     int relief;			/* Indicates whether window as a whole is
103 				 * raised, sunken, or flat. */
104     int highlightWidth;		/* Width in pixels of highlight to draw
105 				 * around widget when it has the focus.
106 				 * <= 0 means don't draw a highlight. */
107     XColor *highlightBgColorPtr;
108     /* Color for drawing traversal highlight
109 				 * area when highlight is off. */
110     XColor *highlightColorPtr;	/* Color for drawing traversal highlight. */
111     int inset;			/* Total width of all borders, including
112 				 * traversal highlight and 3-D border.
113 				 * Indicates how much interior stuff must
114 				 * be offset from outside edges to leave
115 				 * room for borders. */
116     int elementBorderWidth;	/* Width of border to draw around elements
117 				 * inside scrollbar (arrows and slider).
118 				 * -1 means use borderWidth. */
119     int arrowLength;		/* Length of arrows along long dimension of
120 				 * scrollbar, including space for a small gap
121 				 * between the arrow and the slider.
122 				 * Recomputed on window size changes. */
123     int sliderFirst;		/* Pixel coordinate of top or left edge
124 				 * of slider area, including border. */
125     int sliderLast;		/* Coordinate of pixel just after bottom
126 				 * or right edge of slider area, including
127 				 * border. */
128     int activeField;		/* Names field to be displayed in active
129 				 * colors, such as TOP_ARROW, or 0 for
130 				 * no field. */
131     int activeRelief;		/* Value of -activeRelief option: relief
132 				 * to use for active element. */
133 
134     /*
135      * Information describing the application related to the scrollbar.
136      * This information is provided by the application by invoking the
137      * "set" widget command.  This information can now be provided in
138      * two ways:  the "old" form (totalUnits, windowUnits, firstUnit,
139      * and lastUnit), or the "new" form (firstFraction and lastFraction).
140      * FirstFraction and lastFraction will always be valid, but
141      * the old-style information is only valid if the NEW_STYLE_COMMANDS
142      * flag is 0.
143      */
144 
145     int totalUnits;		/* Total dimension of application, in
146 				 * units.  Valid only if the NEW_STYLE_COMMANDS
147 				 * flag isn't set. */
148     int windowUnits;		/* Maximum number of units that can be
149 				 * displayed in the window at once.  Valid
150 				 * only if the NEW_STYLE_COMMANDS flag isn't
151 				 * set. */
152     int firstUnit;		/* Number of last unit visible in
153 				 * application's window.  Valid only if the
154 				 * NEW_STYLE_COMMANDS flag isn't set. */
155     int lastUnit;		/* Index of last unit visible in window.
156 				 * Valid only if the NEW_STYLE_COMMANDS
157 				 * flag isn't set. */
158     double firstFraction;	/* Position of first visible thing in window,
159 				 * specified as a fraction between 0 and
160 				 * 1.0. */
161     double lastFraction;	/* Position of last visible thing in window,
162 				 * specified as a fraction between 0 and
163 				 * 1.0. */
164 
165     /*
166      * Miscellaneous information:
167      */
168 
169     Tk_Cursor cursor;		/* Current cursor for window, or None. */
170     char *takeFocus;		/* Value of -takefocus option;  not used in
171 				 * the C code, but used by keyboard traversal
172 				 * scripts.  Malloc'ed, but may be NULL. */
173     int flags;			/* Various flags;  see below for
174 				 * definitions. */
175     Blt_Tile tile, tile2, activeTile;
176     char *imagePtr;
177     Tk_Image image;
178 } Scrollbar;
179 
180 /*
181  * Legal values for "activeField" field of Scrollbar structures.  These
182  * are also the return values from the ScrollbarPosition procedure.
183  */
184 
185 #define OUTSIDE		0
186 #define TOP_ARROW	1
187 #define TOP_GAP		2
188 #define SLIDER		3
189 #define BOTTOM_GAP	4
190 #define BOTTOM_ARROW	5
191 
192 /*
193  * Flag bits for scrollbars:
194  *
195  * REDRAW_PENDING:		Non-zero means a DoWhenIdle handler
196  *				has already been queued to redraw
197  *				this window.
198  * NEW_STYLE_COMMANDS:		Non-zero means the new style of commands
199  *				should be used to communicate with the
200  *				widget:  ".t yview scroll 2 lines", instead
201  *				of ".t yview 40", for example.
202  * GOT_FOCUS:			Non-zero means this window has the input
203  *				focus.
204  */
205 
206 #define REDRAW_PENDING		1
207 #define NEW_STYLE_COMMANDS	2
208 #define GOT_FOCUS		4
209 #define SCROLLBAR_LAYOUT 8
210 
211 /*
212  * Minimum slider length, in pixels (designed to make sure that the slider
213  * is always easy to grab with the mouse).
214  */
215 
216 #define MIN_SLIDER_LENGTH	8
217 
218 /*
219  * Information used for argv parsing.
220  */
221 
222 static Tk_ConfigSpec configSpecs[] =
223 {
224     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
225 	DEF_SCROLLBAR_ACTIVE_BACKGROUND, Tk_Offset(Scrollbar, activeBorder),
226 	TK_CONFIG_COLOR_ONLY},
227     {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
228 	DEF_SCROLLBAR_ACTIVE_BG_MONO, Tk_Offset(Scrollbar, activeBorder),
229 	TK_CONFIG_MONO_ONLY},
230     {TK_CONFIG_RELIEF, "-activerelief", "activeRelief", "Relief",
231 	DEF_SCROLLBAR_ACTIVE_RELIEF, Tk_Offset(Scrollbar, activeRelief), 0},
232     {TK_CONFIG_CUSTOM, "-activetile", "activeTile", "Tile",
233 	(char *)NULL, Tk_Offset(Scrollbar, activeTile), TK_CONFIG_NULL_OK,
234 	&bltTileOption},
235     {TK_CONFIG_BORDER, "-background", "background", "Background",
236 	DEF_SCROLLBAR_BACKGROUND, Tk_Offset(Scrollbar, bgBorder),
237 	TK_CONFIG_COLOR_ONLY},
238     {TK_CONFIG_BORDER, "-background", "background", "Background",
239 	DEF_SCROLLBAR_BG_MONO, Tk_Offset(Scrollbar, bgBorder),
240 	TK_CONFIG_MONO_ONLY},
241     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *)NULL,
242 	(char *)NULL, 0, 0},
243     {TK_CONFIG_SYNONYM, "-bg", "background", (char *)NULL,
244 	(char *)NULL, 0, 0},
245     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
246 	DEF_SCROLLBAR_BORDERWIDTH, Tk_Offset(Scrollbar, borderWidth), 0},
247     {TK_CONFIG_STRING, "-command", "command", "Command",
248 	DEF_SCROLLBAR_COMMAND, Tk_Offset(Scrollbar, command),
249 	TK_CONFIG_NULL_OK},
250     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
251 	DEF_SCROLLBAR_CURSOR, Tk_Offset(Scrollbar, cursor), TK_CONFIG_NULL_OK},
252     {TK_CONFIG_PIXELS, "-elementborderwidth", "elementBorderWidth",
253 	"BorderWidth", DEF_SCROLLBAR_EL_BORDERWIDTH,
254 	Tk_Offset(Scrollbar, elementBorderWidth), 0},
255     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
256 	"HighlightBackground", DEF_SCROLLBAR_HIGHLIGHT_BG,
257 	Tk_Offset(Scrollbar, highlightBgColorPtr), 0},
258     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
259 	DEF_SCROLLBAR_HIGHLIGHT,
260 	Tk_Offset(Scrollbar, highlightColorPtr), 0},
261     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
262 	"HighlightThickness",
263 	DEF_SCROLLBAR_HIGHLIGHT_WIDTH, Tk_Offset(Scrollbar, highlightWidth), 0},
264     {TK_CONFIG_STRING, "-image", "image", "Image",
265 	NULL, Tk_Offset(Scrollbar, imagePtr), 0},
266     {TK_CONFIG_BOOLEAN, "-jump", "jump", "Jump",
267 	DEF_SCROLLBAR_JUMP, Tk_Offset(Scrollbar, jump), 0},
268     {TK_CONFIG_UID, "-orient", "orient", "Orient",
269 	DEF_SCROLLBAR_ORIENT, Tk_Offset(Scrollbar, orientUid), 0},
270     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
271 	DEF_SCROLLBAR_RELIEF, Tk_Offset(Scrollbar, relief), 0},
272     {TK_CONFIG_INT, "-repeatdelay", "repeatDelay", "RepeatDelay",
273 	DEF_SCROLLBAR_REPEAT_DELAY, Tk_Offset(Scrollbar, repeatDelay), 0},
274     {TK_CONFIG_INT, "-repeatinterval", "repeatInterval", "RepeatInterval",
275 	DEF_SCROLLBAR_REPEAT_INTERVAL, Tk_Offset(Scrollbar, repeatInterval), 0},
276     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
277 	DEF_SCROLLBAR_TAKE_FOCUS, Tk_Offset(Scrollbar, takeFocus),
278 	TK_CONFIG_NULL_OK},
279     {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile",
280 	(char *)NULL, Tk_Offset(Scrollbar, tile), TK_CONFIG_NULL_OK,
281 	&bltTileOption},
282     {TK_CONFIG_CUSTOM, "-tile2", "tile2", "Tile2",
283 	(char *)NULL, Tk_Offset(Scrollbar, tile2), TK_CONFIG_NULL_OK,
284 	&bltTileOption},
285     {TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
286 	DEF_SCROLLBAR_TROUGH_COLOR, Tk_Offset(Scrollbar, troughColorPtr),
287 	TK_CONFIG_COLOR_ONLY},
288     {TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
289 	DEF_SCROLLBAR_TROUGH_MONO, Tk_Offset(Scrollbar, troughColorPtr),
290 	TK_CONFIG_MONO_ONLY},
291     {TK_CONFIG_PIXELS, "-width", "width", "Width",
292 	DEF_SCROLLBAR_WIDTH, Tk_Offset(Scrollbar, width), 0},
293     {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL,
294 	(char *)NULL, 0, 0}
295 };
296 
297 /*
298  * Forward declarations for procedures defined later in this file:
299  */
300 
301 static void ComputeScrollbarGeometry _ANSI_ARGS_((
302 	Scrollbar *scrollPtr));
303 static int ConfigureScrollbar _ANSI_ARGS_((Tcl_Interp *interp,
304 	Scrollbar *scrollPtr, int argc, char **argv,
305 	int flags));
306 static void DestroyScrollbar _ANSI_ARGS_((DestroyData *memPtr));
307 static void DisplayScrollbar _ANSI_ARGS_((ClientData clientData));
308 static void EventuallyRedraw _ANSI_ARGS_((Scrollbar *scrollPtr));
309 static void ScrollbarCmdDeletedProc _ANSI_ARGS_((
310 	ClientData clientData));
311 static void ScrollbarEventProc _ANSI_ARGS_((ClientData clientData,
312 	XEvent *eventPtr));
313 static int ScrollbarPosition _ANSI_ARGS_((Scrollbar *scrollPtr,
314 	int x, int y));
315 static int ScrollbarWidgetCmd _ANSI_ARGS_((ClientData clientData,
316 	Tcl_Interp *, int argc, char **argv));
317 
318 static void widgetWorldChanged(ClientData clientData);
319 
320 static Tk_ClassProcs scrollClass = {
321     sizeof(Tk_ClassProcs),	/* size */
322     widgetWorldChanged,		/* worldChangedProc */
323 };
324 
325 
326 static void
widgetWorldChanged(ClientData clientData)327 widgetWorldChanged(ClientData clientData)
328 {
329     Scrollbar *scrollPtr = (Scrollbar *)clientData;
330     scrollPtr->flags |= (SCROLLBAR_LAYOUT);
331 
332     EventuallyRedraw(scrollPtr);
333 }
334 
335 
336 /*
337  *--------------------------------------------------------------
338  *
339  * ScrollbarCmd --
340  *
341  *	This procedure is invoked to process the "scrollbar" Tcl
342  *	command.  See the user documentation for details on what
343  *	it does.
344  *
345  * Results:
346  *	A standard Tcl result.
347  *
348  * Side effects:
349  *	See the user documentation.
350  *
351  *--------------------------------------------------------------
352  */
353 
354 /*ARGSUSED*/
355 static int
scrollbarCmd(clientData,interp,argc,argv,stype)356 scrollbarCmd(clientData, interp, argc, argv, stype)
357     ClientData clientData;	/* Main window associated with
358 				 * interpreter. */
359     Tcl_Interp *interp;		/* Current interpreter. */
360     int argc;			/* Number of arguments. */
361     char **argv;		/* Argument strings. */
362     int stype;
363 {
364     register Scrollbar *scrollPtr;
365     Tk_Window tkwin;
366 
367     if (argc < 2) {
368 	Tcl_AppendResult(interp, "wrong # args: should be \"",
369 	    argv[0], " pathName ?options?\"", (char *)NULL);
370 	return TCL_ERROR;
371     }
372     tkwin = Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), argv[1],
373 	(char *)NULL);
374     if (tkwin == NULL) {
375 	return TCL_ERROR;
376     }
377     /*
378      * Initialize fields that won't be initialized by ConfigureScrollbar,
379      * or which ConfigureScrollbar expects to have reasonable values
380      * (e.g. resource pointers).
381      */
382 
383     scrollPtr = Blt_Calloc(1, sizeof(Scrollbar));
384     scrollPtr->tkwin = tkwin;
385     scrollPtr->display = Tk_Display(tkwin);
386     scrollPtr->interp = interp;
387     scrollPtr->widgetCmd = Tcl_CreateCommand(interp,
388 	Tk_PathName(scrollPtr->tkwin), ScrollbarWidgetCmd,
389 	(ClientData)scrollPtr, ScrollbarCmdDeletedProc);
390 #ifdef ITCL_NAMESPACES
391     Itk_SetWidgetCommand(scrollPtr->tkwin, scrollPtr->widgetCmd);
392 #endif /* ITCL_NAMESPACES */
393     scrollPtr->orientUid = NULL;
394     scrollPtr->vertical = 0;
395     scrollPtr->width = 0;
396     scrollPtr->command = NULL;
397     scrollPtr->commandSize = 0;
398     scrollPtr->repeatDelay = 0;
399     scrollPtr->repeatInterval = 0;
400     scrollPtr->borderWidth = 0;
401     scrollPtr->bgBorder = NULL;
402     scrollPtr->activeBorder = NULL;
403     scrollPtr->troughColorPtr = NULL;
404     scrollPtr->troughGC = None;
405     scrollPtr->copyGC = None;
406     scrollPtr->relief = TK_RELIEF_FLAT;
407     scrollPtr->highlightWidth = 0;
408     scrollPtr->highlightBgColorPtr = NULL;
409     scrollPtr->highlightColorPtr = NULL;
410     scrollPtr->inset = 0;
411     scrollPtr->elementBorderWidth = -1;
412     scrollPtr->arrowLength = 0;
413     scrollPtr->sliderFirst = 0;
414     scrollPtr->sliderLast = 0;
415     scrollPtr->activeField = 0;
416     scrollPtr->activeRelief = TK_RELIEF_RAISED;
417     scrollPtr->totalUnits = 0;
418     scrollPtr->windowUnits = 0;
419     scrollPtr->firstUnit = 0;
420     scrollPtr->lastUnit = 0;
421     scrollPtr->firstFraction = 0.0;
422     scrollPtr->lastFraction = 0.0;
423     scrollPtr->cursor = None;
424     scrollPtr->takeFocus = NULL;
425     scrollPtr->flags = 0;
426     scrollPtr->tile = scrollPtr->tile2 = scrollPtr->activeTile = NULL;
427 
428     if (stype) {
429         Tk_SetClass(scrollPtr->tkwin, "BScrollbar");
430     } else {
431         Tk_SetClass(scrollPtr->tkwin, "Scrollbar");
432     }
433     Tk_SetClassProcs(tkwin, &scrollClass, (ClientData)scrollPtr);
434     Tk_CreateEventHandler(scrollPtr->tkwin,
435 	ExposureMask | StructureNotifyMask | FocusChangeMask,
436 	ScrollbarEventProc, (ClientData)scrollPtr);
437     if (ConfigureScrollbar(interp, scrollPtr, argc - 2, argv + 2, 0) != TCL_OK) {
438 	goto error;
439     }
440     Tcl_SetResult(interp, Tk_PathName(scrollPtr->tkwin), TCL_VOLATILE);
441     return TCL_OK;
442 
443   error:
444     Tk_DestroyWindow(scrollPtr->tkwin);
445     return TCL_ERROR;
446 }
447 
448 static int
BScrollbarCmd(clientData,interp,argc,argv)449 BScrollbarCmd(clientData, interp, argc, argv)
450     ClientData clientData;	/* Main window associated with
451 				 * interpreter. */
452     Tcl_Interp *interp;		/* Current interpreter. */
453     int argc;			/* Number of arguments. */
454     char **argv;		/* Argument strings. */
455 {
456     return scrollbarCmd(clientData, interp, argc, argv, 1);
457 }
458 
459 static int
ScrollbarCmd(clientData,interp,argc,argv)460 ScrollbarCmd(clientData, interp, argc, argv)
461     ClientData clientData;	/* Main window associated with
462 				 * interpreter. */
463     Tcl_Interp *interp;		/* Current interpreter. */
464     int argc;			/* Number of arguments. */
465     char **argv;		/* Argument strings. */
466 {
467     return scrollbarCmd(clientData, interp, argc, argv, 0);
468 }
469 
470 /*
471  *--------------------------------------------------------------
472  *
473  * ScrollbarWidgetCmd --
474  *
475  *	This procedure is invoked to process the Tcl command
476  *	that corresponds to a widget managed by this module.
477  *	See the user documentation for details on what it does.
478  *
479  * Results:
480  *	A standard Tcl result.
481  *
482  * Side effects:
483  *	See the user documentation.
484  *
485  *--------------------------------------------------------------
486  */
487 
488 static int
ScrollbarWidgetCmd(clientData,interp,argc,argv)489 ScrollbarWidgetCmd(clientData, interp, argc, argv)
490     ClientData clientData;	/* Information about scrollbar
491 					 * widget. */
492     Tcl_Interp *interp;		/* Current interpreter. */
493     int argc;			/* Number of arguments. */
494     char **argv;		/* Argument strings. */
495 {
496     register Scrollbar *scrollPtr = clientData;
497     char string[200];
498     int result = TCL_OK;
499     size_t length;
500     int c;
501 
502     if (argc < 2) {
503 	Tcl_AppendResult(interp, "wrong # args: should be \"",
504 	    argv[0], " option ?arg arg ...?\"", (char *)NULL);
505 	return TCL_ERROR;
506     }
507     Tcl_Preserve((ClientData)scrollPtr);
508     c = argv[1][0];
509     length = strlen(argv[1]);
510     if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) {
511 	if (argc == 2) {
512 	    switch (scrollPtr->activeField) {
513 	    case TOP_ARROW:
514 		Tcl_SetResult(interp, "arrow1", TCL_STATIC);
515 		break;
516 	    case SLIDER:
517 		Tcl_SetResult(interp, "slider", TCL_STATIC);
518 		break;
519 	    case BOTTOM_ARROW:
520 		Tcl_SetResult(interp, "arrow2", TCL_STATIC);
521 		break;
522 	    }
523 	    goto done;
524 	}
525 	if (argc != 3) {
526 	    Tcl_AppendResult(interp, "wrong # args: should be \"",
527 		argv[0], " activate element\"", (char *)NULL);
528 	    goto error;
529 	}
530 	c = argv[2][0];
531 	length = strlen(argv[2]);
532 	if ((c == 'a') && (strcmp(argv[2], "arrow1") == 0)) {
533 	    scrollPtr->activeField = TOP_ARROW;
534 	} else if ((c == 'a') && (strcmp(argv[2], "arrow2") == 0)) {
535 	    scrollPtr->activeField = BOTTOM_ARROW;
536 	} else if ((c == 's') && (strncmp(argv[2], "slider", length) == 0)) {
537 	    scrollPtr->activeField = SLIDER;
538 	} else {
539 	    scrollPtr->activeField = OUTSIDE;
540 	}
541 	EventuallyRedraw(scrollPtr);
542     } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
543 	&& (length >= 2)) {
544 	if (argc != 3) {
545 	    Tcl_AppendResult(interp, "wrong # args: should be \"",
546 		argv[0], " cget option\"",
547 		(char *)NULL);
548 	    goto error;
549 	}
550 	result = Tk_ConfigureValue(interp, scrollPtr->tkwin, configSpecs,
551 	    (char *)scrollPtr, argv[2], 0);
552     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
553 	&& (length >= 2)) {
554 	if (argc == 2) {
555 	    result = Tk_ConfigureInfo(interp, scrollPtr->tkwin, configSpecs,
556 		(char *)scrollPtr, (char *)NULL, 0);
557 	} else if (argc == 3) {
558 	    result = Tk_ConfigureInfo(interp, scrollPtr->tkwin, configSpecs,
559 		(char *)scrollPtr, argv[2], 0);
560 	} else {
561 	    result = ConfigureScrollbar(interp, scrollPtr, argc - 2, argv + 2,
562 		TK_CONFIG_ARGV_ONLY);
563 	}
564     } else if ((c == 'd') && (strncmp(argv[1], "delta", length) == 0)) {
565 	int xDelta, yDelta, pixels, barWidth;
566 	double fraction;
567 
568 	if (argc != 4) {
569 	    Tcl_AppendResult(interp, "wrong # args: should be \"",
570 		argv[0], " delta xDelta yDelta\"", (char *)NULL);
571 	    goto error;
572 	}
573 	if ((Tcl_GetInt(interp, argv[2], &xDelta) != TCL_OK)
574 	    || (Tcl_GetInt(interp, argv[3], &yDelta) != TCL_OK)) {
575 	    goto error;
576 	}
577 	if (scrollPtr->vertical) {
578 	    pixels = yDelta;
579 	    barWidth = Tk_Height(scrollPtr->tkwin) - 1
580 		- 2 * (scrollPtr->arrowLength + scrollPtr->inset);
581 	} else {
582 	    pixels = xDelta;
583 	    barWidth = Tk_Width(scrollPtr->tkwin) - 1
584 		- 2 * (scrollPtr->arrowLength + scrollPtr->inset);
585 	}
586 	if (barWidth == 0) {
587 	    fraction = 0.0;
588 	} else {
589 	    fraction = ((double)pixels / (double)barWidth);
590 	}
591 	Tcl_SetObjResult(interp, Tcl_NewDoubleObj(fraction));
592     } else if ((c == 'f') && (strncmp(argv[1], "fraction", length) == 0)) {
593 	int x, y, pos, barWidth;
594 	double fraction;
595 
596 	if (argc != 4) {
597 	    Tcl_AppendResult(interp, "wrong # args: should be \"",
598 		argv[0], " fraction x y\"", (char *)NULL);
599 	    goto error;
600 	}
601 	if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
602 	    || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
603 	    goto error;
604 	}
605 	if (scrollPtr->vertical) {
606 	    pos = y - (scrollPtr->arrowLength + scrollPtr->inset);
607 	    barWidth = Tk_Height(scrollPtr->tkwin) - 1
608 		- 2 * (scrollPtr->arrowLength + scrollPtr->inset);
609 	} else {
610 	    pos = x - (scrollPtr->arrowLength + scrollPtr->inset);
611 	    barWidth = Tk_Width(scrollPtr->tkwin) - 1
612 		- 2 * (scrollPtr->arrowLength + scrollPtr->inset);
613 	}
614 	if (barWidth == 0) {
615 	    fraction = 0.0;
616 	} else {
617 	    fraction = ((double)pos / (double)barWidth);
618 	}
619 	if (fraction < 0) {
620 	    fraction = 0;
621 	} else if (fraction > 1.0) {
622 	    fraction = 1.0;
623 	}
624 	sprintf(string, "%g", fraction);
625 	Tcl_SetResult(interp, string, TCL_VOLATILE);
626     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
627 	if (argc != 2) {
628 	    Tcl_AppendResult(interp, "wrong # args: should be \"",
629 		argv[0], " get\"", (char *)NULL);
630 	    goto error;
631 	}
632 	if (scrollPtr->flags & NEW_STYLE_COMMANDS) {
633 	    char first[TCL_DOUBLE_SPACE], last[TCL_DOUBLE_SPACE];
634 
635 	    Tcl_PrintDouble(interp, scrollPtr->firstFraction, first);
636 	    Tcl_PrintDouble(interp, scrollPtr->lastFraction, last);
637 	    Tcl_AppendResult(interp, first, " ", last, (char *)NULL);
638 	} else {
639 	    sprintf(string, "%d %d %d %d", scrollPtr->totalUnits,
640 		scrollPtr->windowUnits, scrollPtr->firstUnit,
641 		scrollPtr->lastUnit);
642 	    Tcl_SetResult(interp, string, TCL_VOLATILE);
643 	}
644     } else if ((c == 'i') && (strncmp(argv[1], "identify", length) == 0)) {
645 	int x, y, thing;
646 
647 	if (argc != 4) {
648 	    Tcl_AppendResult(interp, "wrong # args: should be \"",
649 		argv[0], " identify x y\"", (char *)NULL);
650 	    goto error;
651 	}
652 	if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
653 	    || (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
654 	    goto error;
655 	}
656 	thing = ScrollbarPosition(scrollPtr, x, y);
657 	switch (thing) {
658 	case TOP_ARROW:
659 	    Tcl_SetResult(interp, "arrow1", TCL_STATIC);
660 	    break;
661 	case TOP_GAP:
662 	    Tcl_SetResult(interp, "trough1", TCL_STATIC);
663 	    break;
664 	case SLIDER:
665 	    Tcl_SetResult(interp, "slider", TCL_STATIC);
666 	    break;
667 	case BOTTOM_GAP:
668 	    Tcl_SetResult(interp, "trough2", TCL_STATIC);
669 	    break;
670 	case BOTTOM_ARROW:
671 	    Tcl_SetResult(interp, "arrow2", TCL_STATIC);
672 	    break;
673 	}
674     } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
675 	int totalUnits, windowUnits, firstUnit, lastUnit;
676 
677 	if (argc == 4) {
678 	    double first, last;
679 
680 	    if (Tcl_GetDouble(interp, argv[2], &first) != TCL_OK) {
681 		goto error;
682 	    }
683 	    if (Tcl_GetDouble(interp, argv[3], &last) != TCL_OK) {
684 		goto error;
685 	    }
686 	    if (first < 0) {
687 		scrollPtr->firstFraction = 0;
688 	    } else if (first > 1.0) {
689 		scrollPtr->firstFraction = 1.0;
690 	    } else {
691 		scrollPtr->firstFraction = first;
692 	    }
693 	    if (last < scrollPtr->firstFraction) {
694 		scrollPtr->lastFraction = scrollPtr->firstFraction;
695 	    } else if (last > 1.0) {
696 		scrollPtr->lastFraction = 1.0;
697 	    } else {
698 		scrollPtr->lastFraction = last;
699 	    }
700 	    scrollPtr->flags |= NEW_STYLE_COMMANDS;
701 	} else if (argc == 6) {
702 	    if (Tcl_GetInt(interp, argv[2], &totalUnits) != TCL_OK) {
703 		goto error;
704 	    }
705 	    if (totalUnits < 0) {
706 		totalUnits = 0;
707 	    }
708 	    if (Tcl_GetInt(interp, argv[3], &windowUnits) != TCL_OK) {
709 		goto error;
710 	    }
711 	    if (windowUnits < 0) {
712 		windowUnits = 0;
713 	    }
714 	    if (Tcl_GetInt(interp, argv[4], &firstUnit) != TCL_OK) {
715 		goto error;
716 	    }
717 	    if (Tcl_GetInt(interp, argv[5], &lastUnit) != TCL_OK) {
718 		goto error;
719 	    }
720 	    if (totalUnits > 0) {
721 		if (lastUnit < firstUnit) {
722 		    lastUnit = firstUnit;
723 		}
724 	    } else {
725 		firstUnit = lastUnit = 0;
726 	    }
727 	    scrollPtr->totalUnits = totalUnits;
728 	    scrollPtr->windowUnits = windowUnits;
729 	    scrollPtr->firstUnit = firstUnit;
730 	    scrollPtr->lastUnit = lastUnit;
731 	    if (scrollPtr->totalUnits == 0) {
732 		scrollPtr->firstFraction = 0.0;
733 		scrollPtr->lastFraction = 1.0;
734 	    } else {
735 		scrollPtr->firstFraction = ((double)firstUnit) / totalUnits;
736 		scrollPtr->lastFraction = ((double)(lastUnit + 1)) / totalUnits;
737 	    }
738 	    scrollPtr->flags &= ~NEW_STYLE_COMMANDS;
739 	} else {
740 	    Tcl_AppendResult(interp, "wrong # args: should be \"",
741 		argv[0], " set firstFraction lastFraction\" or \"",
742 		argv[0],
743 		" set totalUnits windowUnits firstUnit lastUnit\"",
744 		(char *)NULL);
745 	    goto error;
746 	}
747 	ComputeScrollbarGeometry(scrollPtr);
748 	EventuallyRedraw(scrollPtr);
749     } else {
750 	Tcl_AppendResult(interp, "bad option \"", argv[1],
751 	    "\": must be activate, cget, configure, delta, fraction, ",
752 	    "get, identify, or set", (char *)NULL);
753 	goto error;
754     }
755   done:
756     Tcl_Release((ClientData)scrollPtr);
757     return result;
758 
759   error:
760     Tcl_Release((ClientData)scrollPtr);
761     return TCL_ERROR;
762 }
763 
764 /*
765  *----------------------------------------------------------------------
766  *
767  * DestroyScrollbar --
768  *
769  *	This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
770  *	to clean up the internal structure of a scrollbar at a safe time
771  *	(when no-one is using it anymore).
772  *
773  * Results:
774  *	None.
775  *
776  * Side effects:
777  *	Everything associated with the scrollbar is freed up.
778  *
779  *----------------------------------------------------------------------
780  */
781 
782 static void
DestroyScrollbar(memPtr)783 DestroyScrollbar(memPtr)
784     DestroyData *memPtr;	/* Info about scrollbar widget. */
785 {
786     register Scrollbar *scrollPtr = (Scrollbar *)memPtr;
787 
788     /*
789      * Free up all the stuff that requires special handling, then
790      * let Tk_FreeOptions handle all the standard option-related
791      * stuff.
792      */
793 
794     if (scrollPtr->troughGC != None) {
795 	Tk_FreeGC(scrollPtr->display, scrollPtr->troughGC);
796     }
797     if (scrollPtr->copyGC != None) {
798 	Tk_FreeGC(scrollPtr->display, scrollPtr->copyGC);
799     }
800     if (scrollPtr->activeTile != NULL) {
801 	Blt_FreeTile(scrollPtr->activeTile);
802     }
803     if (scrollPtr->tile != NULL) {
804 	Blt_FreeTile(scrollPtr->tile);
805     }
806     if (scrollPtr->tile2 != NULL) {
807         Blt_FreeTile(scrollPtr->tile2);
808     }
809     if (scrollPtr->image != NULL) {
810         Tk_FreeImage(scrollPtr->image);
811     }
812     Tk_FreeOptions(configSpecs, (char *)scrollPtr, scrollPtr->display, 0);
813     Blt_Free(scrollPtr);
814 }
815 
816 /*
817  *----------------------------------------------------------------------
818  *
819  * TileChangedProc
820  *
821  *	Routine for tile change notifications.
822  *
823  * Results:
824  *	None.
825  *
826  *----------------------------------------------------------------------
827  */
828 /*ARGSUSED*/
829 static void
TileChangedProc(clientData,tile)830 TileChangedProc(clientData, tile)
831     ClientData clientData;
832     Blt_Tile tile;		/* Not used. */
833 {
834     Scrollbar *scrollPtr = clientData;
835 
836     if (scrollPtr->tkwin != NULL) {
837 	EventuallyRedraw(scrollPtr);
838     }
839 }
840 
841 /*
842  *----------------------------------------------------------------------
843  *
844  * ScrollbarImageProc --
845  *
846  *	This function is invoked by the image code whenever the manager for an
847  *	image does something that affects the size or contents of an image
848  *	displayed in a button.
849  *
850  * Results:
851  *	None.
852  *
853  * Side effects:
854  *	Arranges for the button to get redisplayed.
855  *
856  *----------------------------------------------------------------------
857  */
858 
859 static void
ScrollbarImageProc(ClientData clientData,int x,int y,int width,int height,int imgWidth,int imgHeight)860 ScrollbarImageProc(
861     ClientData clientData,	/* Pointer to widget record. */
862     int x, int y,		/* Upper left pixel (within image) that must
863 				 * be redisplayed. */
864     int width, int height,	/* Dimensions of area to redisplay (might be
865 				 * <= 0). */
866     int imgWidth, int imgHeight)/* New dimensions of image. */
867 {
868     register Scrollbar *scrollPtr = (Scrollbar *) clientData;
869 
870     if (scrollPtr->tkwin != NULL) {
871 	ComputeScrollbarGeometry(scrollPtr);
872 	if (Tk_IsMapped(scrollPtr->tkwin) && !(scrollPtr->flags & REDRAW_PENDING)) {
873 	    Tcl_DoWhenIdle(DisplayScrollbar, (ClientData) scrollPtr);
874 	    scrollPtr->flags |= REDRAW_PENDING;
875 	}
876     }
877 }
878 
879 /*
880  *----------------------------------------------------------------------
881  *
882  * ConfigureScrollbar --
883  *
884  *	This procedure is called to process an argv/argc list, plus
885  *	the Tk option database, in order to configure (or
886  *	reconfigure) a scrollbar widget.
887  *
888  * Results:
889  *	The return value is a standard Tcl result.  If TCL_ERROR is
890  *	returned, then interp->result contains an error message.
891  *
892  * Side effects:
893  *	Configuration information, such as colors, border width,
894  *	etc. get set for scrollPtr;  old resources get freed,
895  *	if there were any.
896  *
897  *----------------------------------------------------------------------
898  */
899 
900 static int
ConfigureScrollbar(interp,scrollPtr,argc,argv,flags)901 ConfigureScrollbar(interp, scrollPtr, argc, argv, flags)
902     Tcl_Interp *interp;		/* Used for error reporting. */
903     register Scrollbar *scrollPtr;	/* Information about widget;  may or
904 					 * may not already have values for
905 					 * some fields. */
906     int argc;			/* Number of valid entries in argv. */
907     char **argv;		/* Arguments. */
908     int flags;			/* Flags to pass to
909 					 * Tk_ConfigureWidget. */
910 {
911     size_t length;
912     XGCValues gcValues;
913     GC new;
914     Tk_Image image;
915 
916     if (Tk_ConfigureWidget(interp, scrollPtr->tkwin, configSpecs,
917 	    argc, argv, (char *)scrollPtr, flags) != TCL_OK) {
918 	return TCL_ERROR;
919     }
920     /*
921      * A few options need special processing, such as parsing the
922      * orientation or setting the background from a 3-D border.
923      */
924 
925     length = strlen(scrollPtr->orientUid);
926     if (strncmp(scrollPtr->orientUid, "vertical", length) == 0) {
927 	scrollPtr->vertical = 1;
928     } else if (strncmp(scrollPtr->orientUid, "horizontal", length) == 0) {
929 	scrollPtr->vertical = 0;
930     } else {
931 	Tcl_AppendResult(interp, "bad orientation \"", scrollPtr->orientUid,
932 	    "\": must be vertical or horizontal", (char *)NULL);
933 	return TCL_ERROR;
934     }
935 
936     if (scrollPtr->command != NULL) {
937 	scrollPtr->commandSize = strlen(scrollPtr->command);
938     } else {
939 	scrollPtr->commandSize = 0;
940     }
941 
942     /* Get the images for the widget, if there are any. Allocate the new
943     * images before freeing the old ones, so that the reference counts
944     * don't go to zero and cause image data to be discarded.
945     */
946 
947     if (scrollPtr->imagePtr != NULL) {
948         image = Tk_GetImage(scrollPtr->interp, scrollPtr->tkwin,
949             scrollPtr->imagePtr, ScrollbarImageProc,
950             (ClientData) scrollPtr);
951     } else {
952         image = NULL;
953     }
954     if (scrollPtr->image != NULL) {
955         Tk_FreeImage(scrollPtr->image);
956     }
957     scrollPtr->image = image;
958 
959     if (scrollPtr->activeTile != NULL) {
960 	Blt_SetTileChangedProc(scrollPtr->activeTile, TileChangedProc,
961 	    (ClientData)scrollPtr);
962     }
963     if (scrollPtr->tile != NULL) {
964 	Blt_SetTileChangedProc(scrollPtr->tile, TileChangedProc,
965 	    (ClientData)scrollPtr);
966     }
967     if (scrollPtr->tile2 != NULL) {
968         Blt_SetTileChangedProc(scrollPtr->tile2, TileChangedProc,
969             (ClientData)scrollPtr);
970     }
971     Tk_SetBackgroundFromBorder(scrollPtr->tkwin, scrollPtr->bgBorder);
972 
973     gcValues.foreground = scrollPtr->troughColorPtr->pixel;
974     new = Tk_GetGC(scrollPtr->tkwin, GCForeground, &gcValues);
975     if (scrollPtr->troughGC != None) {
976 	Tk_FreeGC(scrollPtr->display, scrollPtr->troughGC);
977     }
978     scrollPtr->troughGC = new;
979     if (scrollPtr->copyGC == None) {
980 	gcValues.graphics_exposures = False;
981 	scrollPtr->copyGC = Tk_GetGC(scrollPtr->tkwin, GCGraphicsExposures,
982 	    &gcValues);
983     }
984     /*
985      * Register the desired geometry for the window (leave enough space
986      * for the two arrows plus a minimum-size slider, plus border around
987      * the whole window, if any).  Then arrange for the window to be
988      * redisplayed.
989      */
990 
991     ComputeScrollbarGeometry(scrollPtr);
992     EventuallyRedraw(scrollPtr);
993     return TCL_OK;
994 }
995 
996 /*
997  *--------------------------------------------------------------
998  *
999  * DisplayScrollbar --
1000  *
1001  *	This procedure redraws the contents of a scrollbar window.
1002  *	It is invoked as a do-when-idle handler, so it only runs
1003  *	when there's nothing else for the application to do.
1004  *
1005  * Results:
1006  *	None.
1007  *
1008  * Side effects:
1009  *	Information appears on the screen.
1010  *
1011  *--------------------------------------------------------------
1012  */
1013 
1014 static void
DisplayScrollbar(clientData)1015 DisplayScrollbar(clientData)
1016     ClientData clientData;	/* Information about window. */
1017 {
1018     register Scrollbar *scrollPtr = clientData;
1019     register Tk_Window tkwin = scrollPtr->tkwin;
1020     XPoint points[7];
1021     Tk_3DBorder border;
1022     int relief, width, elementBorderWidth;
1023     Pixmap pixmap;
1024     Blt_Tile tile, tiletr, tilecur;
1025 
1026     tiletr = (Blt_HasTile(scrollPtr->tile) ? scrollPtr->tile : NULL);
1027     tile = (Blt_HasTile(scrollPtr->tile2) ? scrollPtr->tile2 : tiletr);
1028     if ((scrollPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
1029 	goto done;
1030     }
1031     if (scrollPtr->vertical) {
1032 	width = Tk_Width(tkwin) - 2 * scrollPtr->inset;
1033     } else {
1034 	width = Tk_Height(tkwin) - 2 * scrollPtr->inset;
1035     }
1036     elementBorderWidth = scrollPtr->elementBorderWidth;
1037     if (elementBorderWidth < 0) {
1038 	elementBorderWidth = scrollPtr->borderWidth;
1039     }
1040 
1041     /*
1042      * In order to avoid screen flashes, this procedure redraws
1043      * the scrollbar in a pixmap, then copies the pixmap to the
1044      * screen in a single operation.  This means that there's no
1045      * point in time where the on-sreen image has been cleared.
1046      */
1047 
1048     pixmap = Tk_GetPixmap(scrollPtr->display, Tk_WindowId(tkwin),
1049 	Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
1050 
1051     if (scrollPtr->highlightWidth != 0) {
1052 	GC gc;
1053 
1054 	if (scrollPtr->flags & GOT_FOCUS) {
1055 	    gc = Tk_GCForColor(scrollPtr->highlightColorPtr, pixmap);
1056 	} else {
1057 	    gc = Tk_GCForColor(scrollPtr->highlightBgColorPtr, pixmap);
1058 	}
1059 	Tk_DrawFocusHighlight(tkwin, gc, scrollPtr->highlightWidth, pixmap);
1060     }
1061     Blt_Draw3DRectangle(tkwin, pixmap, scrollPtr->bgBorder,
1062 	scrollPtr->highlightWidth, scrollPtr->highlightWidth,
1063 	Tk_Width(tkwin) - 2 * scrollPtr->highlightWidth,
1064 	Tk_Height(tkwin) - 2 * scrollPtr->highlightWidth,
1065 	scrollPtr->borderWidth, scrollPtr->relief);
1066 
1067     if (tiletr != NULL) {
1068 	Blt_SetTileOrigin(tkwin, tiletr, 0, 0);
1069 	Blt_TileRectangle(tkwin, pixmap, tiletr, scrollPtr->inset,
1070 		scrollPtr->inset,
1071 		(unsigned)(Tk_Width(tkwin) - 2 * scrollPtr->inset),
1072 		(unsigned)(Tk_Height(tkwin) - 2 * scrollPtr->inset));
1073     } else {
1074 	XFillRectangle(scrollPtr->display, pixmap, scrollPtr->troughGC,
1075 		scrollPtr->inset, scrollPtr->inset,
1076 		(unsigned)(Tk_Width(tkwin) - 2 * scrollPtr->inset),
1077 		(unsigned)(Tk_Height(tkwin) - 2 * scrollPtr->inset));
1078     }
1079 
1080     /*
1081      * Draw the top or left arrow.  The coordinates of the polygon
1082      * points probably seem odd, but they were carefully chosen with
1083      * respect to X's rules for filling polygons.  These point choices
1084      * cause the arrows to just fill the narrow dimension of the
1085      * scrollbar and be properly centered.
1086      */
1087     tilecur = tile;
1088     if (scrollPtr->activeField == TOP_ARROW) {
1089 	border = scrollPtr->activeBorder;
1090 	relief = scrollPtr->activeField == TOP_ARROW ? scrollPtr->activeRelief
1091 	    : TK_RELIEF_RAISED;
1092 	if (Blt_HasTile(scrollPtr->activeTile)) {
1093 	    Blt_SetTileOrigin(tkwin, scrollPtr->activeTile, 0, 0);
1094 	    tilecur = scrollPtr->activeTile;
1095 	}
1096     } else {
1097 	border = scrollPtr->bgBorder;
1098 	relief = TK_RELIEF_RAISED;
1099     }
1100 #ifdef notdef
1101     if (scrollPtr->vertical) {
1102 	points[0].x = scrollPtr->inset - 1;
1103 	points[0].y = scrollPtr->arrowLength + scrollPtr->inset - 1;
1104 	points[1].x = width + scrollPtr->inset;
1105 	points[1].y = points[0].y;
1106 	points[2].x = width / 2 + scrollPtr->inset;
1107 	points[2].y = scrollPtr->inset - 1;
1108     } else {
1109 	points[0].x = scrollPtr->arrowLength + scrollPtr->inset - 1;
1110 	points[0].y = scrollPtr->inset - 1;
1111 	points[1].x = scrollPtr->inset;
1112 	points[1].y = width / 2 + scrollPtr->inset;
1113 	points[2].x = points[0].x;
1114 	points[2].y = width + scrollPtr->inset;
1115     }
1116     if (tilecur != NULL) {
1117 	Blt_TilePolygon(tkwin, pixmap, tilecur, points, 3);
1118 	Tk_Draw3DPolygon(tkwin, pixmap, border, points, 3,
1119 	    elementBorderWidth, relief);
1120     } else {
1121 	Tk_Fill3DPolygon(tkwin, pixmap, border, points, 3,
1122 	    elementBorderWidth, relief);
1123     }
1124 #else
1125     Blt_Fill3DRectangleTile(tkwin, pixmap, border, scrollPtr->inset,
1126        scrollPtr->inset, width, width, elementBorderWidth, relief, tilecur, 0, 0);
1127 
1128     Blt_DrawArrow(scrollPtr->display, pixmap, scrollPtr->copyGC,
1129 		  scrollPtr->inset + width / 2,
1130 		  scrollPtr->inset + width / 2,
1131 		  STD_ARROW_HEIGHT,
1132 		  (scrollPtr->vertical) ? ARROW_UP : ARROW_LEFT);
1133 #endif
1134     /*
1135      * Display the bottom or right arrow.
1136      */
1137     tilecur = tile;
1138     if (scrollPtr->activeField == BOTTOM_ARROW) {
1139 	border = scrollPtr->activeBorder;
1140 	relief = scrollPtr->activeField == BOTTOM_ARROW
1141 	    ? scrollPtr->activeRelief : TK_RELIEF_RAISED;
1142 	if (Blt_HasTile(scrollPtr->activeTile)) {
1143 	    Blt_SetTileOrigin(tkwin, scrollPtr->activeTile, 0, 0);
1144 	    tilecur = scrollPtr->activeTile;
1145 	}
1146     } else {
1147 	border = scrollPtr->bgBorder;
1148 	relief = TK_RELIEF_RAISED;
1149     }
1150 #ifdef notdef
1151     if (scrollPtr->vertical) {
1152 	points[0].x = scrollPtr->inset;
1153 	points[0].y = Tk_Height(tkwin) - scrollPtr->arrowLength
1154 	    - scrollPtr->inset + 1;
1155 	points[1].x = width / 2 + scrollPtr->inset;
1156 	points[1].y = Tk_Height(tkwin) - scrollPtr->inset;
1157 	points[2].x = width + scrollPtr->inset;
1158 	points[2].y = points[0].y;
1159     } else {
1160 	points[0].x = Tk_Width(tkwin) - scrollPtr->arrowLength
1161 	    - scrollPtr->inset + 1;
1162 	points[0].y = scrollPtr->inset - 1;
1163 	points[1].x = points[0].x;
1164 	points[1].y = width + scrollPtr->inset;
1165 	points[2].x = Tk_Width(tkwin) - scrollPtr->inset;
1166 	points[2].y = width / 2 + scrollPtr->inset;
1167     }
1168     if (tile != NULL) {
1169 	Blt_TilePolygon(tkwin, pixmap, tile, points, 3);
1170 	Tk_Draw3DPolygon(tkwin, pixmap, border, points, 3,
1171 	    elementBorderWidth, relief);
1172     } else {
1173 	Tk_Fill3DPolygon(tkwin, pixmap, border, points, 3,
1174 	    elementBorderWidth, relief);
1175     }
1176 #else
1177     Blt_Fill3DRectangleTile(tkwin, pixmap, border,
1178             Tk_Width(tkwin) - (width + scrollPtr->inset),
1179             Tk_Height(tkwin) - (width + scrollPtr->inset),
1180             width, width, elementBorderWidth, relief, tilecur, 0, 0);
1181 
1182     Blt_DrawArrow(scrollPtr->display, pixmap, scrollPtr->copyGC,
1183 		  Tk_Width(tkwin) - (scrollPtr->inset + width / 2) - 1,
1184 		  Tk_Height(tkwin) - (scrollPtr->inset + width / 2) - 1,
1185 		  STD_ARROW_HEIGHT,
1186 		  (scrollPtr->vertical) ? ARROW_DOWN : ARROW_RIGHT);
1187 #endif
1188     /*
1189      * Display the slider.
1190      */
1191      tilecur = tile;
1192      if (scrollPtr->activeField == SLIDER) {
1193 	border = scrollPtr->activeBorder;
1194 	relief = scrollPtr->activeField == SLIDER ? scrollPtr->activeRelief
1195 	    : TK_RELIEF_RAISED;
1196 	if (Blt_HasTile(scrollPtr->activeTile)) {
1197 	    Blt_SetTileOrigin(tkwin, scrollPtr->activeTile, 0, 0);
1198 	    tilecur = scrollPtr->activeTile;
1199 	}
1200     } else {
1201 	border = scrollPtr->bgBorder;
1202 	relief = TK_RELIEF_RAISED;
1203     }
1204     if (scrollPtr->vertical) {
1205 	if (tilecur) {
1206 	    Blt_TileRectangle(tkwin, pixmap, tilecur, scrollPtr->inset,
1207 		scrollPtr->sliderFirst, width - 1,
1208 		scrollPtr->sliderLast - scrollPtr->sliderFirst - 1);
1209 	    Blt_Draw3DRectangle(tkwin, pixmap, border,
1210 		scrollPtr->inset, scrollPtr->sliderFirst,
1211 		width, scrollPtr->sliderLast - scrollPtr->sliderFirst,
1212 		elementBorderWidth, relief);
1213 	} else {
1214 	    Blt_Fill3DRectangle(tkwin, pixmap, border,
1215 		scrollPtr->inset, scrollPtr->sliderFirst,
1216 		width, scrollPtr->sliderLast - scrollPtr->sliderFirst,
1217 		elementBorderWidth, relief);
1218 	}
1219     } else {
1220 	if (tilecur) {
1221 	    Blt_TileRectangle(tkwin, pixmap, tilecur, scrollPtr->sliderFirst,
1222 		scrollPtr->inset,
1223 		scrollPtr->sliderLast - scrollPtr->sliderFirst - 1, width - 1);
1224 	    Blt_Draw3DRectangle(tkwin, pixmap, border,
1225 		scrollPtr->sliderFirst, scrollPtr->inset,
1226 		scrollPtr->sliderLast - scrollPtr->sliderFirst, width,
1227 		elementBorderWidth, relief);
1228 	} else {
1229 	    Blt_Fill3DRectangle(tkwin, pixmap, border,
1230 		scrollPtr->sliderFirst, scrollPtr->inset,
1231 		scrollPtr->sliderLast - scrollPtr->sliderFirst, width,
1232 	       elementBorderWidth, relief);
1233 	}
1234     }
1235 
1236     if (scrollPtr->image != NULL) {
1237         /* Draw adornment image centered on bar. */
1238         int imgW, imgH, xLen, yLen, yStart, xStart, yOfs = 0, xOfs = 0, pad = 1 + scrollPtr->borderWidth;
1239 
1240         Tk_SizeOfImage(scrollPtr->image, &imgW, &imgH);
1241 
1242         if (scrollPtr->vertical) {
1243             xLen = width;
1244             yLen = scrollPtr->sliderLast - scrollPtr->sliderFirst;
1245         } else {
1246             yLen = width;
1247             xLen = scrollPtr->sliderLast - scrollPtr->sliderFirst;
1248 
1249         }
1250         if (imgW > (xLen-pad*2)) {
1251             int oWid = imgW;
1252             imgW = (xLen-pad*2);
1253             xOfs = (oWid-imgW)/2;
1254         }
1255         if (imgH > (yLen-pad*2)) {
1256             int oWid = imgH;
1257             imgH = (yLen-pad*2);
1258             yOfs = (oWid-imgH)/2;
1259         }
1260         xStart = (xLen - imgW)/2+pad;
1261         yStart = (yLen - imgH)/2+pad;
1262         if (scrollPtr->vertical) {
1263             yStart += scrollPtr->sliderFirst;
1264         } else {
1265             xStart += scrollPtr->sliderFirst;
1266         }
1267         if (xStart>=0 && yStart>=0 && imgW > 0 && imgH > 0) {
1268             Tk_RedrawImage(scrollPtr->image, xOfs, yOfs, imgW, imgH, pixmap, xStart, yStart);
1269         }
1270     }
1271 
1272     /*
1273      * Copy the information from the off-screen pixmap onto the screen,
1274      * then delete the pixmap.
1275      */
1276 
1277     XCopyArea(scrollPtr->display, pixmap, Tk_WindowId(tkwin),
1278 	scrollPtr->copyGC, 0, 0, (unsigned)Tk_Width(tkwin),
1279 	(unsigned)Tk_Height(tkwin), 0, 0);
1280     Tk_FreePixmap(scrollPtr->display, pixmap);
1281 
1282   done:
1283     scrollPtr->flags &= ~REDRAW_PENDING;
1284 }
1285 
1286 /*
1287  *--------------------------------------------------------------
1288  *
1289  * ScrollbarEventProc --
1290  *
1291  *	This procedure is invoked by the Tk dispatcher for various
1292  *	events on scrollbars.
1293  *
1294  * Results:
1295  *	None.
1296  *
1297  * Side effects:
1298  *	When the window gets deleted, internal structures get
1299  *	cleaned up.  When it gets exposed, it is redisplayed.
1300  *
1301  *--------------------------------------------------------------
1302  */
1303 
1304 static void
ScrollbarEventProc(clientData,eventPtr)1305 ScrollbarEventProc(clientData, eventPtr)
1306     ClientData clientData;	/* Information about window. */
1307     XEvent *eventPtr;		/* Information about event. */
1308 {
1309     Scrollbar *scrollPtr = clientData;
1310 
1311     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
1312 	EventuallyRedraw(scrollPtr);
1313     } else if (eventPtr->type == DestroyNotify) {
1314 	if (scrollPtr->tkwin != NULL) {
1315 	    scrollPtr->tkwin = NULL;
1316 	    Tcl_DeleteCommandFromToken(scrollPtr->interp,scrollPtr->widgetCmd);
1317 	}
1318 	if (scrollPtr->flags & REDRAW_PENDING) {
1319 	    Tcl_CancelIdleCall(DisplayScrollbar, (ClientData)scrollPtr);
1320 	}
1321 	Tcl_EventuallyFree((ClientData)scrollPtr,
1322 	    (Tcl_FreeProc *)DestroyScrollbar);
1323     } else if (eventPtr->type == ConfigureNotify) {
1324 	ComputeScrollbarGeometry(scrollPtr);
1325 	EventuallyRedraw(scrollPtr);
1326     } else if (eventPtr->type == FocusIn) {
1327 	if (eventPtr->xfocus.detail != NotifyInferior) {
1328 	    scrollPtr->flags |= GOT_FOCUS;
1329 	    if (scrollPtr->highlightWidth > 0) {
1330 		EventuallyRedraw(scrollPtr);
1331 	    }
1332 	}
1333     } else if (eventPtr->type == FocusOut) {
1334 	if (eventPtr->xfocus.detail != NotifyInferior) {
1335 	    scrollPtr->flags &= ~GOT_FOCUS;
1336 	    if (scrollPtr->highlightWidth > 0) {
1337 		EventuallyRedraw(scrollPtr);
1338 	    }
1339 	}
1340     }
1341 }
1342 
1343 /*
1344  *----------------------------------------------------------------------
1345  *
1346  * ScrollbarCmdDeletedProc --
1347  *
1348  *	This procedure is invoked when a widget command is deleted.  If
1349  *	the widget isn't already in the process of being destroyed,
1350  *	this command destroys it.
1351  *
1352  * Results:
1353  *	None.
1354  *
1355  * Side effects:
1356  *	The widget is destroyed.
1357  *
1358  *----------------------------------------------------------------------
1359  */
1360 
1361 static void
ScrollbarCmdDeletedProc(clientData)1362 ScrollbarCmdDeletedProc(clientData)
1363     ClientData clientData;	/* Pointer to widget record for widget. */
1364 {
1365     Scrollbar *scrollPtr = clientData;
1366     Tk_Window tkwin = scrollPtr->tkwin;
1367 
1368     /*
1369      * This procedure could be invoked either because the window was
1370      * destroyed and the command was then deleted (in which case tkwin
1371      * is NULL) or because the command was deleted, and then this procedure
1372      * destroys the widget.
1373      */
1374 
1375     if (tkwin != NULL) {
1376 #ifdef ITCL_NAMESPACES
1377 	Itk_SetWidgetCommand(scrollPtr->tkwin, (Tcl_Command) NULL);
1378 #endif
1379 	scrollPtr->tkwin = NULL;
1380 	Tk_DestroyWindow(tkwin);
1381     }
1382 }
1383 
1384 /*
1385  *----------------------------------------------------------------------
1386  *
1387  * ComputeScrollbarGeometry --
1388  *
1389  *	After changes in a scrollbar's size or configuration, this
1390  *	procedure recomputes various geometry information used in
1391  *	displaying the scrollbar.
1392  *
1393  * Results:
1394  *	None.
1395  *
1396  * Side effects:
1397  *	The scrollbar will be displayed differently.
1398  *
1399  *----------------------------------------------------------------------
1400  */
1401 
1402 static void
ComputeScrollbarGeometry(scrollPtr)1403 ComputeScrollbarGeometry(scrollPtr)
1404     register Scrollbar *scrollPtr;	/* Scrollbar whose geometry may
1405 					 * have changed. */
1406 {
1407     int width, fieldLength;
1408 
1409     if (scrollPtr->highlightWidth < 0) {
1410 	scrollPtr->highlightWidth = 0;
1411     }
1412     scrollPtr->inset = scrollPtr->highlightWidth + scrollPtr->borderWidth;
1413     width = (scrollPtr->vertical) ? Tk_Width(scrollPtr->tkwin)
1414 	: Tk_Height(scrollPtr->tkwin);
1415     scrollPtr->arrowLength = width - 2 * scrollPtr->inset + 1;
1416     fieldLength = (scrollPtr->vertical ? Tk_Height(scrollPtr->tkwin)
1417 	: Tk_Width(scrollPtr->tkwin))
1418 	- 2 * (scrollPtr->arrowLength + scrollPtr->inset);
1419     if (fieldLength < 0) {
1420 	fieldLength = 0;
1421     }
1422     scrollPtr->sliderFirst = fieldLength * scrollPtr->firstFraction;
1423     scrollPtr->sliderLast = fieldLength * scrollPtr->lastFraction;
1424 
1425     /*
1426      * Adjust the slider so that some piece of it is always
1427      * displayed in the scrollbar and so that it has at least
1428      * a minimal width (so it can be grabbed with the mouse).
1429      */
1430 
1431     if (scrollPtr->sliderFirst > (fieldLength - 2 * scrollPtr->borderWidth)) {
1432 	scrollPtr->sliderFirst = fieldLength - 2 * scrollPtr->borderWidth;
1433     }
1434     if (scrollPtr->sliderFirst < 0) {
1435 	scrollPtr->sliderFirst = 0;
1436     }
1437     if (scrollPtr->sliderLast < (scrollPtr->sliderFirst + MIN_SLIDER_LENGTH)) {
1438 	scrollPtr->sliderLast = scrollPtr->sliderFirst + MIN_SLIDER_LENGTH;
1439     }
1440     if (scrollPtr->sliderLast > fieldLength) {
1441 	scrollPtr->sliderLast = fieldLength;
1442     }
1443     scrollPtr->sliderFirst += scrollPtr->arrowLength + scrollPtr->inset;
1444     scrollPtr->sliderLast += scrollPtr->arrowLength + scrollPtr->inset;
1445 
1446     /*
1447      * Register the desired geometry for the window (leave enough space
1448      * for the two arrows plus a minimum-size slider, plus border around
1449      * the whole window, if any).  Then arrange for the window to be
1450      * redisplayed.
1451      */
1452 
1453     if (scrollPtr->vertical) {
1454 	Tk_GeometryRequest(scrollPtr->tkwin,
1455 	    scrollPtr->width + 2 * scrollPtr->inset,
1456 	    2 * (scrollPtr->arrowLength + scrollPtr->borderWidth
1457 		+ scrollPtr->inset));
1458     } else {
1459 	Tk_GeometryRequest(scrollPtr->tkwin,
1460 	    2 * (scrollPtr->arrowLength + scrollPtr->borderWidth
1461 		+ scrollPtr->inset), scrollPtr->width + 2 * scrollPtr->inset);
1462     }
1463     Tk_SetInternalBorder(scrollPtr->tkwin, scrollPtr->inset);
1464 }
1465 
1466 /*
1467  *--------------------------------------------------------------
1468  *
1469  * ScrollbarPosition --
1470  *
1471  *	Determine the scrollbar element corresponding to a
1472  *	given position.
1473  *
1474  * Results:
1475  *	One of TOP_ARROW, TOP_GAP, etc., indicating which element
1476  *	of the scrollbar covers the position given by (x, y).  If
1477  *	(x,y) is outside the scrollbar entirely, then OUTSIDE is
1478  *	returned.
1479  *
1480  * Side effects:
1481  *	None.
1482  *
1483  *--------------------------------------------------------------
1484  */
1485 static int
ScrollbarPosition(scrollPtr,x,y)1486 ScrollbarPosition(scrollPtr, x, y)
1487     register Scrollbar *scrollPtr;	/* Scrollbar widget record. */
1488     int x, y;			/* Coordinates within scrollPtr's
1489 					 * window. */
1490 {
1491     int length, width, tmp;
1492 
1493     if (scrollPtr->vertical) {
1494 	length = Tk_Height(scrollPtr->tkwin);
1495 	width = Tk_Width(scrollPtr->tkwin);
1496     } else {
1497 	tmp = x;
1498 	x = y;
1499 	y = tmp;
1500 	length = Tk_Width(scrollPtr->tkwin);
1501 	width = Tk_Height(scrollPtr->tkwin);
1502     }
1503 
1504     if ((x < scrollPtr->inset) || (x >= (width - scrollPtr->inset))
1505 	|| (y < scrollPtr->inset) || (y >= (length - scrollPtr->inset))) {
1506 	return OUTSIDE;
1507     }
1508     /*
1509      * All of the calculations in this procedure mirror those in
1510      * DisplayScrollbar.  Be sure to keep the two consistent.
1511      */
1512 
1513     if (y < (scrollPtr->inset + scrollPtr->arrowLength)) {
1514 	return TOP_ARROW;
1515     }
1516     if (y < scrollPtr->sliderFirst) {
1517 	return TOP_GAP;
1518     }
1519     if (y < scrollPtr->sliderLast) {
1520 	return SLIDER;
1521     }
1522     if (y >= (length - (scrollPtr->arrowLength + scrollPtr->inset))) {
1523 	return BOTTOM_ARROW;
1524     }
1525     return BOTTOM_GAP;
1526 }
1527 
1528 /*
1529  *--------------------------------------------------------------
1530  *
1531  * EventuallyRedraw --
1532  *
1533  *	Arrange for one or more of the fields of a scrollbar
1534  *	to be redrawn.
1535  *
1536  * Results:
1537  *	None.
1538  *
1539  * Side effects:
1540  *	None.
1541  *
1542  *--------------------------------------------------------------
1543  */
1544 
1545 static void
EventuallyRedraw(scrollPtr)1546 EventuallyRedraw(scrollPtr)
1547     register Scrollbar *scrollPtr;	/* Information about widget. */
1548 {
1549     if ((scrollPtr->tkwin == NULL) || (!Tk_IsMapped(scrollPtr->tkwin))) {
1550 	return;
1551     }
1552     if ((scrollPtr->flags & REDRAW_PENDING) == 0) {
1553 	Tcl_DoWhenIdle(DisplayScrollbar, (ClientData)scrollPtr);
1554 	scrollPtr->flags |= REDRAW_PENDING;
1555     }
1556 }
1557 
1558 int
Blt_ScrollbarInit(interp)1559 Blt_ScrollbarInit(interp)
1560     Tcl_Interp *interp;
1561 {
1562     static Blt_CmdSpec cmdSpecs[2] =
1563     {
1564 	{ "scrollbar", ScrollbarCmd, },
1565 	{ "bscrollbar", BScrollbarCmd, },
1566     };
1567 
1568     return Blt_InitCmds(interp, "blt::tile", cmdSpecs, 2);
1569 }
1570 
1571 #endif /* NO_TILESCROLLBAR */
1572