1/*
2 * tkWinScrollbar.c --
3 *
4 *	This file implements the Windows specific portion of the scrollbar
5 *	widget.
6 *
7 * Copyright (c) 1996 by Sun Microsystems, Inc.
8 *
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id$
13 */
14
15#include "tkWinInt.h"
16#include "tkScrollbar.h"
17
18/*
19 * The following constant is used to specify the maximum scroll position. This
20 * value is limited by the Win32 API to either 16-bits or 32-bits, depending
21 * on the context. For now we'll just use a value small enough to fit in
22 * 16-bits, but which gives us 4-digits of precision.
23 */
24
25#define MAX_SCROLL 10000
26
27/*
28 * Declaration of Windows specific scrollbar structure.
29 */
30
31typedef struct WinScrollbar {
32    TkScrollbar info;		/* Generic scrollbar info. */
33    WNDPROC oldProc;		/* Old window procedure. */
34    int lastVertical;		/* 1 if was vertical at last refresh. */
35    HWND hwnd;			/* Current window handle. */
36    int winFlags;		/* Various flags; see below. */
37} WinScrollbar;
38
39/*
40 * Flag bits for native scrollbars:
41 *
42 * IN_MODAL_LOOP:		Non-zero means this scrollbar is in the middle
43 *				of a modal loop.
44 * ALREADY_DEAD:		Non-zero means this scrollbar has been
45 *				destroyed, but has not been cleaned up.
46 */
47
48#define IN_MODAL_LOOP	1
49#define ALREADY_DEAD	2
50
51/*
52 * Cached system metrics used to determine scrollbar geometry.
53 */
54
55static int initialized = 0;
56static int hArrowWidth, hThumb; /* Horizontal control metrics. */
57static int vArrowWidth, vArrowHeight, vThumb; /* Vertical control metrics. */
58
59TCL_DECLARE_MUTEX(winScrlbrMutex)
60
61/*
62 * This variable holds the default width for a scrollbar in string form for
63 * use in a Tk_ConfigSpec.
64 */
65
66static char defWidth[TCL_INTEGER_SPACE];
67
68/*
69 * Declarations for functions defined in this file.
70 */
71
72static Window		CreateProc(Tk_Window tkwin, Window parent,
73			    ClientData instanceData);
74static void		ModalLoopProc(Tk_Window tkwin, XEvent *eventPtr);
75static int		ScrollbarBindProc(ClientData clientData,
76			    Tcl_Interp *interp, XEvent *eventPtr,
77			    Tk_Window tkwin, KeySym keySym);
78static LRESULT CALLBACK	ScrollbarProc(HWND hwnd, UINT message, WPARAM wParam,
79			    LPARAM lParam);
80static void		UpdateScrollbar(WinScrollbar *scrollPtr);
81static void		UpdateScrollbarMetrics(void);
82
83/*
84 * The class procedure table for the scrollbar widget.
85 */
86
87Tk_ClassProcs tkpScrollbarProcs = {
88    sizeof(Tk_ClassProcs),	/* size */
89    NULL,			/* worldChangedProc */
90    CreateProc,			/* createProc */
91    ModalLoopProc,		/* modalProc */
92};
93
94
95/*
96 *----------------------------------------------------------------------
97 *
98 * TkpCreateScrollbar --
99 *
100 *	Allocate a new TkScrollbar structure.
101 *
102 * Results:
103 *	Returns a newly allocated TkScrollbar structure.
104 *
105 * Side effects:
106 *	Registers an event handler for the widget.
107 *
108 *----------------------------------------------------------------------
109 */
110
111TkScrollbar *
112TkpCreateScrollbar(
113    Tk_Window tkwin)
114{
115    WinScrollbar *scrollPtr;
116    TkWindow *winPtr = (TkWindow *)tkwin;
117
118    if (!initialized) {
119	Tcl_MutexLock(&winScrlbrMutex);
120	UpdateScrollbarMetrics();
121	initialized = 1;
122	Tcl_MutexUnlock(&winScrlbrMutex);
123    }
124
125    scrollPtr = (WinScrollbar *) ckalloc(sizeof(WinScrollbar));
126    scrollPtr->winFlags = 0;
127    scrollPtr->hwnd = NULL;
128
129    Tk_CreateEventHandler(tkwin,
130	    ExposureMask|StructureNotifyMask|FocusChangeMask,
131	    TkScrollbarEventProc, (ClientData) scrollPtr);
132
133    if (!Tcl_GetAssocData(winPtr->mainPtr->interp, "TkScrollbar", NULL)) {
134	Tcl_SetAssocData(winPtr->mainPtr->interp, "TkScrollbar", NULL,
135		(ClientData)1);
136	TkCreateBindingProcedure(winPtr->mainPtr->interp,
137		winPtr->mainPtr->bindingTable,
138		(ClientData)Tk_GetUid("Scrollbar"), "<ButtonPress>",
139		ScrollbarBindProc, NULL, NULL);
140    }
141
142    return (TkScrollbar *) scrollPtr;
143}
144
145/*
146 *----------------------------------------------------------------------
147 *
148 * UpdateScrollbar --
149 *
150 *	This function updates the position and size of the scrollbar thumb
151 *	based on the current settings.
152 *
153 * Results:
154 *	None.
155 *
156 * Side effects:
157 *	Moves the thumb.
158 *
159 *----------------------------------------------------------------------
160 */
161
162static void
163UpdateScrollbar(
164    WinScrollbar *scrollPtr)
165{
166    SCROLLINFO scrollInfo;
167    double thumbSize;
168
169    /*
170     * Update the current scrollbar position and shape.
171     */
172
173    scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
174    scrollInfo.cbSize = sizeof(scrollInfo);
175    scrollInfo.nMin = 0;
176    scrollInfo.nMax = MAX_SCROLL;
177    thumbSize = (scrollPtr->info.lastFraction - scrollPtr->info.firstFraction);
178    scrollInfo.nPage = ((UINT) (thumbSize * (double) MAX_SCROLL)) + 1;
179    if (thumbSize < 1.0) {
180	scrollInfo.nPos = (int)
181		((scrollPtr->info.firstFraction / (1.0-thumbSize))
182		* (MAX_SCROLL - (scrollInfo.nPage - 1)));
183    } else {
184	scrollInfo.nPos = 0;
185
186	/*
187	 * Disable the scrollbar when there is nothing to scroll. This is
188	 * standard Windows style (see eg Notepad). Also prevents possible
189	 * crash on XP+ systems [Bug #624116].
190	 */
191
192	scrollInfo.fMask |= SIF_DISABLENOSCROLL;
193    }
194    SetScrollInfo(scrollPtr->hwnd, SB_CTL, &scrollInfo, TRUE);
195}
196
197/*
198 *----------------------------------------------------------------------
199 *
200 * CreateProc --
201 *
202 *	This function creates a new Scrollbar control, subclasses the
203 *	instance, and generates a new Window object.
204 *
205 * Results:
206 *	Returns the newly allocated Window object, or None on failure.
207 *
208 * Side effects:
209 *	Causes a new Scrollbar control to come into existence.
210 *
211 *----------------------------------------------------------------------
212 */
213
214static Window
215CreateProc(
216    Tk_Window tkwin,		/* Token for window. */
217    Window parentWin,		/* Parent of new window. */
218    ClientData instanceData)	/* Scrollbar instance data. */
219{
220    DWORD style;
221    Window window;
222    HWND parent;
223    TkWindow *winPtr;
224    WinScrollbar *scrollPtr = (WinScrollbar *)instanceData;
225
226    parent = Tk_GetHWND(parentWin);
227
228    if (scrollPtr->info.vertical) {
229	style = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS
230		| SBS_VERT | SBS_RIGHTALIGN;
231    } else {
232	style = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS
233		| SBS_HORZ | SBS_BOTTOMALIGN;
234    }
235
236    scrollPtr->hwnd = CreateWindow("SCROLLBAR", NULL, style,
237	    Tk_X(tkwin), Tk_Y(tkwin), Tk_Width(tkwin), Tk_Height(tkwin),
238	    parent, NULL, Tk_GetHINSTANCE(), NULL);
239
240    /*
241     * Ensure new window is inserted into the stacking order at the correct
242     * place.
243     */
244
245    SetWindowPos(scrollPtr->hwnd, HWND_TOP, 0, 0, 0, 0,
246		    SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
247
248    for (winPtr = ((TkWindow*)tkwin)->nextPtr; winPtr != NULL;
249	    winPtr = winPtr->nextPtr) {
250	if ((winPtr->window != None) && !(winPtr->flags & TK_TOP_HIERARCHY)) {
251	    TkWinSetWindowPos(scrollPtr->hwnd, Tk_GetHWND(winPtr->window),
252		    Below);
253	    break;
254	}
255    }
256
257    scrollPtr->lastVertical = scrollPtr->info.vertical;
258    scrollPtr->oldProc = (WNDPROC)SetWindowLongPtr(scrollPtr->hwnd,
259	    GWLP_WNDPROC, (INT_PTR) ScrollbarProc);
260    window = Tk_AttachHWND(tkwin, scrollPtr->hwnd);
261
262    UpdateScrollbar(scrollPtr);
263    return window;
264}
265
266/*
267 *--------------------------------------------------------------
268 *
269 * TkpDisplayScrollbar --
270 *
271 *	This procedure redraws the contents of a scrollbar window. It is
272 *	invoked as a do-when-idle handler, so it only runs when there's
273 *	nothing else for the application to do.
274 *
275 * Results:
276 *	None.
277 *
278 * Side effects:
279 *	Information appears on the screen.
280 *
281 *--------------------------------------------------------------
282 */
283
284void
285TkpDisplayScrollbar(
286    ClientData clientData)	/* Information about window. */
287{
288    WinScrollbar *scrollPtr = (WinScrollbar *) clientData;
289    Tk_Window tkwin = scrollPtr->info.tkwin;
290
291    scrollPtr->info.flags &= ~REDRAW_PENDING;
292    if ((tkwin == NULL) || !Tk_IsMapped(tkwin)) {
293	return;
294    }
295
296    /*
297     * Destroy and recreate the scrollbar control if the orientation has
298     * changed.
299     */
300
301    if (scrollPtr->lastVertical != scrollPtr->info.vertical) {
302	HWND hwnd = Tk_GetHWND(Tk_WindowId(tkwin));
303
304	SetWindowLongPtr(hwnd, GWLP_WNDPROC, (INT_PTR) scrollPtr->oldProc);
305	DestroyWindow(hwnd);
306
307	CreateProc(tkwin, Tk_WindowId(Tk_Parent(tkwin)),
308		(ClientData) scrollPtr);
309    } else {
310	UpdateScrollbar(scrollPtr);
311    }
312}
313
314/*
315 *----------------------------------------------------------------------
316 *
317 * TkpDestroyScrollbar --
318 *
319 *	Free data structures associated with the scrollbar control.
320 *
321 * Results:
322 *	None.
323 *
324 * Side effects:
325 *	Restores the default control state.
326 *
327 *----------------------------------------------------------------------
328 */
329
330void
331TkpDestroyScrollbar(
332    TkScrollbar *scrollPtr)
333{
334    WinScrollbar *winScrollPtr = (WinScrollbar *)scrollPtr;
335    HWND hwnd = winScrollPtr->hwnd;
336
337    if (hwnd) {
338	SetWindowLongPtr(hwnd, GWLP_WNDPROC, (INT_PTR) winScrollPtr->oldProc);
339	if (winScrollPtr->winFlags & IN_MODAL_LOOP) {
340	    ((TkWindow *)scrollPtr->tkwin)->flags |= TK_DONT_DESTROY_WINDOW;
341	    SetParent(hwnd, NULL);
342	}
343    }
344    winScrollPtr->winFlags |= ALREADY_DEAD;
345}
346
347/*
348 *----------------------------------------------------------------------
349 *
350 * UpdateScrollbarMetrics --
351 *
352 *	This function retrieves the current system metrics for a scrollbar.
353 *
354 * Results:
355 *	None.
356 *
357 * Side effects:
358 *	Updates the geometry cache info for all scrollbars.
359 *
360 *----------------------------------------------------------------------
361 */
362
363void
364UpdateScrollbarMetrics(void)
365{
366    Tk_ConfigSpec *specPtr;
367
368    hArrowWidth = GetSystemMetrics(SM_CXHSCROLL);
369    hThumb = GetSystemMetrics(SM_CXHTHUMB);
370    vArrowWidth = GetSystemMetrics(SM_CXVSCROLL);
371    vArrowHeight = GetSystemMetrics(SM_CYVSCROLL);
372    vThumb = GetSystemMetrics(SM_CYVTHUMB);
373
374    sprintf(defWidth, "%d", vArrowWidth);
375    for (specPtr = tkpScrollbarConfigSpecs; specPtr->type != TK_CONFIG_END;
376	    specPtr++) {
377	if (specPtr->offset == Tk_Offset(TkScrollbar, width)) {
378	    specPtr->defValue = defWidth;
379	}
380    }
381}
382
383/*
384 *----------------------------------------------------------------------
385 *
386 * TkpComputeScrollbarGeometry --
387 *
388 *	After changes in a scrollbar's size or configuration, this procedure
389 *	recomputes various geometry information used in displaying the
390 *	scrollbar.
391 *
392 * Results:
393 *	None.
394 *
395 * Side effects:
396 *	The scrollbar will be displayed differently.
397 *
398 *----------------------------------------------------------------------
399 */
400
401void
402TkpComputeScrollbarGeometry(
403    register TkScrollbar *scrollPtr)
404				/* Scrollbar whose geometry may have
405				 * changed. */
406{
407    int fieldLength, minThumbSize;
408
409    /*
410     * Windows doesn't use focus rings on scrollbars, but we still perform
411     * basic sanity checks to appease backwards compatibility.
412     */
413
414    if (scrollPtr->highlightWidth < 0) {
415	scrollPtr->highlightWidth = 0;
416    }
417
418    if (scrollPtr->vertical) {
419	scrollPtr->arrowLength = vArrowHeight;
420	fieldLength = Tk_Height(scrollPtr->tkwin);
421	minThumbSize = vThumb;
422    } else {
423	scrollPtr->arrowLength = hArrowWidth;
424	fieldLength = Tk_Width(scrollPtr->tkwin);
425	minThumbSize = hThumb;
426    }
427    fieldLength -= 2*scrollPtr->arrowLength;
428    if (fieldLength < 0) {
429	fieldLength = 0;
430    }
431    scrollPtr->sliderFirst = (int) ((double)fieldLength
432	    * scrollPtr->firstFraction);
433    scrollPtr->sliderLast = (int) ((double)fieldLength
434	    * scrollPtr->lastFraction);
435
436    /*
437     * Adjust the slider so that some piece of it is always displayed in the
438     * scrollbar and so that it has at least a minimal width (so it can be
439     * grabbed with the mouse).
440     */
441
442    if (scrollPtr->sliderFirst > fieldLength) {
443	scrollPtr->sliderFirst = fieldLength;
444    }
445    if (scrollPtr->sliderFirst < 0) {
446	scrollPtr->sliderFirst = 0;
447    }
448    if (scrollPtr->sliderLast < (scrollPtr->sliderFirst
449	    + minThumbSize)) {
450	scrollPtr->sliderLast = scrollPtr->sliderFirst + minThumbSize;
451    }
452    if (scrollPtr->sliderLast > fieldLength) {
453	scrollPtr->sliderLast = fieldLength;
454    }
455    scrollPtr->sliderFirst += scrollPtr->arrowLength;
456    scrollPtr->sliderLast += scrollPtr->arrowLength;
457
458    /*
459     * Register the desired geometry for the window (leave enough space for
460     * the two arrows plus a minimum-size slider, plus border around the whole
461     * window, if any). Then arrange for the window to be redisplayed.
462     */
463
464    if (scrollPtr->vertical) {
465	Tk_GeometryRequest(scrollPtr->tkwin,
466		scrollPtr->width, 2*scrollPtr->arrowLength + minThumbSize);
467    } else {
468	Tk_GeometryRequest(scrollPtr->tkwin,
469		2*scrollPtr->arrowLength + minThumbSize, scrollPtr->width);
470    }
471    Tk_SetInternalBorder(scrollPtr->tkwin, 0);
472}
473
474/*
475 *----------------------------------------------------------------------
476 *
477 * ScrollbarProc --
478 *
479 *	This function is call by Windows whenever an event occurs on a
480 *	scrollbar control created by Tk.
481 *
482 * Results:
483 *	Standard Windows return value.
484 *
485 * Side effects:
486 *	May generate events.
487 *
488 *----------------------------------------------------------------------
489 */
490
491static LRESULT CALLBACK
492ScrollbarProc(
493    HWND hwnd,
494    UINT message,
495    WPARAM wParam,
496    LPARAM lParam)
497{
498    LRESULT result;
499    POINT point;
500    WinScrollbar *scrollPtr;
501    Tk_Window tkwin = Tk_HWNDToWindow(hwnd);
502
503    if (tkwin == NULL) {
504	Tcl_Panic("ScrollbarProc called on an invalid HWND");
505    }
506    scrollPtr = (WinScrollbar *)((TkWindow*)tkwin)->instanceData;
507
508    switch(message) {
509    case WM_HSCROLL:
510    case WM_VSCROLL: {
511	Tcl_Interp *interp;
512	Tcl_DString cmdString;
513	int command = LOWORD(wParam);
514	int code;
515
516	GetCursorPos(&point);
517	Tk_TranslateWinEvent(NULL, WM_MOUSEMOVE, 0,
518		MAKELPARAM(point.x, point.y), &result);
519
520	if (command == SB_ENDSCROLL) {
521	    return 0;
522	}
523
524	/*
525	 * Bail out immediately if there isn't a command to invoke.
526	 */
527
528	if (scrollPtr->info.commandSize == 0) {
529	    Tcl_ServiceAll();
530	    return 0;
531	}
532
533	Tcl_DStringInit(&cmdString);
534	Tcl_DStringAppend(&cmdString, scrollPtr->info.command,
535		scrollPtr->info.commandSize);
536
537	if (command == SB_LINELEFT || command == SB_LINERIGHT) {
538	    Tcl_DStringAppendElement(&cmdString, "scroll");
539	    Tcl_DStringAppendElement(&cmdString,
540		    (command == SB_LINELEFT ) ? "-1" : "1");
541	    Tcl_DStringAppendElement(&cmdString, "units");
542	} else if (command == SB_PAGELEFT || command == SB_PAGERIGHT) {
543	    Tcl_DStringAppendElement(&cmdString, "scroll");
544	    Tcl_DStringAppendElement(&cmdString,
545		    (command == SB_PAGELEFT ) ? "-1" : "1");
546	    Tcl_DStringAppendElement(&cmdString, "pages");
547	} else {
548	    char valueString[TCL_DOUBLE_SPACE];
549	    double pos = 0.0;
550
551	    switch (command) {
552	    case SB_THUMBPOSITION:
553		pos = ((double)HIWORD(wParam)) / MAX_SCROLL;
554		break;
555	    case SB_THUMBTRACK:
556		pos = ((double)HIWORD(wParam)) / MAX_SCROLL;
557		break;
558	    case SB_TOP:
559		pos = 0.0;
560		break;
561	    case SB_BOTTOM:
562		pos = 1.0;
563		break;
564	    }
565
566	    Tcl_PrintDouble(NULL, pos, valueString);
567	    Tcl_DStringAppendElement(&cmdString, "moveto");
568	    Tcl_DStringAppendElement(&cmdString, valueString);
569	}
570
571	interp = scrollPtr->info.interp;
572	code = Tcl_GlobalEval(interp, cmdString.string);
573	if (code != TCL_OK && code != TCL_CONTINUE && code != TCL_BREAK) {
574	    Tcl_AddErrorInfo(interp, "\n    (scrollbar command)");
575	    Tcl_BackgroundError(interp);
576	}
577	Tcl_DStringFree(&cmdString);
578
579	Tcl_ServiceAll();
580	return 0;
581    }
582
583    default:
584	if (Tk_TranslateWinEvent(hwnd, message, wParam, lParam, &result)) {
585	    return result;
586	}
587    }
588    return CallWindowProc(scrollPtr->oldProc, hwnd, message, wParam, lParam);
589}
590
591/*
592 *----------------------------------------------------------------------
593 *
594 * TkpConfigureScrollbar --
595 *
596 *	This procedure is called after the generic code has finished
597 *	processing configuration options, in order to configure platform
598 *	specific options.
599 *
600 * Results:
601 *	None.
602 *
603 * Side effects:
604 *	None.
605 *
606 *----------------------------------------------------------------------
607 */
608
609void
610TkpConfigureScrollbar(
611    register TkScrollbar *scrollPtr)
612				/* Information about widget; may or may not
613				 * already have values for some fields. */
614{
615}
616
617/*
618 *--------------------------------------------------------------
619 *
620 * ScrollbarBindProc --
621 *
622 *	This procedure is invoked when the default <ButtonPress> binding on
623 *	the Scrollbar bind tag fires.
624 *
625 * Results:
626 *	None.
627 *
628 * Side effects:
629 *	The event enters a modal loop.
630 *
631 *--------------------------------------------------------------
632 */
633
634static int
635ScrollbarBindProc(
636    ClientData clientData,
637    Tcl_Interp *interp,
638    XEvent *eventPtr,
639    Tk_Window tkwin,
640    KeySym keySym)
641{
642    TkWindow *winPtr = (TkWindow *) tkwin;
643
644    if (eventPtr->type == ButtonPress) {
645	winPtr->flags |= TK_DEFER_MODAL;
646    }
647    return TCL_OK;
648}
649
650/*
651 *----------------------------------------------------------------------
652 *
653 * ModalLoopProc --
654 *
655 *	This function is invoked at the end of the event processing whenever
656 *	the ScrollbarBindProc has been invoked for a ButtonPress event.
657 *
658 * Results:
659 *	None.
660 *
661 * Side effects:
662 *	Enters a modal loop.
663 *
664 *----------------------------------------------------------------------
665 */
666
667static void
668ModalLoopProc(
669    Tk_Window tkwin,
670    XEvent *eventPtr)
671{
672    TkWindow *winPtr = (TkWindow *) tkwin;
673    WinScrollbar *scrollPtr = (WinScrollbar *) winPtr->instanceData;
674    int oldMode;
675
676    if (scrollPtr->hwnd) {
677	Tcl_Preserve(scrollPtr);
678	scrollPtr->winFlags |= IN_MODAL_LOOP;
679	oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL);
680	TkWinResendEvent(scrollPtr->oldProc, scrollPtr->hwnd, eventPtr);
681	(void) Tcl_SetServiceMode(oldMode);
682	scrollPtr->winFlags &= ~IN_MODAL_LOOP;
683	if (scrollPtr->hwnd && scrollPtr->winFlags & ALREADY_DEAD) {
684	    DestroyWindow(scrollPtr->hwnd);
685	}
686	Tcl_Release(scrollPtr);
687    }
688}
689
690/*
691 *--------------------------------------------------------------
692 *
693 * TkpScrollbarPosition --
694 *
695 *	Determine the scrollbar element corresponding to a given position.
696 *
697 * Results:
698 *	One of TOP_ARROW, TOP_GAP, etc., indicating which element of the
699 *	scrollbar covers the position given by (x, y). If (x,y) is outside the
700 *	scrollbar entirely, then OUTSIDE is returned.
701 *
702 * Side effects:
703 *	None.
704 *
705 *--------------------------------------------------------------
706 */
707
708int
709TkpScrollbarPosition(
710    register TkScrollbar *scrollPtr,
711				/* Scrollbar widget record. */
712    int x, int y)		/* Coordinates within scrollPtr's window. */
713{
714    int length, width, tmp;
715
716    if (scrollPtr->vertical) {
717	length = Tk_Height(scrollPtr->tkwin);
718	width = Tk_Width(scrollPtr->tkwin);
719    } else {
720	tmp = x;
721	x = y;
722	y = tmp;
723	length = Tk_Width(scrollPtr->tkwin);
724	width = Tk_Height(scrollPtr->tkwin);
725    }
726
727    if ((x < scrollPtr->inset) || (x >= (width - scrollPtr->inset))
728	    || (y < scrollPtr->inset) || (y >= (length - scrollPtr->inset))) {
729	return OUTSIDE;
730    }
731
732    /*
733     * All of the calculations in this procedure mirror those in
734     * TkpDisplayScrollbar. Be sure to keep the two consistent.
735     */
736
737    if (y < (scrollPtr->inset + scrollPtr->arrowLength)) {
738	return TOP_ARROW;
739    }
740    if (y < scrollPtr->sliderFirst) {
741	return TOP_GAP;
742    }
743    if (y < scrollPtr->sliderLast) {
744	return SLIDER;
745    }
746    if (y >= (length - (scrollPtr->arrowLength + scrollPtr->inset))) {
747	return BOTTOM_ARROW;
748    }
749    return BOTTOM_GAP;
750}
751
752/*
753 * Local Variables:
754 * mode: c
755 * c-basic-offset: 4
756 * fill-column: 78
757 * End:
758 */
759