1/*
2 * tkScrollbar.c --
3 *
4 *	This module implements a scrollbar widgets for the Tk toolkit. A
5 *	scrollbar displays a slider and two arrows; mouse clicks on features
6 *	within the scrollbar cause scrolling commands to be invoked.
7 *
8 * Copyright (c) 1990-1994 The Regents of the University of California.
9 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
10 *
11 * See the file "license.terms" for information on usage and redistribution of
12 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 *
14 * RCS: @(#) $Id$
15 */
16
17#include "tkInt.h"
18#include "tkScrollbar.h"
19#include "default.h"
20
21/*
22 * Custom option for handling "-orient"
23 */
24
25static Tk_CustomOption orientOption = {
26    (Tk_OptionParseProc *) TkOrientParseProc,
27    TkOrientPrintProc,
28    (ClientData) NULL
29};
30
31/*
32 * Information used for argv parsing.
33 */
34
35Tk_ConfigSpec tkpScrollbarConfigSpecs[] = {
36    {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
37	DEF_SCROLLBAR_ACTIVE_BG_COLOR, Tk_Offset(TkScrollbar, activeBorder),
38	TK_CONFIG_COLOR_ONLY},
39    {TK_CONFIG_BORDER, "-activebackground", "activeBackground", "Foreground",
40	DEF_SCROLLBAR_ACTIVE_BG_MONO, Tk_Offset(TkScrollbar, activeBorder),
41	TK_CONFIG_MONO_ONLY},
42    {TK_CONFIG_RELIEF, "-activerelief", "activeRelief", "Relief",
43	DEF_SCROLLBAR_ACTIVE_RELIEF, Tk_Offset(TkScrollbar, activeRelief), 0},
44    {TK_CONFIG_BORDER, "-background", "background", "Background",
45	DEF_SCROLLBAR_BG_COLOR, Tk_Offset(TkScrollbar, bgBorder),
46	TK_CONFIG_COLOR_ONLY},
47    {TK_CONFIG_BORDER, "-background", "background", "Background",
48	DEF_SCROLLBAR_BG_MONO, Tk_Offset(TkScrollbar, bgBorder),
49	TK_CONFIG_MONO_ONLY},
50    {TK_CONFIG_SYNONYM, "-bd", "borderWidth", NULL, NULL, 0, 0},
51    {TK_CONFIG_SYNONYM, "-bg", "background", NULL, NULL, 0, 0},
52    {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
53	DEF_SCROLLBAR_BORDER_WIDTH, Tk_Offset(TkScrollbar, borderWidth), 0},
54    {TK_CONFIG_STRING, "-command", "command", "Command",
55	DEF_SCROLLBAR_COMMAND, Tk_Offset(TkScrollbar, command),
56	TK_CONFIG_NULL_OK},
57    {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
58	DEF_SCROLLBAR_CURSOR, Tk_Offset(TkScrollbar, cursor), TK_CONFIG_NULL_OK},
59    {TK_CONFIG_PIXELS, "-elementborderwidth", "elementBorderWidth",
60	"BorderWidth", DEF_SCROLLBAR_EL_BORDER_WIDTH,
61	Tk_Offset(TkScrollbar, elementBorderWidth), 0},
62    {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
63	"HighlightBackground", DEF_SCROLLBAR_HIGHLIGHT_BG,
64	Tk_Offset(TkScrollbar, highlightBgColorPtr), 0},
65    {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
66	DEF_SCROLLBAR_HIGHLIGHT,
67	Tk_Offset(TkScrollbar, highlightColorPtr), 0},
68    {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
69	"HighlightThickness",
70	DEF_SCROLLBAR_HIGHLIGHT_WIDTH, Tk_Offset(TkScrollbar, highlightWidth), 0},
71    {TK_CONFIG_BOOLEAN, "-jump", "jump", "Jump",
72	DEF_SCROLLBAR_JUMP, Tk_Offset(TkScrollbar, jump), 0},
73    {TK_CONFIG_CUSTOM, "-orient", "orient", "Orient",
74	DEF_SCROLLBAR_ORIENT, Tk_Offset(TkScrollbar, vertical), 0,
75	&orientOption},
76    {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
77	DEF_SCROLLBAR_RELIEF, Tk_Offset(TkScrollbar, relief), 0},
78    {TK_CONFIG_INT, "-repeatdelay", "repeatDelay", "RepeatDelay",
79	DEF_SCROLLBAR_REPEAT_DELAY, Tk_Offset(TkScrollbar, repeatDelay), 0},
80    {TK_CONFIG_INT, "-repeatinterval", "repeatInterval", "RepeatInterval",
81	DEF_SCROLLBAR_REPEAT_INTERVAL, Tk_Offset(TkScrollbar, repeatInterval), 0},
82    {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
83	DEF_SCROLLBAR_TAKE_FOCUS, Tk_Offset(TkScrollbar, takeFocus),
84	TK_CONFIG_NULL_OK},
85    {TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
86	DEF_SCROLLBAR_TROUGH_COLOR, Tk_Offset(TkScrollbar, troughColorPtr),
87	TK_CONFIG_COLOR_ONLY},
88    {TK_CONFIG_COLOR, "-troughcolor", "troughColor", "Background",
89	DEF_SCROLLBAR_TROUGH_MONO, Tk_Offset(TkScrollbar, troughColorPtr),
90	TK_CONFIG_MONO_ONLY},
91    {TK_CONFIG_PIXELS, "-width", "width", "Width",
92	DEF_SCROLLBAR_WIDTH, Tk_Offset(TkScrollbar, width), 0},
93    {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
94};
95
96/*
97 * Forward declarations for functions defined later in this file:
98 */
99
100static int		ConfigureScrollbar(Tcl_Interp *interp,
101			    TkScrollbar *scrollPtr, int argc,
102			    CONST char **argv, int flags);
103static void		ScrollbarCmdDeletedProc(ClientData clientData);
104static int		ScrollbarWidgetCmd(ClientData clientData,
105			    Tcl_Interp *, int argc, CONST char **argv);
106
107/*
108 *--------------------------------------------------------------
109 *
110 * Tk_ScrollbarCmd --
111 *
112 *	This function is invoked to process the "scrollbar" Tcl command. See
113 *	the user documentation for details on what it does.
114 *
115 * Results:
116 *	A standard Tcl result.
117 *
118 * Side effects:
119 *	See the user documentation.
120 *
121 *--------------------------------------------------------------
122 */
123
124int
125Tk_ScrollbarCmd(
126    ClientData clientData,	/* Main window associated with interpreter. */
127    Tcl_Interp *interp,		/* Current interpreter. */
128    int argc,			/* Number of arguments. */
129    CONST char **argv)		/* Argument strings. */
130{
131    Tk_Window tkwin = (Tk_Window) clientData;
132    register TkScrollbar *scrollPtr;
133    Tk_Window newWin;
134
135    if (argc < 2) {
136	Tcl_AppendResult(interp, "wrong # args: should be \"",
137		argv[0], " pathName ?options?\"", NULL);
138	return TCL_ERROR;
139    }
140
141    newWin = Tk_CreateWindowFromPath(interp, tkwin, argv[1], NULL);
142    if (newWin == NULL) {
143	return TCL_ERROR;
144    }
145
146    Tk_SetClass(newWin, "Scrollbar");
147    scrollPtr = TkpCreateScrollbar(newWin);
148
149    Tk_SetClassProcs(newWin, &tkpScrollbarProcs, (ClientData) scrollPtr);
150
151    /*
152     * Initialize fields that won't be initialized by ConfigureScrollbar, or
153     * which ConfigureScrollbar expects to have reasonable values (e.g.
154     * resource pointers).
155     */
156
157    scrollPtr->tkwin = newWin;
158    scrollPtr->display = Tk_Display(newWin);
159    scrollPtr->interp = interp;
160    scrollPtr->widgetCmd = Tcl_CreateCommand(interp,
161	    Tk_PathName(scrollPtr->tkwin), ScrollbarWidgetCmd,
162	    (ClientData) scrollPtr, ScrollbarCmdDeletedProc);
163    scrollPtr->vertical = 0;
164    scrollPtr->width = 0;
165    scrollPtr->command = NULL;
166    scrollPtr->commandSize = 0;
167    scrollPtr->repeatDelay = 0;
168    scrollPtr->repeatInterval = 0;
169    scrollPtr->borderWidth = 0;
170    scrollPtr->bgBorder = NULL;
171    scrollPtr->activeBorder = NULL;
172    scrollPtr->troughColorPtr = NULL;
173    scrollPtr->relief = TK_RELIEF_FLAT;
174    scrollPtr->highlightWidth = 0;
175    scrollPtr->highlightBgColorPtr = NULL;
176    scrollPtr->highlightColorPtr = NULL;
177    scrollPtr->inset = 0;
178    scrollPtr->elementBorderWidth = -1;
179    scrollPtr->arrowLength = 0;
180    scrollPtr->sliderFirst = 0;
181    scrollPtr->sliderLast = 0;
182    scrollPtr->activeField = 0;
183    scrollPtr->activeRelief = TK_RELIEF_RAISED;
184    scrollPtr->totalUnits = 0;
185    scrollPtr->windowUnits = 0;
186    scrollPtr->firstUnit = 0;
187    scrollPtr->lastUnit = 0;
188    scrollPtr->firstFraction = 0.0;
189    scrollPtr->lastFraction = 0.0;
190    scrollPtr->cursor = None;
191    scrollPtr->takeFocus = NULL;
192    scrollPtr->flags = 0;
193
194    if (ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2, 0) != TCL_OK) {
195	Tk_DestroyWindow(scrollPtr->tkwin);
196	return TCL_ERROR;
197    }
198
199    Tcl_SetResult(interp, Tk_PathName(scrollPtr->tkwin), TCL_STATIC);
200    return TCL_OK;
201}
202
203/*
204 *--------------------------------------------------------------
205 *
206 * ScrollbarWidgetCmd --
207 *
208 *	This function is invoked to process the Tcl command that corresponds
209 *	to a widget managed by this module. See the user documentation for
210 *	details on what it does.
211 *
212 * Results:
213 *	A standard Tcl result.
214 *
215 * Side effects:
216 *	See the user documentation.
217 *
218 *--------------------------------------------------------------
219 */
220
221static int
222ScrollbarWidgetCmd(
223    ClientData clientData,	/* Information about scrollbar widget. */
224    Tcl_Interp *interp,		/* Current interpreter. */
225    int argc,			/* Number of arguments. */
226    CONST char **argv)		/* Argument strings. */
227{
228    register TkScrollbar *scrollPtr = (TkScrollbar *) clientData;
229    int result = TCL_OK;
230    size_t length;
231    int c;
232
233    if (argc < 2) {
234	Tcl_AppendResult(interp, "wrong # args: should be \"",
235		argv[0], " option ?arg arg ...?\"", NULL);
236	return TCL_ERROR;
237    }
238    Tcl_Preserve((ClientData) scrollPtr);
239    c = argv[1][0];
240    length = strlen(argv[1]);
241    if ((c == 'a') && (strncmp(argv[1], "activate", length) == 0)) {
242	int oldActiveField;
243	if (argc == 2) {
244	    switch (scrollPtr->activeField) {
245	    case TOP_ARROW:
246		Tcl_SetResult(interp, "arrow1", TCL_STATIC);
247		break;
248	    case SLIDER:
249		Tcl_SetResult(interp, "slider", TCL_STATIC);
250		break;
251	    case BOTTOM_ARROW:
252		Tcl_SetResult(interp, "arrow2", TCL_STATIC);
253		break;
254	    }
255	    goto done;
256	}
257	if (argc != 3) {
258	    Tcl_AppendResult(interp, "wrong # args: should be \"",
259		    argv[0], " activate element\"", NULL);
260	    goto error;
261	}
262	c = argv[2][0];
263	length = strlen(argv[2]);
264	oldActiveField = scrollPtr->activeField;
265	if ((c == 'a') && (strcmp(argv[2], "arrow1") == 0)) {
266	    scrollPtr->activeField = TOP_ARROW;
267	} else if ((c == 'a') && (strcmp(argv[2], "arrow2") == 0)) {
268	    scrollPtr->activeField = BOTTOM_ARROW;
269	} else if ((c == 's') && (strncmp(argv[2], "slider", length) == 0)) {
270	    scrollPtr->activeField = SLIDER;
271	} else {
272	    scrollPtr->activeField = OUTSIDE;
273	}
274	if (oldActiveField != scrollPtr->activeField) {
275	    TkScrollbarEventuallyRedraw(scrollPtr);
276	}
277    } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
278	    && (length >= 2)) {
279	if (argc != 3) {
280	    Tcl_AppendResult(interp, "wrong # args: should be \"",
281		    argv[0], " cget option\"",
282		    NULL);
283	    goto error;
284	}
285	result = Tk_ConfigureValue(interp, scrollPtr->tkwin,
286		tkpScrollbarConfigSpecs, (char *) scrollPtr, argv[2], 0);
287    } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
288	    && (length >= 2)) {
289	if (argc == 2) {
290	    result = Tk_ConfigureInfo(interp, scrollPtr->tkwin,
291		    tkpScrollbarConfigSpecs, (char *) scrollPtr, NULL, 0);
292	} else if (argc == 3) {
293	    result = Tk_ConfigureInfo(interp, scrollPtr->tkwin,
294		    tkpScrollbarConfigSpecs, (char *) scrollPtr, argv[2], 0);
295	} else {
296	    result = ConfigureScrollbar(interp, scrollPtr, argc-2, argv+2,
297		    TK_CONFIG_ARGV_ONLY);
298	}
299    } else if ((c == 'd') && (strncmp(argv[1], "delta", length) == 0)) {
300	int xDelta, yDelta, pixels, length;
301	double fraction;
302	char buf[TCL_DOUBLE_SPACE];
303
304	if (argc != 4) {
305	    Tcl_AppendResult(interp, "wrong # args: should be \"",
306		    argv[0], " delta xDelta yDelta\"", NULL);
307	    goto error;
308	}
309	if ((Tcl_GetInt(interp, argv[2], &xDelta) != TCL_OK)
310		|| (Tcl_GetInt(interp, argv[3], &yDelta) != TCL_OK)) {
311	    goto error;
312	}
313	if (scrollPtr->vertical) {
314	    pixels = yDelta;
315	    length = Tk_Height(scrollPtr->tkwin) - 1
316		    - 2*(scrollPtr->arrowLength + scrollPtr->inset);
317	} else {
318	    pixels = xDelta;
319	    length = Tk_Width(scrollPtr->tkwin) - 1
320		    - 2*(scrollPtr->arrowLength + scrollPtr->inset);
321	}
322	if (length == 0) {
323	    fraction = 0.0;
324	} else {
325	    fraction = ((double) pixels / (double) length);
326	}
327	Tcl_PrintDouble(NULL, fraction, buf);
328	Tcl_SetResult(interp, buf, TCL_VOLATILE);
329    } else if ((c == 'f') && (strncmp(argv[1], "fraction", length) == 0)) {
330	int x, y, pos, length;
331	double fraction;
332	char buf[TCL_DOUBLE_SPACE];
333
334	if (argc != 4) {
335	    Tcl_AppendResult(interp, "wrong # args: should be \"",
336		    argv[0], " fraction x y\"", NULL);
337	    goto error;
338	}
339	if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
340		|| (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
341	    goto error;
342	}
343	if (scrollPtr->vertical) {
344	    pos = y - (scrollPtr->arrowLength + scrollPtr->inset);
345	    length = Tk_Height(scrollPtr->tkwin) - 1
346		    - 2*(scrollPtr->arrowLength + scrollPtr->inset);
347	} else {
348	    pos = x - (scrollPtr->arrowLength + scrollPtr->inset);
349	    length = Tk_Width(scrollPtr->tkwin) - 1
350		    - 2*(scrollPtr->arrowLength + scrollPtr->inset);
351	}
352	if (length == 0) {
353	    fraction = 0.0;
354	} else {
355	    fraction = ((double) pos / (double) length);
356	}
357	if (fraction < 0) {
358	    fraction = 0;
359	} else if (fraction > 1.0) {
360	    fraction = 1.0;
361	}
362	Tcl_PrintDouble(NULL, fraction, buf);
363	Tcl_SetResult(interp, buf, TCL_VOLATILE);
364    } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
365	if (argc != 2) {
366	    Tcl_AppendResult(interp, "wrong # args: should be \"",
367		    argv[0], " get\"", NULL);
368	    goto error;
369	}
370	if (scrollPtr->flags & NEW_STYLE_COMMANDS) {
371	    char first[TCL_DOUBLE_SPACE], last[TCL_DOUBLE_SPACE];
372
373	    Tcl_PrintDouble(interp, scrollPtr->firstFraction, first);
374	    Tcl_PrintDouble(interp, scrollPtr->lastFraction, last);
375	    Tcl_AppendResult(interp, first, " ", last, NULL);
376	} else {
377	    char buf[TCL_INTEGER_SPACE * 4];
378
379	    sprintf(buf, "%d %d %d %d", scrollPtr->totalUnits,
380		    scrollPtr->windowUnits, scrollPtr->firstUnit,
381		    scrollPtr->lastUnit);
382	    Tcl_SetResult(interp, buf, TCL_VOLATILE);
383	}
384    } else if ((c == 'i') && (strncmp(argv[1], "identify", length) == 0)) {
385	int x, y, thing;
386
387	if (argc != 4) {
388	    Tcl_AppendResult(interp, "wrong # args: should be \"",
389		    argv[0], " identify x y\"", NULL);
390	    goto error;
391	}
392	if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK)
393		|| (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
394	    goto error;
395	}
396	thing = TkpScrollbarPosition(scrollPtr, x,y);
397	switch (thing) {
398	case TOP_ARROW:
399	    Tcl_SetResult(interp, "arrow1", TCL_STATIC);
400	    break;
401	case TOP_GAP:
402	    Tcl_SetResult(interp, "trough1", TCL_STATIC);
403	    break;
404	case SLIDER:
405	    Tcl_SetResult(interp, "slider", TCL_STATIC);
406	    break;
407	case BOTTOM_GAP:
408	    Tcl_SetResult(interp, "trough2", TCL_STATIC);
409	    break;
410	case BOTTOM_ARROW:
411	    Tcl_SetResult(interp, "arrow2", TCL_STATIC);
412	    break;
413	}
414    } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)) {
415	int totalUnits, windowUnits, firstUnit, lastUnit;
416
417	if (argc == 4) {
418	    double first, last;
419
420	    if (Tcl_GetDouble(interp, argv[2], &first) != TCL_OK) {
421		goto error;
422	    }
423	    if (Tcl_GetDouble(interp, argv[3], &last) != TCL_OK) {
424		goto error;
425	    }
426	    if (first < 0) {
427		scrollPtr->firstFraction = 0;
428	    } else if (first > 1.0) {
429		scrollPtr->firstFraction = 1.0;
430	    } else {
431		scrollPtr->firstFraction = first;
432	    }
433	    if (last < scrollPtr->firstFraction) {
434		scrollPtr->lastFraction = scrollPtr->firstFraction;
435	    } else if (last > 1.0) {
436		scrollPtr->lastFraction = 1.0;
437	    } else {
438		scrollPtr->lastFraction = last;
439	    }
440	    scrollPtr->flags |= NEW_STYLE_COMMANDS;
441	} else if (argc == 6) {
442	    if (Tcl_GetInt(interp, argv[2], &totalUnits) != TCL_OK) {
443		goto error;
444	    }
445	    if (totalUnits < 0) {
446		totalUnits = 0;
447	    }
448	    if (Tcl_GetInt(interp, argv[3], &windowUnits) != TCL_OK) {
449		goto error;
450	    }
451	    if (windowUnits < 0) {
452		windowUnits = 0;
453	    }
454	    if (Tcl_GetInt(interp, argv[4], &firstUnit) != TCL_OK) {
455		goto error;
456	    }
457	    if (Tcl_GetInt(interp, argv[5], &lastUnit) != TCL_OK) {
458		goto error;
459	    }
460	    if (totalUnits > 0) {
461		if (lastUnit < firstUnit) {
462		    lastUnit = firstUnit;
463		}
464	    } else {
465		firstUnit = lastUnit = 0;
466	    }
467	    scrollPtr->totalUnits = totalUnits;
468	    scrollPtr->windowUnits = windowUnits;
469	    scrollPtr->firstUnit = firstUnit;
470	    scrollPtr->lastUnit = lastUnit;
471	    if (scrollPtr->totalUnits == 0) {
472		scrollPtr->firstFraction = 0.0;
473		scrollPtr->lastFraction = 1.0;
474	    } else {
475		scrollPtr->firstFraction = ((double) firstUnit)/totalUnits;
476		scrollPtr->lastFraction = ((double) (lastUnit+1))/totalUnits;
477	    }
478	    scrollPtr->flags &= ~NEW_STYLE_COMMANDS;
479	} else {
480	    Tcl_AppendResult(interp, "wrong # args: should be \"",
481		    argv[0], " set firstFraction lastFraction\" or \"",
482		    argv[0],
483		    " set totalUnits windowUnits firstUnit lastUnit\"", NULL);
484	    goto error;
485	}
486	TkpComputeScrollbarGeometry(scrollPtr);
487	TkScrollbarEventuallyRedraw(scrollPtr);
488    } else {
489	Tcl_AppendResult(interp, "bad option \"", argv[1],
490		"\": must be activate, cget, configure, delta, fraction, ",
491		"get, identify, or set", NULL);
492	goto error;
493    }
494
495  done:
496    Tcl_Release((ClientData) scrollPtr);
497    return result;
498
499  error:
500    Tcl_Release((ClientData) scrollPtr);
501    return TCL_ERROR;
502}
503
504/*
505 *----------------------------------------------------------------------
506 *
507 * ConfigureScrollbar --
508 *
509 *	This function is called to process an argv/argc list, plus the Tk
510 *	option database, in order to configure (or reconfigure) a scrollbar
511 *	widget.
512 *
513 * Results:
514 *	The return value is a standard Tcl result. If TCL_ERROR is returned,
515 *	then the interp's result contains an error message.
516 *
517 * Side effects:
518 *	Configuration information, such as colors, border width, etc. get set
519 *	for scrollPtr; old resources get freed, if there were any.
520 *
521 *----------------------------------------------------------------------
522 */
523
524static int
525ConfigureScrollbar(
526    Tcl_Interp *interp,		/* Used for error reporting. */
527    register TkScrollbar *scrollPtr,
528				/* Information about widget; may or may not
529				 * already have values for some fields. */
530    int argc,			/* Number of valid entries in argv. */
531    CONST char **argv,		/* Arguments. */
532    int flags)			/* Flags to pass to Tk_ConfigureWidget. */
533{
534    if (Tk_ConfigureWidget(interp, scrollPtr->tkwin, tkpScrollbarConfigSpecs,
535	    argc, argv, (char *) scrollPtr, flags) != TCL_OK) {
536	return TCL_ERROR;
537    }
538
539    /*
540     * A few options need special processing, such as setting the background
541     * from a 3-D border.
542     */
543
544    if (scrollPtr->command != NULL) {
545        scrollPtr->commandSize = (int)strlen(scrollPtr->command);
546    } else {
547	scrollPtr->commandSize = 0;
548    }
549
550    /*
551     * Configure platform specific options.
552     */
553
554    TkpConfigureScrollbar(scrollPtr);
555
556    /*
557     * Register the desired geometry for the window (leave enough space for
558     * the two arrows plus a minimum-size slider, plus border around the whole
559     * window, if any). Then arrange for the window to be redisplayed.
560     */
561
562    TkpComputeScrollbarGeometry(scrollPtr);
563    TkScrollbarEventuallyRedraw(scrollPtr);
564    return TCL_OK;
565}
566
567/*
568 *--------------------------------------------------------------
569 *
570 * TkScrollbarEventProc --
571 *
572 *	This function is invoked by the Tk dispatcher for various events on
573 *	scrollbars.
574 *
575 * Results:
576 *	None.
577 *
578 * Side effects:
579 *	When the window gets deleted, internal structures get cleaned up.
580 *	When it gets exposed, it is redisplayed.
581 *
582 *--------------------------------------------------------------
583 */
584
585void
586TkScrollbarEventProc(
587    ClientData clientData,	/* Information about window. */
588    XEvent *eventPtr)		/* Information about event. */
589{
590    TkScrollbar *scrollPtr = (TkScrollbar *) clientData;
591
592    if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
593	TkScrollbarEventuallyRedraw(scrollPtr);
594    } else if (eventPtr->type == DestroyNotify) {
595	TkpDestroyScrollbar(scrollPtr);
596	if (scrollPtr->tkwin != NULL) {
597	    scrollPtr->tkwin = NULL;
598	    Tcl_DeleteCommandFromToken(scrollPtr->interp,
599		    scrollPtr->widgetCmd);
600	}
601	if (scrollPtr->flags & REDRAW_PENDING) {
602	    Tcl_CancelIdleCall(TkpDisplayScrollbar, (ClientData) scrollPtr);
603	}
604	/*
605	 * Free up all the stuff that requires special handling, then let
606	 * Tk_FreeOptions handle all the standard option-related stuff.
607	 */
608
609	Tk_FreeOptions(tkpScrollbarConfigSpecs, (char *) scrollPtr,
610		scrollPtr->display, 0);
611	Tcl_EventuallyFree((ClientData) scrollPtr, TCL_DYNAMIC);
612    } else if (eventPtr->type == ConfigureNotify) {
613	TkpComputeScrollbarGeometry(scrollPtr);
614	TkScrollbarEventuallyRedraw(scrollPtr);
615    } else if (eventPtr->type == FocusIn) {
616	if (eventPtr->xfocus.detail != NotifyInferior) {
617	    scrollPtr->flags |= GOT_FOCUS;
618	    if (scrollPtr->highlightWidth > 0) {
619		TkScrollbarEventuallyRedraw(scrollPtr);
620	    }
621	}
622    } else if (eventPtr->type == FocusOut) {
623	if (eventPtr->xfocus.detail != NotifyInferior) {
624	    scrollPtr->flags &= ~GOT_FOCUS;
625	    if (scrollPtr->highlightWidth > 0) {
626		TkScrollbarEventuallyRedraw(scrollPtr);
627	    }
628	}
629    }
630}
631
632/*
633 *----------------------------------------------------------------------
634 *
635 * ScrollbarCmdDeletedProc --
636 *
637 *	This function is invoked when a widget command is deleted. If the
638 *	widget isn't already in the process of being destroyed, this command
639 *	destroys it.
640 *
641 * Results:
642 *	None.
643 *
644 * Side effects:
645 *	The widget is destroyed.
646 *
647 *----------------------------------------------------------------------
648 */
649
650static void
651ScrollbarCmdDeletedProc(
652    ClientData clientData)	/* Pointer to widget record for widget. */
653{
654    TkScrollbar *scrollPtr = (TkScrollbar *) clientData;
655    Tk_Window tkwin = scrollPtr->tkwin;
656
657    /*
658     * This function could be invoked either because the window was destroyed
659     * and the command was then deleted (in which case tkwin is NULL) or
660     * because the command was deleted, and then this function destroys the
661     * widget.
662     */
663
664    if (tkwin != NULL) {
665	scrollPtr->tkwin = NULL;
666	Tk_DestroyWindow(tkwin);
667    }
668}
669
670/*
671 *--------------------------------------------------------------
672 *
673 * TkScrollbarEventuallyRedraw --
674 *
675 *	Arrange for one or more of the fields of a scrollbar to be redrawn.
676 *
677 * Results:
678 *	None.
679 *
680 * Side effects:
681 *	None.
682 *
683 *--------------------------------------------------------------
684 */
685
686void
687TkScrollbarEventuallyRedraw(
688    TkScrollbar *scrollPtr)	/* Information about widget. */
689{
690    if ((scrollPtr->tkwin == NULL) || (!Tk_IsMapped(scrollPtr->tkwin))) {
691	return;
692    }
693    if ((scrollPtr->flags & REDRAW_PENDING) == 0) {
694	Tcl_DoWhenIdle(TkpDisplayScrollbar, (ClientData) scrollPtr);
695	scrollPtr->flags |= REDRAW_PENDING;
696    }
697}
698
699/*
700 * Local Variables:
701 * mode: c
702 * c-basic-offset: 4
703 * fill-column: 78
704 * End:
705 */
706