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