Release 951105
[wine] / controls / scroll.c
1 /*              
2  * Interface code to SCROLLBAR widget
3  *
4  * Copyright  Martin Ayotte, 1993
5  * Copyright  Alexandre Julliard, 1994
6  *
7  * Small fixes and implemented SB_THUMBPOSITION
8  * by Peter Broadhurst, 940611
9  */
10
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include "windows.h"
16 #include "syscolor.h"
17 #include "sysmetrics.h"
18 #include "scroll.h"
19 #include "user.h"
20 #include "graphics.h"
21 #include "win.h"
22 #include "stddebug.h"
23 /* #define DEBUG_SCROLL */
24 #include "debug.h"
25
26
27 static HBITMAP hUpArrow = 0;
28 static HBITMAP hDnArrow = 0;
29 static HBITMAP hLfArrow = 0;
30 static HBITMAP hRgArrow = 0;
31 static HBITMAP hUpArrowD = 0;
32 static HBITMAP hDnArrowD = 0;
33 static HBITMAP hLfArrowD = 0;
34 static HBITMAP hRgArrowD = 0;
35 static HBITMAP hUpArrowI = 0;
36 static HBITMAP hDnArrowI = 0;
37 static HBITMAP hLfArrowI = 0;
38 static HBITMAP hRgArrowI = 0;
39
40 #define TOP_ARROW(flags,pressed) \
41    (((flags)&ESB_DISABLE_UP) ? hUpArrowI : ((pressed) ? hUpArrowD:hUpArrow))
42 #define BOTTOM_ARROW(flags,pressed) \
43    (((flags)&ESB_DISABLE_DOWN) ? hDnArrowI : ((pressed) ? hDnArrowD:hDnArrow))
44 #define LEFT_ARROW(flags,pressed) \
45    (((flags)&ESB_DISABLE_LEFT) ? hLfArrowI : ((pressed) ? hLfArrowD:hLfArrow))
46 #define RIGHT_ARROW(flags,pressed) \
47    (((flags)&ESB_DISABLE_RIGHT) ? hRgArrowI : ((pressed) ? hRgArrowD:hRgArrow))
48
49
50   /* Minimum size of the rectangle between the arrows */
51 #define SCROLL_MIN_RECT  4  
52
53   /* Delay (in ms) before first repetition when holding the button down */
54 #define SCROLL_FIRST_DELAY   200
55
56   /* Delay (in ms) between scroll repetitions */
57 #define SCROLL_REPEAT_DELAY  100
58
59   /* Scroll timer id */
60 #define SCROLL_TIMER   0
61
62   /* Scroll-bar hit testing */
63 enum SCROLL_HITTEST
64 {
65     SCROLL_NOWHERE,      /* Outside the scroll bar */
66     SCROLL_TOP_ARROW,    /* Top or left arrow */
67     SCROLL_TOP_RECT,     /* Rectangle between the top arrow and the thumb */
68     SCROLL_THUMB,        /* Thumb rectangle */
69     SCROLL_BOTTOM_RECT,  /* Rectangle between the thumb and the bottom arrow */
70     SCROLL_BOTTOM_ARROW  /* Bottom or right arrow */
71 };
72
73
74 /***********************************************************************
75  *           SCROLL_LoadBitmaps
76  */
77 static void SCROLL_LoadBitmaps(void)
78 {
79     hUpArrow  = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_UPARROW));
80     hDnArrow  = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_DNARROW));
81     hLfArrow  = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_LFARROW));
82     hRgArrow  = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_RGARROW));
83     hUpArrowD = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_UPARROWD));
84     hDnArrowD = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_DNARROWD));
85     hLfArrowD = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_LFARROWD));
86     hRgArrowD = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_RGARROWD));
87     hUpArrowI = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_UPARROWI));
88     hDnArrowI = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_DNARROWI));
89     hLfArrowI = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_LFARROWI));
90     hRgArrowI = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_RGARROWI));
91 }
92
93
94 /***********************************************************************
95  *           SCROLL_GetScrollInfo
96  */
97 static SCROLLINFO *SCROLL_GetScrollInfo( HWND hwnd, int nBar )
98 {
99     HANDLE handle;
100     WND *wndPtr = WIN_FindWndPtr( hwnd );
101
102     if (!wndPtr) return NULL;
103     switch(nBar)
104     {
105         case SB_HORZ: handle = wndPtr->hHScroll; break;
106         case SB_VERT: handle = wndPtr->hVScroll; break;
107         case SB_CTL:  return (SCROLLINFO *)wndPtr->wExtra;
108         default:      return NULL;
109     }
110
111     if (!handle)  /* Create the info structure if needed */
112     {
113         if ((handle = USER_HEAP_ALLOC( sizeof(SCROLLINFO) )))
114         {
115             SCROLLINFO *infoPtr = (SCROLLINFO *) USER_HEAP_LIN_ADDR( handle );
116             infoPtr->MinVal = infoPtr->CurVal = 0;
117             infoPtr->MaxVal = 100;
118             infoPtr->flags  = ESB_ENABLE_BOTH;
119             if (nBar == SB_HORZ) wndPtr->hHScroll = handle;
120             else wndPtr->hVScroll = handle;
121         }
122         if (!hUpArrow) SCROLL_LoadBitmaps();
123     }
124     return (SCROLLINFO *) USER_HEAP_LIN_ADDR( handle );
125 }
126
127
128 /***********************************************************************
129  *           SCROLL_GetScrollBarRect
130  *
131  * Compute the scroll bar rectangle, in drawing coordinates (i.e. client
132  * coords for SB_CTL, window coords for SB_VERT and SB_HORZ).
133  * 'arrowSize' returns the width or height of an arrow (depending on the
134  * orientation of the scrollbar), and 'thumbPos' returns the position of
135  * the thumb relative to the left or to the top.
136  * Return TRUE if the scrollbar is vertical, FALSE if horizontal.
137  */
138 static BOOL SCROLL_GetScrollBarRect( HWND hwnd, int nBar, RECT *lprect,
139                                      WORD *arrowSize, WORD *thumbPos )
140 {
141     int pixels;
142     BOOL vertical;
143     WND *wndPtr = WIN_FindWndPtr( hwnd );
144
145     switch(nBar)
146     {
147       case SB_HORZ:
148         lprect->left   = wndPtr->rectClient.left - wndPtr->rectWindow.left - 1;
149         lprect->top    = wndPtr->rectClient.bottom - wndPtr->rectWindow.top;
150         lprect->right  = wndPtr->rectClient.right - wndPtr->rectWindow.left +1;
151         lprect->bottom = lprect->top + SYSMETRICS_CYHSCROLL + 1;
152         vertical = FALSE;
153         break;
154
155       case SB_VERT:
156         lprect->left   = wndPtr->rectClient.right - wndPtr->rectWindow.left;
157         lprect->top    = wndPtr->rectClient.top - wndPtr->rectWindow.top - 1;
158         lprect->right  = lprect->left + SYSMETRICS_CXVSCROLL + 1;
159         lprect->bottom = wndPtr->rectClient.bottom - wndPtr->rectWindow.top +1;
160         vertical = TRUE;
161         break;
162
163       case SB_CTL:
164         GetClientRect( hwnd, lprect );
165         vertical = ((wndPtr->dwStyle & SBS_VERT) != 0);
166         break;
167
168     default:
169         return FALSE;
170     }
171
172     if (vertical) pixels = lprect->bottom - lprect->top;
173     else pixels = lprect->right - lprect->left;
174
175     if (pixels > 2*SYSMETRICS_CXVSCROLL + SCROLL_MIN_RECT)
176     {
177         *arrowSize = SYSMETRICS_CXVSCROLL;
178     }
179     else if (pixels > SCROLL_MIN_RECT)
180     {
181         *arrowSize = (pixels - SCROLL_MIN_RECT) / 2;
182     }
183     else *arrowSize = 0;
184     
185     if ((pixels -= 3*SYSMETRICS_CXVSCROLL+1) > 0)
186     {
187         SCROLLINFO *info = SCROLL_GetScrollInfo( hwnd, nBar );
188         if ((info->flags & ESB_DISABLE_BOTH) == ESB_DISABLE_BOTH)
189             *thumbPos = 0;
190         else if (info->MinVal == info->MaxVal)
191             *thumbPos = *arrowSize;
192         else
193             *thumbPos = *arrowSize + pixels * (info->CurVal - info->MinVal) /
194                                               (info->MaxVal - info->MinVal);
195     }
196     else *thumbPos = 0;
197     return vertical;
198 }
199
200
201 /***********************************************************************
202  *           SCROLL_GetThumbVal
203  *
204  * Compute the current scroll position based on the thumb position in pixels
205  * from the top of the scroll-bar.
206  */
207 static UINT SCROLL_GetThumbVal( SCROLLINFO *infoPtr, RECT *rect,
208                                 BOOL vertical, WORD pos )
209 {
210     int pixels = vertical ? rect->bottom-rect->top : rect->right-rect->left;
211     if ((pixels -= 3*SYSMETRICS_CXVSCROLL+1) <= 0) return infoPtr->MinVal;
212     pos = MAX( 0, pos - SYSMETRICS_CXVSCROLL );
213     if (pos > pixels) pos = pixels;
214     dprintf_scroll(stddeb,"GetThumbVal: pos=%d ret=%d\n", pos,
215                    (infoPtr->MinVal
216             + (UINT)(((int)pos * (infoPtr->MaxVal-infoPtr->MinVal) + pixels/2)
217                      / pixels)) );
218     return (infoPtr->MinVal
219             + (UINT)(((int)pos * (infoPtr->MaxVal-infoPtr->MinVal) + pixels/2)
220                      / pixels));
221 }
222
223
224 /***********************************************************************
225  *           SCROLL_HitTest
226  *
227  * Scroll-bar hit testing (don't confuse this with WM_NCHITTEST!).
228  */
229 static enum SCROLL_HITTEST SCROLL_HitTest( HWND hwnd, int nBar, POINT pt )
230 {
231     WORD arrowSize, thumbPos;
232     RECT rect;
233
234     BOOL vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
235                                              &arrowSize, &thumbPos );
236     if (!PtInRect( &rect, pt )) return SCROLL_NOWHERE;
237
238     if (vertical)
239     {
240         if (pt.y <= rect.top + arrowSize + 1) return SCROLL_TOP_ARROW;
241         if (pt.y >= rect.bottom - arrowSize) return SCROLL_BOTTOM_ARROW;
242         if (!thumbPos) return SCROLL_TOP_RECT;
243         pt.y -= rect.top;
244         if (pt.y < (INT)thumbPos) return SCROLL_TOP_RECT;
245         if (pt.y > thumbPos+SYSMETRICS_CYHSCROLL) return SCROLL_BOTTOM_RECT;
246         return SCROLL_THUMB;
247     }
248     else  /* horizontal */
249     {
250         if (pt.x <= rect.left + arrowSize) return SCROLL_TOP_ARROW;
251         if (pt.x >= rect.right - arrowSize) return SCROLL_BOTTOM_ARROW;
252         if (!thumbPos) return SCROLL_TOP_RECT;
253         pt.x -= rect.left;
254         if (pt.x < (INT)thumbPos) return SCROLL_TOP_RECT;
255         if (pt.x > thumbPos+SYSMETRICS_CXVSCROLL) return SCROLL_BOTTOM_RECT;
256         return SCROLL_THUMB;
257     }
258 }
259
260
261 /***********************************************************************
262  *           SCROLL_DrawArrows
263  *
264  * Draw the scroll bar arrows.
265  */
266 static void SCROLL_DrawArrows( HDC hdc, SCROLLINFO *infoPtr, RECT *rect,
267                                WORD arrowSize, BOOL vertical,
268                                BOOL top_pressed, BOOL bottom_pressed )
269 {
270     HDC hdcMem = CreateCompatibleDC( hdc );
271     HBITMAP hbmpPrev = SelectObject( hdcMem, vertical ?
272                                     TOP_ARROW(infoPtr->flags, top_pressed)
273                                     : LEFT_ARROW(infoPtr->flags, top_pressed));
274     SetStretchBltMode( hdc, STRETCH_DELETESCANS );
275     StretchBlt( hdc, rect->left, rect->top,
276                 vertical ? rect->right-rect->left : arrowSize+1,
277                 vertical ? arrowSize+1 : rect->bottom-rect->top,
278                 hdcMem, 0, 0,
279                 SYSMETRICS_CXVSCROLL + 1, SYSMETRICS_CYHSCROLL + 1,
280                 SRCCOPY );
281
282     SelectObject( hdcMem, vertical ?
283                   BOTTOM_ARROW( infoPtr->flags, bottom_pressed )
284                   : RIGHT_ARROW( infoPtr->flags, bottom_pressed ) );
285     if (vertical)
286         StretchBlt( hdc, rect->left, rect->bottom - arrowSize - 1,
287                    rect->right - rect->left, arrowSize + 1,
288                    hdcMem, 0, 0,
289                    SYSMETRICS_CXVSCROLL + 1, SYSMETRICS_CYHSCROLL + 1,
290                    SRCCOPY );
291     else
292         StretchBlt( hdc, rect->right - arrowSize - 1, rect->top,
293                    arrowSize + 1, rect->bottom - rect->top,
294                    hdcMem, 0, 0,
295                    SYSMETRICS_CXVSCROLL + 1, SYSMETRICS_CYHSCROLL + 1,
296                    SRCCOPY );
297     SelectObject( hdcMem, hbmpPrev );
298     DeleteDC( hdcMem );
299 }
300
301
302 /***********************************************************************
303  *           SCROLL_DrawMovingThumb
304  *
305  * Draw the moving thumb rectangle.
306  */
307 static void SCROLL_DrawMovingThumb( HDC hdc, RECT *rect, BOOL vertical,
308                                     WORD arrowSize, WORD thumbPos )
309 {
310     RECT r = *rect;
311     if (vertical)
312     {
313         r.top += thumbPos;
314         if (r.top < rect->top + arrowSize) r.top = rect->top + arrowSize;
315         if (r.top + SYSMETRICS_CYHSCROLL >= rect->bottom - arrowSize)
316             r.top = rect->bottom - arrowSize - SYSMETRICS_CYHSCROLL - 1;
317         r.bottom = r.top + SYSMETRICS_CYHSCROLL + 1;
318     }
319     else
320     {
321         r.left += thumbPos;
322         if (r.left < rect->left + arrowSize) r.left = rect->left + arrowSize;
323         if (r.left + SYSMETRICS_CXVSCROLL >= rect->right - arrowSize)
324             r.left = rect->right - arrowSize - SYSMETRICS_CXVSCROLL - 1;
325         r.right = r.left + SYSMETRICS_CXVSCROLL + 1;
326     }
327     InflateRect( &r, -1, -1 );
328     DrawFocusRect( hdc, &r );
329 }
330
331
332 /***********************************************************************
333  *           SCROLL_DrawInterior
334  *
335  * Draw the scroll bar interior (everything except the arrows).
336  */
337 static void SCROLL_DrawInterior( HWND hwnd, HDC hdc, int nBar, RECT *rect,
338                                  WORD arrowSize, WORD thumbPos, WORD flags,
339                                  BOOL vertical, BOOL top_selected,
340                                  BOOL bottom_selected )
341 {
342     RECT r;
343
344       /* Select the correct brush and pen */
345
346     SelectObject( hdc, sysColorObjects.hpenWindowFrame );
347     if ((flags & ESB_DISABLE_BOTH) == ESB_DISABLE_BOTH)
348     {
349           /* This ought to be the color of the parent window */
350         SelectObject( hdc, sysColorObjects.hbrushWindow );
351     }
352     else
353     {
354         if (nBar == SB_CTL)  /* Only scrollbar controls send WM_CTLCOLOR */
355         {
356 #ifdef WINELIB32
357             HBRUSH hbrush = (HBRUSH)SendMessage( GetParent(hwnd), 
358                                                  WM_CTLCOLORSCROLLBAR,
359                                                  (WPARAM)hdc, (LPARAM)hwnd );
360 #else
361             HBRUSH hbrush = SendMessage( GetParent(hwnd), WM_CTLCOLOR, hdc,
362                                          MAKELONG(hwnd, CTLCOLOR_SCROLLBAR) );
363 #endif
364             SelectObject( hdc, hbrush );
365         }
366         else SelectObject( hdc, sysColorObjects.hbrushScrollbar );
367     }
368
369       /* Calculate the scroll rectangle */
370
371     r = *rect;
372     if (vertical)
373     {
374         r.top    += arrowSize;
375         r.bottom -= arrowSize;
376     }
377     else
378     {
379         r.left  += arrowSize;
380         r.right -= arrowSize;
381     }
382
383       /* Draw the scroll bar frame */
384
385     MoveTo( hdc, r.left, r.top );
386     LineTo( hdc, r.right-1, r.top );
387     LineTo( hdc, r.right-1, r.bottom-1 );
388     LineTo( hdc, r.left, r.bottom-1 );
389     LineTo( hdc, r.left, r.top );
390
391       /* Draw the scroll rectangles and thumb */
392
393     if (!thumbPos)  /* No thumb to draw */
394     {
395         PatBlt( hdc, r.left+1, r.top+1, r.right - r.left - 2,
396                 r.bottom - r.top - 2, PATCOPY );
397         return;
398     }
399
400     if (vertical)
401     {
402         PatBlt( hdc, r.left + 1, r.top + 1,
403                 r.right - r.left - 2,
404                 thumbPos - arrowSize,
405                 top_selected ? 0x0f0000 : PATCOPY );
406         r.top += thumbPos - arrowSize;
407         PatBlt( hdc, r.left + 1, r.top + SYSMETRICS_CYHSCROLL + 1,
408                 r.right - r.left - 2,
409                 r.bottom - r.top - SYSMETRICS_CYHSCROLL - 2,
410                 bottom_selected ? 0x0f0000 : PATCOPY );
411         r.bottom = r.top + SYSMETRICS_CYHSCROLL + 1;
412     }
413     else  /* horizontal */
414     {
415         PatBlt( hdc, r.left + 1, r.top + 1,
416                 thumbPos - arrowSize,
417                 r.bottom - r.top - 2,
418                 top_selected ? 0x0f0000 : PATCOPY );
419         r.left += thumbPos - arrowSize;
420         PatBlt( hdc, r.left + SYSMETRICS_CYHSCROLL + 1, r.top + 1,
421                 r.right - r.left - SYSMETRICS_CYHSCROLL - 2,
422                 r.bottom - r.top - 2,
423                 bottom_selected ? 0x0f0000 : PATCOPY );
424         r.right = r.left + SYSMETRICS_CXVSCROLL + 1;
425     }
426
427       /* Draw the thumb */
428
429     SelectObject( hdc, sysColorObjects.hbrushBtnFace );
430     Rectangle( hdc, r.left, r.top, r.right, r.bottom );
431     InflateRect( &r, -1, -1 );
432     GRAPH_DrawReliefRect( hdc, &r, 1, 2, FALSE );
433 }
434
435
436 /***********************************************************************
437  *           SCROLL_DrawScrollBar
438  *
439  * Redraw the whole scrollbar.
440  */
441 void SCROLL_DrawScrollBar( HWND hwnd, HDC hdc, int nBar )
442 {
443     WORD arrowSize, thumbPos;
444     RECT rect;
445     BOOL vertical;
446     WND *wndPtr = WIN_FindWndPtr( hwnd );
447     SCROLLINFO *infoPtr = SCROLL_GetScrollInfo( hwnd, nBar );
448
449     if (!wndPtr || !infoPtr ||
450         ((nBar == SB_VERT) && !(wndPtr->dwStyle & WS_VSCROLL)) ||
451         ((nBar == SB_HORZ) && !(wndPtr->dwStyle & WS_HSCROLL))) return;
452
453     vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
454                                         &arrowSize, &thumbPos );
455       /* Draw the arrows */
456
457     if (arrowSize) SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize,
458                                       vertical, FALSE, FALSE );
459     
460     SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbPos,
461                          infoPtr->flags, vertical, FALSE, FALSE );
462 }
463
464
465 /***********************************************************************
466  *           SCROLL_RefreshScrollBar
467  *
468  * Repaint the scroll bar interior after a SetScrollRange() or
469  * SetScrollPos() call.
470  */
471 static void SCROLL_RefreshScrollBar( HWND hwnd, int nBar )
472 {
473     WORD arrowSize, thumbPos;
474     RECT rect;
475     BOOL vertical;
476     HDC hdc;
477     WND *wndPtr = WIN_FindWndPtr( hwnd );
478     SCROLLINFO *infoPtr = SCROLL_GetScrollInfo( hwnd, nBar );
479
480     if (!wndPtr || !infoPtr ||
481         ((nBar == SB_VERT) && !(wndPtr->dwStyle & WS_VSCROLL)) ||
482         ((nBar == SB_HORZ) && !(wndPtr->dwStyle & WS_HSCROLL))) return;
483
484     vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
485                                         &arrowSize, &thumbPos );
486     hdc = (nBar == SB_CTL) ? GetDC(hwnd) : GetWindowDC(hwnd);
487     if (!hdc) return;
488     SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbPos,
489                          infoPtr->flags, vertical, FALSE, FALSE );
490     ReleaseDC( hwnd, hdc );
491 }
492
493
494 /***********************************************************************
495  *           SCROLL_HandleKbdEvent
496  *
497  * Handle a keyboard event (only for SB_CTL scrollbars).
498  */
499 static void SCROLL_HandleKbdEvent( HWND hwnd, WORD wParam )
500 {
501     WND *wndPtr = WIN_FindWndPtr( hwnd );
502     WORD msg;
503     
504     switch(wParam)
505     {
506     case VK_PRIOR: msg = SB_PAGEUP; break;
507     case VK_NEXT:  msg = SB_PAGEDOWN; break;
508     case VK_HOME:  msg = SB_TOP; break;
509     case VK_END:   msg = SB_BOTTOM; break;
510     case VK_UP:    msg = SB_LINEUP; break;
511     case VK_DOWN:  msg = SB_LINEDOWN; break;
512     default:
513         return;
514     }
515 #ifdef WINELIB32
516     SendMessage( GetParent(hwnd),
517                  (wndPtr->dwStyle & SBS_VERT) ? WM_VSCROLL : WM_HSCROLL,
518                  (WPARAM)msg, (LPARAM)hwnd );
519 #else
520     SendMessage( GetParent(hwnd),
521                  (wndPtr->dwStyle & SBS_VERT) ? WM_VSCROLL : WM_HSCROLL,
522                  msg, MAKELONG( 0, hwnd ));
523 #endif
524 }
525
526
527 /***********************************************************************
528  *           SCROLL_HandleScrollEvent
529  *
530  * Handle a mouse or timer event for the scrollbar.
531  * 'pt' is the location of the mouse event in client (for SB_CTL) or
532  * windows coordinates.
533  */
534 void SCROLL_HandleScrollEvent( HWND hwnd, int nBar, WORD msg, POINT pt )
535 {
536       /* Previous mouse position for timer events */
537     static POINT prevPt;
538       /* Hit test code of the last button-down event */
539     static enum SCROLL_HITTEST trackHitTest;
540       /* Thumb position when tracking started. */
541     static UINT trackThumbPos;
542       /* Position in the scroll-bar of the last button-down event. */
543     static int lastClickPos;
544       /* Position in the scroll-bar of the last mouse event. */
545     static int lastMousePos;
546
547     enum SCROLL_HITTEST hittest;
548     HWND hwndOwner, hwndCtl;
549     BOOL vertical;
550     WORD arrowSize, thumbPos;
551     RECT rect;
552     HDC hdc;
553
554     SCROLLINFO *infoPtr = SCROLL_GetScrollInfo( hwnd, nBar );
555     if (!infoPtr) return;
556     if ((trackHitTest == SCROLL_NOWHERE) && (msg != WM_LBUTTONDOWN)) return;
557
558     hdc = (nBar == SB_CTL) ? GetDC(hwnd) : GetWindowDC(hwnd);
559     vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
560                                         &arrowSize, &thumbPos );
561     hwndOwner = (nBar == SB_CTL) ? GetParent(hwnd) : hwnd;
562     hwndCtl   = (nBar == SB_CTL) ? hwnd : 0;
563
564     switch(msg)
565     {
566       case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
567           trackHitTest  = hittest = SCROLL_HitTest( hwnd, nBar, pt );
568           lastClickPos  = vertical ? (pt.y - rect.top) : (pt.x - rect.left);
569           lastMousePos  = lastClickPos;
570           trackThumbPos = thumbPos;
571           prevPt = pt;
572           SetCapture( hwnd );
573           if (nBar == SB_CTL) SetFocus( hwnd );
574           break;
575
576       case WM_MOUSEMOVE:
577           hittest = SCROLL_HitTest( hwnd, nBar, pt );
578           prevPt = pt;
579           break;
580
581       case WM_LBUTTONUP:
582           hittest = SCROLL_NOWHERE;
583           ReleaseCapture();
584           break;
585
586       case WM_SYSTIMER:
587           pt = prevPt;
588           hittest = SCROLL_HitTest( hwnd, nBar, pt );
589           break;
590
591       default:
592           return;  /* Should never happen */
593     }
594
595     dprintf_scroll( stddeb, "ScrollBar Event: hwnd="NPFMT" bar=%d msg=%x pt=%ld,%ld hit=%d\n",
596                     hwnd, nBar, msg, (LONG)pt.x, (LONG)pt.y, hittest );
597
598     switch(trackHitTest)
599     {
600     case SCROLL_NOWHERE:  /* No tracking in progress */
601         break;
602
603     case SCROLL_TOP_ARROW:
604         SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
605                            (hittest == trackHitTest), FALSE );
606         if (hittest == trackHitTest)
607         {
608             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
609             {
610                 SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
611                                 SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
612                                 (FARPROC)0 );
613 #ifdef WINELIB32
614                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
615                              (WPARAM)SB_LINEUP, (LPARAM)hwndCtl );
616 #else
617                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
618                              SB_LINEUP, MAKELONG( 0, hwndCtl ));
619 #endif
620             }
621         }
622         else KillSystemTimer( hwnd, SCROLL_TIMER );
623         break;
624
625     case SCROLL_TOP_RECT:
626         SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbPos,
627                              infoPtr->flags, vertical,
628                              (hittest == trackHitTest), FALSE );
629         if (hittest == trackHitTest)
630         {
631             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
632             {
633                 SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
634                                 SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
635                                 (FARPROC)0 );
636 #ifdef WINELIB32
637                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
638                              (WPARAM)SB_PAGEUP, (LPARAM)hwndCtl );
639 #else
640                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
641                              SB_PAGEUP, MAKELONG( 0, hwndCtl ));
642 #endif
643             }
644         }
645         else KillSystemTimer( hwnd, SCROLL_TIMER );
646         break;
647
648     case SCROLL_THUMB:
649         if (msg == WM_LBUTTONDOWN)
650             SCROLL_DrawMovingThumb( hdc, &rect, vertical, arrowSize,
651                                  trackThumbPos + lastMousePos - lastClickPos );
652         else if (msg == WM_LBUTTONUP)
653             SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbPos,
654                                  infoPtr->flags, vertical, FALSE, FALSE );
655         else  /* WM_MOUSEMOVE */
656         {
657             UINT pos, val;
658
659             if (!PtInRect( &rect, pt )) pos = lastClickPos;
660             else pos = vertical ? (pt.y - rect.top) : (pt.x - rect.left);
661             if (pos != lastMousePos)
662             {
663                 SCROLL_DrawMovingThumb( hdc, &rect, vertical, arrowSize,
664                                  trackThumbPos + lastMousePos - lastClickPos );
665                 SCROLL_DrawMovingThumb( hdc, &rect, vertical, arrowSize,
666                                        trackThumbPos + pos - lastClickPos );
667                 lastMousePos = pos;
668                 val = SCROLL_GetThumbVal( infoPtr, &rect, vertical,
669                                  trackThumbPos + lastMousePos - lastClickPos );
670 #ifdef WINELIB32
671                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
672                              MAKEWPARAM(SB_THUMBTRACK,val), (LPARAM)hwndCtl );
673 #else
674                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
675                              SB_THUMBTRACK, MAKELONG( val, hwndCtl ));
676 #endif
677             }
678         }
679         break;
680         
681     case SCROLL_BOTTOM_RECT:
682         SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbPos,
683                              infoPtr->flags, vertical,
684                              FALSE, (hittest == trackHitTest) );
685         if (hittest == trackHitTest)
686         {
687             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
688             {
689                 SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
690                                 SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
691                                 (FARPROC)0 );
692 #ifdef WINELIB32
693                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
694                              (WPARAM)SB_PAGEDOWN, (LPARAM)hwndCtl );
695 #else
696                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
697                              SB_PAGEDOWN, MAKELONG( 0, hwndCtl ));
698 #endif
699             }
700         }
701         else KillSystemTimer( hwnd, SCROLL_TIMER );
702         break;
703         
704     case SCROLL_BOTTOM_ARROW:
705         SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
706                            FALSE, (hittest == trackHitTest) );
707         if (hittest == trackHitTest)
708         {
709             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
710             {
711                 SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
712                                 SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
713                                 (FARPROC)0 );
714 #ifdef WINELIB32
715                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
716                              (WPARAM)SB_LINEDOWN, (LPARAM)hwndCtl );
717 #else
718                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
719                              SB_LINEDOWN, MAKELONG( 0, hwndCtl ));
720 #endif
721             }
722         }
723         else KillSystemTimer( hwnd, SCROLL_TIMER );
724         break;
725     }
726
727     if (msg == WM_LBUTTONUP)
728     {
729         if (trackHitTest == SCROLL_THUMB)
730         {
731             UINT val = SCROLL_GetThumbVal( infoPtr, &rect, vertical,
732                                  trackThumbPos + lastMousePos - lastClickPos );
733 #ifdef WINELIB32
734             SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
735                          MAKEWPARAM(SB_THUMBPOSITION,val), (LPARAM)hwndCtl );
736 #else
737             SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
738                          SB_THUMBPOSITION, MAKELONG( val, hwndCtl ) );
739 #endif
740         }
741         else
742 #ifdef WINELIB32
743             SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
744                          (WPARAM)SB_ENDSCROLL, (LPARAM)hwndCtl );
745 #else
746             SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
747                          SB_ENDSCROLL, MAKELONG( 0, hwndCtl ) );
748 #endif
749         trackHitTest = SCROLL_NOWHERE;  /* Terminate tracking */
750     }
751
752     ReleaseDC( hwnd, hdc );
753 }
754
755
756 /***********************************************************************
757  *           ScrollBarWndProc
758  */
759 LONG ScrollBarWndProc( HWND hwnd, WORD message, WORD wParam, LONG lParam )
760 {
761     POINT Pt;
762     Pt.x = LOWORD(lParam); Pt.y = HIWORD(lParam);
763     /* ^ Can't use MAKEPOINT macro in WINELIB32 */
764
765     switch(message)
766     {
767     case WM_CREATE:
768         {
769             CREATESTRUCT *lpCreat = (CREATESTRUCT *)PTR_SEG_TO_LIN(lParam);
770             if (lpCreat->style & SBS_SIZEBOX)
771             {
772                 fprintf( stdnimp, "Unimplemented style SBS_SIZEBOX.\n" );
773                 return -1;
774             }
775             
776             if (lpCreat->style & SBS_VERT)
777             {
778                 if (lpCreat->style & SBS_LEFTALIGN)
779                     MoveWindow( hwnd, lpCreat->x, lpCreat->y,
780                                 SYSMETRICS_CXVSCROLL+1, lpCreat->cy, FALSE );
781                 else if (lpCreat->style & SBS_RIGHTALIGN)
782                     MoveWindow( hwnd, 
783                                 lpCreat->x+lpCreat->cx-SYSMETRICS_CXVSCROLL-1,
784                                 lpCreat->y,
785                                 SYSMETRICS_CXVSCROLL + 1, lpCreat->cy, FALSE );
786             }
787             else  /* SBS_HORZ */
788             {
789                 if (lpCreat->style & SBS_TOPALIGN)
790                     MoveWindow( hwnd, lpCreat->x, lpCreat->y,
791                                 lpCreat->cx, SYSMETRICS_CYHSCROLL+1, FALSE );
792                 else if (lpCreat->style & SBS_BOTTOMALIGN)
793                     MoveWindow( hwnd, 
794                                 lpCreat->x,
795                                 lpCreat->y+lpCreat->cy-SYSMETRICS_CYHSCROLL-1,
796                                 lpCreat->cx, SYSMETRICS_CYHSCROLL+1, FALSE );
797             }
798         }
799         if (!hUpArrow) SCROLL_LoadBitmaps();
800         dprintf_scroll( stddeb, "ScrollBar creation, hwnd="NPFMT"\n", hwnd );
801         return 0;
802         
803     case WM_LBUTTONDOWN:
804     case WM_LBUTTONUP:
805     case WM_MOUSEMOVE:
806     case WM_SYSTIMER:
807         SCROLL_HandleScrollEvent( hwnd, SB_CTL, message, Pt );
808         break;
809
810     case WM_KEYDOWN:
811         SCROLL_HandleKbdEvent( hwnd, wParam );
812         break;
813
814     case WM_ERASEBKGND:
815         break;
816
817     case WM_PAINT:
818         {
819             PAINTSTRUCT ps;
820             HDC hdc = BeginPaint( hwnd, &ps );
821             SCROLL_DrawScrollBar( hwnd, hdc, SB_CTL );
822             EndPaint( hwnd, &ps );
823         }
824         break;
825
826     default:
827         return DefWindowProc( hwnd, message, wParam, lParam );
828     }
829     return 0;
830 }
831
832
833 /*************************************************************************
834  *           SetScrollPos   (USER.62)
835  */
836 int SetScrollPos( HWND hwnd, int nBar, int nPos, BOOL bRedraw )
837 {
838     SCROLLINFO *infoPtr;
839     INT oldPos;
840
841     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return 0;
842
843     dprintf_scroll( stddeb,"SetScrollPos min=%d max=%d pos=%d\n", 
844                     infoPtr->MinVal, infoPtr->MaxVal, nPos );
845
846     if (nPos < infoPtr->MinVal) nPos = infoPtr->MinVal;
847     else if (nPos > infoPtr->MaxVal) nPos = infoPtr->MaxVal;
848     oldPos = infoPtr->CurVal;
849     infoPtr->CurVal = nPos;
850     if (bRedraw) SCROLL_RefreshScrollBar( hwnd, nBar );
851     return oldPos;
852 }
853
854
855 /*************************************************************************
856  *           GetScrollPos   (USER.63)
857  */
858 int GetScrollPos( HWND hwnd, int nBar )
859 {
860     SCROLLINFO *infoPtr;
861
862     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return 0;
863     return infoPtr->CurVal;
864 }
865
866
867 /*************************************************************************
868  *           SetScrollRange   (USER.64)
869  */
870 void SetScrollRange(HWND hwnd, int nBar, int MinVal, int MaxVal, BOOL bRedraw)
871 {
872     SCROLLINFO *infoPtr;
873
874     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return;
875
876     dprintf_scroll( stddeb,"SetScrollRange hwnd="NPFMT" bar=%d min=%d max=%d\n",
877                     hwnd, nBar, MinVal, MaxVal );
878
879       /* Invalid range -> range is set to (0,0) */
880     if ((MinVal > MaxVal) || ((long)MaxVal - MinVal > 32767L))
881         MinVal = MaxVal = 0;
882     if (infoPtr->CurVal < MinVal) infoPtr->CurVal = MinVal;
883     else if (infoPtr->CurVal > MaxVal) infoPtr->CurVal = MaxVal;
884     infoPtr->MinVal = MinVal;
885     infoPtr->MaxVal = MaxVal;
886
887       /* Non-client scroll-bar is hidden if min==max */
888     if (nBar != SB_CTL) ShowScrollBar( hwnd, nBar, (MinVal != MaxVal) );
889     if (bRedraw) SCROLL_RefreshScrollBar( hwnd, nBar );
890 }
891
892
893 /*************************************************************************
894  *           GetScrollRange   (USER.65)
895  */
896 void GetScrollRange(HWND hwnd, int nBar, LPINT lpMin, LPINT lpMax)
897 {
898     SCROLLINFO *infoPtr;
899
900     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return;
901     if (lpMin) *lpMin = infoPtr->MinVal;
902     if (lpMax) *lpMax = infoPtr->MaxVal;
903 }
904
905
906 /*************************************************************************
907  *           ShowScrollBar   (USER.267)
908  */
909 void ShowScrollBar( HWND hwnd, WORD wBar, BOOL fShow )
910 {
911     WND *wndPtr = WIN_FindWndPtr( hwnd );
912
913     if (!wndPtr) return;
914     dprintf_scroll( stddeb, "ShowScrollBar: hwnd="NPFMT" bar=%d on=%d\n", hwnd, wBar, fShow );
915
916     switch(wBar)
917     {
918     case SB_CTL:
919         ShowWindow( hwnd, fShow ? SW_SHOW : SW_HIDE );
920         return;
921
922     case SB_HORZ:
923         if (fShow)
924         {
925             if (wndPtr->dwStyle & WS_HSCROLL) return;
926             wndPtr->dwStyle |= WS_HSCROLL;
927         }
928         else  /* hide it */
929         {
930             if (!(wndPtr->dwStyle & WS_HSCROLL)) return;
931             wndPtr->dwStyle &= ~WS_HSCROLL;
932         }
933         break;
934
935     case SB_VERT:
936         if (fShow)
937         {
938             if (wndPtr->dwStyle & WS_VSCROLL) return;
939             wndPtr->dwStyle |= WS_VSCROLL;
940         }
941         else  /* hide it */
942         {
943             if (!(wndPtr->dwStyle & WS_VSCROLL)) return;
944             wndPtr->dwStyle &= ~WS_VSCROLL;
945         }
946         break;
947
948     case SB_BOTH:
949         if (fShow)
950         {
951             if ((wndPtr->dwStyle & WS_HSCROLL)
952                 && (wndPtr->dwStyle & WS_VSCROLL)) return;
953             wndPtr->dwStyle |= WS_HSCROLL | WS_VSCROLL;
954         }
955         else  /* hide it */
956         {
957             if (!(wndPtr->dwStyle & WS_HSCROLL)
958                 && !(wndPtr->dwStyle & WS_HSCROLL)) return;
959             wndPtr->dwStyle &= ~(WS_HSCROLL | WS_VSCROLL);
960         }
961         break;
962
963     default:
964         return;  /* Nothing to do! */
965     }
966     SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE
967                  | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
968 }
969
970
971 /*************************************************************************
972  *           EnableScrollBar   (USER.482)
973  */
974 BOOL EnableScrollBar( HWND hwnd, INT nBar, UINT flags )
975 {
976     SCROLLINFO *infoPtr;
977     HDC hdc;
978
979     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return FALSE;
980     dprintf_scroll( stddeb, "EnableScrollBar: "NPFMT" %d %d\n", hwnd, nBar, flags );
981     flags &= ESB_DISABLE_BOTH;
982     if (infoPtr->flags == flags) return FALSE;
983     infoPtr->flags = flags;
984
985       /* Redraw the whole scroll bar */
986     hdc = (nBar == SB_CTL) ? GetDC(hwnd) : GetWindowDC(hwnd);
987     SCROLL_DrawScrollBar( hwnd, hdc, nBar );
988     ReleaseDC( hwnd, hdc );
989     return TRUE;
990 }