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