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