Release 960516
[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  /* Thumb-tracking info */
74 static HWND hwndTracking = 0;
75 static int nBarTracking = 0;
76 static UINT uTrackingPos = 0;
77
78 /***********************************************************************
79  *           SCROLL_LoadBitmaps
80  */
81 static void SCROLL_LoadBitmaps(void)
82 {
83     hUpArrow  = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_UPARROW));
84     hDnArrow  = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_DNARROW));
85     hLfArrow  = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_LFARROW));
86     hRgArrow  = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_RGARROW));
87     hUpArrowD = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_UPARROWD));
88     hDnArrowD = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_DNARROWD));
89     hLfArrowD = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_LFARROWD));
90     hRgArrowD = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_RGARROWD));
91     hUpArrowI = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_UPARROWI));
92     hDnArrowI = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_DNARROWI));
93     hLfArrowI = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_LFARROWI));
94     hRgArrowI = LoadBitmap((HINSTANCE)NULL, MAKEINTRESOURCE(OBM_RGARROWI));
95 }
96
97 /***********************************************************************
98  *           SCROLL_GetPtrScrollInfo
99  */
100 static SCROLLINFO *SCROLL_GetPtrScrollInfo( WND* wndPtr, int nBar )
101 {
102     HANDLE handle;
103
104     if (!wndPtr) return NULL;
105     switch(nBar)
106     {
107         case SB_HORZ: handle = wndPtr->hHScroll; break;
108         case SB_VERT: handle = wndPtr->hVScroll; break;
109         case SB_CTL:  return (SCROLLINFO *)wndPtr->wExtra;
110         default:      return NULL;
111     }
112
113     if (!handle)  /* Create the info structure if needed */
114     {
115         if ((handle = USER_HEAP_ALLOC( sizeof(SCROLLINFO) )))
116         {
117             SCROLLINFO *infoPtr = (SCROLLINFO *) USER_HEAP_LIN_ADDR( handle );
118             infoPtr->MinVal = infoPtr->CurVal = 0;
119             infoPtr->MaxVal = 100;
120             infoPtr->flags  = ESB_ENABLE_BOTH;
121             if (nBar == SB_HORZ) wndPtr->hHScroll = handle;
122             else wndPtr->hVScroll = handle;
123         }
124         if (!hUpArrow) SCROLL_LoadBitmaps();
125     }
126     return (SCROLLINFO *) USER_HEAP_LIN_ADDR( handle );
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 #ifdef WINELIB32
368             HBRUSH hbrush = SendMessage( GetParent(hwnd), WM_CTLCOLORSCROLLBAR,
369                                          hdc, hwnd );
370 #else
371             HBRUSH hbrush = SendMessage( GetParent(hwnd), WM_CTLCOLOR, hdc,
372                                          MAKELONG(hwnd, CTLCOLOR_SCROLLBAR) );
373 #endif
374             SelectObject( hdc, hbrush );
375         }
376         else SelectObject( hdc, sysColorObjects.hbrushScrollbar );
377     }
378
379       /* Calculate the scroll rectangle */
380
381     r = *rect;
382     if (vertical)
383     {
384         r.top    += arrowSize;
385         r.bottom -= arrowSize;
386     }
387     else
388     {
389         r.left  += arrowSize;
390         r.right -= arrowSize;
391     }
392
393       /* Draw the scroll bar frame */
394
395     MoveTo( hdc, r.left, r.top );
396     LineTo( hdc, r.right-1, r.top );
397     LineTo( hdc, r.right-1, r.bottom-1 );
398     LineTo( hdc, r.left, r.bottom-1 );
399     LineTo( hdc, r.left, r.top );
400
401       /* Draw the scroll rectangles and thumb */
402
403     if (!thumbPos)  /* No thumb to draw */
404     {
405         PatBlt( hdc, r.left+1, r.top+1, r.right - r.left - 2,
406                 r.bottom - r.top - 2, PATCOPY );
407         return;
408     }
409
410     if (vertical)
411     {
412         PatBlt( hdc, r.left + 1, r.top + 1,
413                 r.right - r.left - 2,
414                 thumbPos - arrowSize,
415                 top_selected ? 0x0f0000 : PATCOPY );
416         r.top += thumbPos - arrowSize;
417         PatBlt( hdc, r.left + 1, r.top + SYSMETRICS_CYHSCROLL + 1,
418                 r.right - r.left - 2,
419                 r.bottom - r.top - SYSMETRICS_CYHSCROLL - 2,
420                 bottom_selected ? 0x0f0000 : PATCOPY );
421         r.bottom = r.top + SYSMETRICS_CYHSCROLL + 1;
422     }
423     else  /* horizontal */
424     {
425         PatBlt( hdc, r.left + 1, r.top + 1,
426                 thumbPos - arrowSize,
427                 r.bottom - r.top - 2,
428                 top_selected ? 0x0f0000 : PATCOPY );
429         r.left += thumbPos - arrowSize;
430         PatBlt( hdc, r.left + SYSMETRICS_CYHSCROLL + 1, r.top + 1,
431                 r.right - r.left - SYSMETRICS_CYHSCROLL - 2,
432                 r.bottom - r.top - 2,
433                 bottom_selected ? 0x0f0000 : PATCOPY );
434         r.right = r.left + SYSMETRICS_CXVSCROLL + 1;
435     }
436
437       /* Draw the thumb */
438
439     SelectObject( hdc, sysColorObjects.hbrushBtnFace );
440     Rectangle( hdc, r.left, r.top, r.right, r.bottom );
441     InflateRect16( &r, -1, -1 );
442     GRAPH_DrawReliefRect( hdc, &r, 1, 2, FALSE );
443     if ((hwndTracking == hwnd) && (nBarTracking == nBar))
444         SCROLL_DrawMovingThumb( hdc, rect, vertical, arrowSize, uTrackingPos);
445 }
446
447
448 /***********************************************************************
449  *           SCROLL_DrawScrollBar
450  *
451  * Redraw the whole scrollbar.
452  */
453 void SCROLL_DrawScrollBar( HWND hwnd, HDC hdc, int nBar )
454 {
455     WORD arrowSize, thumbPos;
456     RECT16 rect;
457     BOOL vertical;
458     WND *wndPtr = WIN_FindWndPtr( hwnd );
459     SCROLLINFO *infoPtr = SCROLL_GetPtrScrollInfo( wndPtr, nBar );
460
461     if (!wndPtr || !infoPtr ||
462         ((nBar == SB_VERT) && !(wndPtr->dwStyle & WS_VSCROLL)) ||
463         ((nBar == SB_HORZ) && !(wndPtr->dwStyle & WS_HSCROLL))) return;
464
465     vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
466                                         &arrowSize, &thumbPos );
467       /* Draw the arrows */
468
469     if (arrowSize) SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize,
470                                       vertical, FALSE, FALSE );
471     
472     SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbPos,
473                          infoPtr->flags, vertical, FALSE, FALSE );
474 }
475
476
477 /***********************************************************************
478  *           SCROLL_RefreshScrollBar
479  *
480  * Repaint the scroll bar interior after a SetScrollRange() or
481  * SetScrollPos() call.
482  */
483 static void SCROLL_RefreshScrollBar( HWND hwnd, int nBar )
484 {
485     WORD arrowSize, thumbPos;
486     RECT16 rect;
487     BOOL vertical;
488     HDC hdc;
489     WND *wndPtr = WIN_FindWndPtr( hwnd );
490     SCROLLINFO *infoPtr = SCROLL_GetPtrScrollInfo( wndPtr, nBar );
491
492     if (!wndPtr || !infoPtr ||
493         ((nBar == SB_VERT) && !(wndPtr->dwStyle & WS_VSCROLL)) ||
494         ((nBar == SB_HORZ) && !(wndPtr->dwStyle & WS_HSCROLL))) return;
495
496     vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
497                                         &arrowSize, &thumbPos );
498     hdc = (nBar == SB_CTL) ? GetDC(hwnd) : GetWindowDC(hwnd);
499     if (!hdc) return;
500     SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbPos,
501                          infoPtr->flags, vertical, FALSE, FALSE );
502     ReleaseDC( hwnd, hdc );
503 }
504
505
506 /***********************************************************************
507  *           SCROLL_HandleKbdEvent
508  *
509  * Handle a keyboard event (only for SB_CTL scrollbars).
510  */
511 static void SCROLL_HandleKbdEvent( HWND hwnd, WORD wParam )
512 {
513     WND *wndPtr = WIN_FindWndPtr( hwnd );
514     WPARAM msg;
515     
516     switch(wParam)
517     {
518     case VK_PRIOR: msg = SB_PAGEUP; break;
519     case VK_NEXT:  msg = SB_PAGEDOWN; break;
520     case VK_HOME:  msg = SB_TOP; break;
521     case VK_END:   msg = SB_BOTTOM; break;
522     case VK_UP:    msg = SB_LINEUP; break;
523     case VK_DOWN:  msg = SB_LINEDOWN; break;
524     default:
525         return;
526     }
527 #ifdef WINELIB32
528     SendMessage( GetParent(hwnd),
529                  (wndPtr->dwStyle & SBS_VERT) ? WM_VSCROLL : WM_HSCROLL,
530                  msg, hwnd );
531 #else
532     SendMessage( GetParent(hwnd),
533                  (wndPtr->dwStyle & SBS_VERT) ? WM_VSCROLL : WM_HSCROLL,
534                  msg, MAKELONG( 0, hwnd ));
535 #endif
536 }
537
538
539 /***********************************************************************
540  *           SCROLL_HandleScrollEvent
541  *
542  * Handle a mouse or timer event for the scrollbar.
543  * 'pt' is the location of the mouse event in client (for SB_CTL) or
544  * windows coordinates.
545  */
546 void SCROLL_HandleScrollEvent( HWND hwnd, int nBar, WORD msg, POINT16 pt )
547 {
548       /* Previous mouse position for timer events */
549     static POINT16 prevPt;
550       /* Hit test code of the last button-down event */
551     static enum SCROLL_HITTEST trackHitTest;
552       /* Thumb position when tracking started. */
553     static UINT trackThumbPos;
554       /* Position in the scroll-bar of the last button-down event. */
555     static int lastClickPos;
556       /* Position in the scroll-bar of the last mouse event. */
557     static int lastMousePos;
558
559     enum SCROLL_HITTEST hittest;
560     HWND hwndOwner, hwndCtl;
561     BOOL vertical;
562     WORD arrowSize, thumbPos;
563     RECT16 rect;
564     HDC hdc;
565
566     SCROLLINFO *infoPtr = SCROLL_GetScrollInfo( hwnd, nBar );
567     if (!infoPtr) return;
568     if ((trackHitTest == SCROLL_NOWHERE) && (msg != WM_LBUTTONDOWN)) return;
569
570     hdc = (nBar == SB_CTL) ? GetDC(hwnd) : GetWindowDC(hwnd);
571     vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
572                                         &arrowSize, &thumbPos );
573     hwndOwner = (nBar == SB_CTL) ? GetParent(hwnd) : hwnd;
574     hwndCtl   = (nBar == SB_CTL) ? hwnd : 0;
575
576     switch(msg)
577     {
578       case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
579           trackHitTest  = hittest = SCROLL_HitTest( hwnd, nBar, pt );
580           lastClickPos  = vertical ? (pt.y - rect.top) : (pt.x - rect.left);
581           lastMousePos  = lastClickPos;
582           trackThumbPos = thumbPos;
583           prevPt = pt;
584           SetCapture( hwnd );
585           if (nBar == SB_CTL) SetFocus( hwnd );
586           break;
587
588       case WM_MOUSEMOVE:
589           hittest = SCROLL_HitTest( hwnd, nBar, pt );
590           prevPt = pt;
591           break;
592
593       case WM_LBUTTONUP:
594           hittest = SCROLL_NOWHERE;
595           ReleaseCapture();
596           break;
597
598       case WM_SYSTIMER:
599           pt = prevPt;
600           hittest = SCROLL_HitTest( hwnd, nBar, pt );
601           break;
602
603       default:
604           return;  /* Should never happen */
605     }
606
607     dprintf_scroll( stddeb, "ScrollBar Event: hwnd=%04x bar=%d msg=%x pt=%d,%d hit=%d\n",
608                     hwnd, nBar, msg, pt.x, pt.y, hittest );
609
610     switch(trackHitTest)
611     {
612     case SCROLL_NOWHERE:  /* No tracking in progress */
613         break;
614
615     case SCROLL_TOP_ARROW:
616         SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
617                            (hittest == trackHitTest), FALSE );
618         if (hittest == trackHitTest)
619         {
620             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
621             {
622 #ifdef WINELIB32
623                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
624                              SB_LINEUP, hwndCtl );
625 #else
626                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
627                              SB_LINEUP, MAKELONG( 0, hwndCtl ));
628 #endif
629                 SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
630                                 SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
631                                 (FARPROC)0 );
632             }
633         }
634         else KillSystemTimer( hwnd, SCROLL_TIMER );
635         break;
636
637     case SCROLL_TOP_RECT:
638         SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbPos,
639                              infoPtr->flags, vertical,
640                              (hittest == trackHitTest), FALSE );
641         if (hittest == trackHitTest)
642         {
643             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
644             {
645 #ifdef WINELIB32
646                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
647                              SB_PAGEUP, hwndCtl );
648 #else
649                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
650                              SB_PAGEUP, MAKELONG( 0, hwndCtl ));
651 #endif
652                 SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
653                                 SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
654                                 (FARPROC)0 );
655             }
656         }
657         else KillSystemTimer( hwnd, SCROLL_TIMER );
658         break;
659
660     case SCROLL_THUMB:
661         if (msg == WM_LBUTTONDOWN)
662         {
663             SCROLL_DrawMovingThumb( hdc, &rect, vertical, arrowSize,
664                                  trackThumbPos + lastMousePos - lastClickPos );
665             hwndTracking = hwnd;
666             nBarTracking = nBar;
667         }
668         else if (msg == WM_LBUTTONUP)
669         {
670             hwndTracking = 0;
671             SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbPos,
672                                  infoPtr->flags, vertical, FALSE, FALSE );
673         }
674         else  /* WM_MOUSEMOVE */
675         {
676             UINT pos, val;
677
678             if (!PtInRect16( &rect, pt )) pos = lastClickPos;
679             else pos = vertical ? (pt.y - rect.top) : (pt.x - rect.left);
680             if (pos != lastMousePos)
681             {
682                 SCROLL_DrawMovingThumb( hdc, &rect, vertical, arrowSize,
683                                  trackThumbPos + lastMousePos - lastClickPos );
684                 lastMousePos = pos;
685                 val = SCROLL_GetThumbVal( infoPtr, &rect, vertical,
686                                  trackThumbPos + lastMousePos - lastClickPos );
687                 /* Save tracking info */
688                 uTrackingPos = trackThumbPos + pos - lastClickPos;
689 #ifdef WINELIB32
690                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
691                              MAKEWPARAM(SB_THUMBTRACK,val), hwndCtl );
692 #else
693                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
694                              SB_THUMBTRACK, MAKELONG( val, hwndCtl ));
695 #endif
696                 SCROLL_DrawMovingThumb( hdc, &rect, vertical,
697                                         arrowSize, uTrackingPos );
698             }
699         }
700         break;
701         
702     case SCROLL_BOTTOM_RECT:
703         SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbPos,
704                              infoPtr->flags, vertical,
705                              FALSE, (hittest == trackHitTest) );
706         if (hittest == trackHitTest)
707         {
708             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
709             {
710 #ifdef WINELIB32
711                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
712                              SB_PAGEDOWN, hwndCtl );
713 #else
714                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
715                              SB_PAGEDOWN, MAKELONG( 0, hwndCtl ));
716 #endif
717                 SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
718                                 SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
719                                 (FARPROC)0 );
720             }
721         }
722         else KillSystemTimer( hwnd, SCROLL_TIMER );
723         break;
724         
725     case SCROLL_BOTTOM_ARROW:
726         SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
727                            FALSE, (hittest == trackHitTest) );
728         if (hittest == trackHitTest)
729         {
730             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
731             {
732 #ifdef WINELIB32
733                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
734                              SB_LINEDOWN, hwndCtl );
735 #else
736                 SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
737                              SB_LINEDOWN, MAKELONG( 0, hwndCtl ));
738 #endif
739                 SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
740                                 SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
741                                 (FARPROC)0 );
742             }
743         }
744         else KillSystemTimer( hwnd, SCROLL_TIMER );
745         break;
746     }
747
748     if (msg == WM_LBUTTONUP)
749     {
750         if (trackHitTest == SCROLL_THUMB)
751         {
752             UINT val = SCROLL_GetThumbVal( infoPtr, &rect, vertical,
753                                  trackThumbPos + lastMousePos - lastClickPos );
754 #ifdef WINELIB32
755             SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
756                          MAKEWPARAM(SB_THUMBPOSITION,val), hwndCtl );
757 #else
758             SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
759                          SB_THUMBPOSITION, MAKELONG( val, hwndCtl ) );
760 #endif
761         }
762         else
763 #ifdef WINELIB32
764             SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
765                          SB_ENDSCROLL, hwndCtl );
766 #else
767             SendMessage( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
768                          SB_ENDSCROLL, MAKELONG( 0, hwndCtl ) );
769 #endif
770         trackHitTest = SCROLL_NOWHERE;  /* Terminate tracking */
771     }
772
773     ReleaseDC( hwnd, hdc );
774 }
775
776
777 /***********************************************************************
778  *           ScrollBarWndProc
779  */
780 LONG ScrollBarWndProc( HWND hwnd, WORD message, WORD wParam, LONG lParam )
781 {
782     switch(message)
783     {
784     case WM_CREATE:
785         {
786             CREATESTRUCT16 *lpCreat = (CREATESTRUCT16 *)PTR_SEG_TO_LIN(lParam);
787             if (lpCreat->style & SBS_SIZEBOX)
788             {
789                 fprintf( stdnimp, "Unimplemented style SBS_SIZEBOX.\n" );
790                 return 0;  /* FIXME */
791             }
792             
793             if (lpCreat->style & SBS_VERT)
794             {
795                 if (lpCreat->style & SBS_LEFTALIGN)
796                     MoveWindow( hwnd, lpCreat->x, lpCreat->y,
797                                 SYSMETRICS_CXVSCROLL+1, lpCreat->cy, FALSE );
798                 else if (lpCreat->style & SBS_RIGHTALIGN)
799                     MoveWindow( hwnd, 
800                                 lpCreat->x+lpCreat->cx-SYSMETRICS_CXVSCROLL-1,
801                                 lpCreat->y,
802                                 SYSMETRICS_CXVSCROLL + 1, lpCreat->cy, FALSE );
803             }
804             else  /* SBS_HORZ */
805             {
806                 if (lpCreat->style & SBS_TOPALIGN)
807                     MoveWindow( hwnd, lpCreat->x, lpCreat->y,
808                                 lpCreat->cx, SYSMETRICS_CYHSCROLL+1, FALSE );
809                 else if (lpCreat->style & SBS_BOTTOMALIGN)
810                     MoveWindow( hwnd, 
811                                 lpCreat->x,
812                                 lpCreat->y+lpCreat->cy-SYSMETRICS_CYHSCROLL-1,
813                                 lpCreat->cx, SYSMETRICS_CYHSCROLL+1, FALSE );
814             }
815         }
816         if (!hUpArrow) SCROLL_LoadBitmaps();
817         dprintf_scroll( stddeb, "ScrollBar creation, hwnd=%04x\n", hwnd );
818         return 0;
819         
820     case WM_LBUTTONDOWN:
821     case WM_LBUTTONUP:
822     case WM_MOUSEMOVE:
823     case WM_SYSTIMER:
824         SCROLL_HandleScrollEvent( hwnd, SB_CTL, message, MAKEPOINT16(lParam) );
825         break;
826
827     case WM_KEYDOWN:
828         SCROLL_HandleKbdEvent( hwnd, wParam );
829         break;
830
831     case WM_ERASEBKGND:
832          return 1;
833
834     case WM_GETDLGCODE:
835          return DLGC_WANTARROWS; /* Windows returns this value */
836
837     case WM_PAINT:
838         {
839             PAINTSTRUCT16 ps;
840             HDC16 hdc = BeginPaint16( hwnd, &ps );
841             SCROLL_DrawScrollBar( hwnd, hdc, SB_CTL );
842             EndPaint16( hwnd, &ps );
843         }
844         break;
845
846     case 0x400: /* SB_SETSCROLLPOS */
847     case 0x401: /* SB_GETSCROLLPOS */
848     case 0x402: /* SB_GETSCROLLRANGE */
849     case 0x403: /* SB_ENABLE */
850     case 0x404: /* SB_REDRAW */
851         fprintf(stdnimp,"ScrollBarWndProc: undocumented message %04x, please report\n", message );
852
853     default:
854         return DefWindowProc( hwnd, message, wParam, lParam );
855     }
856     return 0;
857 }
858
859
860 /*************************************************************************
861  *           SetScrollPos   (USER.62)
862  */
863 int SetScrollPos( HWND hwnd, int nBar, int nPos, BOOL bRedraw )
864 {
865     SCROLLINFO *infoPtr;
866     INT oldPos;
867
868     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return 0;
869
870     dprintf_scroll( stddeb,"SetScrollPos min=%d max=%d pos=%d\n", 
871                     infoPtr->MinVal, infoPtr->MaxVal, nPos );
872
873     if (nPos < infoPtr->MinVal) nPos = infoPtr->MinVal;
874     else if (nPos > infoPtr->MaxVal) nPos = infoPtr->MaxVal;
875     oldPos = infoPtr->CurVal;
876     infoPtr->CurVal = nPos;
877     if (bRedraw) SCROLL_RefreshScrollBar( hwnd, nBar );
878     return oldPos;
879 }
880
881
882 /*************************************************************************
883  *           GetScrollPos   (USER.63)
884  */
885 int GetScrollPos( HWND hwnd, int nBar )
886 {
887     SCROLLINFO *infoPtr;
888
889     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return 0;
890     return infoPtr->CurVal;
891 }
892
893
894 /*************************************************************************
895  *           SetScrollRange   (USER.64)
896  */
897 void SetScrollRange(HWND hwnd, int nBar, int MinVal, int MaxVal, BOOL bRedraw)
898 {
899     SCROLLINFO *infoPtr;
900
901     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return;
902
903     dprintf_scroll( stddeb,"SetScrollRange hwnd=%04x bar=%d min=%d max=%d\n",
904                     hwnd, nBar, MinVal, MaxVal );
905
906       /* Invalid range -> range is set to (0,0) */
907     if ((MinVal > MaxVal) || ((long)MaxVal - MinVal > 32767L))
908         MinVal = MaxVal = 0;
909     if (infoPtr->CurVal < MinVal) infoPtr->CurVal = MinVal;
910     else if (infoPtr->CurVal > MaxVal) infoPtr->CurVal = MaxVal;
911     infoPtr->MinVal = MinVal;
912     infoPtr->MaxVal = MaxVal;
913
914       /* Non-client scroll-bar is hidden if min==max */
915     if (nBar != SB_CTL) ShowScrollBar( hwnd, nBar, (MinVal != MaxVal) );
916     if (bRedraw) SCROLL_RefreshScrollBar( hwnd, nBar );
917 }
918
919 /*************************************************************************
920  *           SCROLL_SetNCSbState
921  *
922  * This is for CalcChildScroll in windows/mdi.c
923  */
924 DWORD SCROLL_SetNCSbState(WND* wndPtr, int vMin, int vMax, int vPos,
925                                        int hMin, int hMax, int hPos)
926 {
927   SCROLLINFO  *infoPtr = SCROLL_GetPtrScrollInfo(wndPtr, SB_VERT);
928  
929   wndPtr->dwStyle |= (WS_VSCROLL | WS_HSCROLL);
930
931   if( vMin >= vMax ) 
932     { vMin = vMax;
933       wndPtr->dwStyle &= ~WS_VSCROLL; }
934   if( vPos > vMax ) vPos = vMax; else if( vPos < vMin ) vPos = vMin;
935   infoPtr->MinVal = vMin;
936   infoPtr->MaxVal = vMax;
937   infoPtr->CurVal = vPos;
938
939   infoPtr = SCROLL_GetPtrScrollInfo(wndPtr, SB_HORZ);
940
941   if( hMin >= hMax )
942     { hMin = hMax;
943       wndPtr->dwStyle &= ~WS_HSCROLL; }
944   if( hPos > hMax ) hPos = hMax; else if( hPos < hMin ) hPos = hMin;
945   infoPtr->MinVal = hMin;
946   infoPtr->MaxVal = hMax;
947   infoPtr->CurVal = hPos;
948
949   return wndPtr->dwStyle & (WS_VSCROLL | WS_HSCROLL);
950 }
951
952 /*************************************************************************
953  *           GetScrollRange   (USER.65)
954  */
955 void GetScrollRange(HWND hwnd, int nBar, LPINT16 lpMin, LPINT16 lpMax)
956 {
957     SCROLLINFO *infoPtr;
958
959     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return;
960     if (lpMin) *lpMin = infoPtr->MinVal;
961     if (lpMax) *lpMax = infoPtr->MaxVal;
962 }
963
964
965 /*************************************************************************
966  *           ShowScrollBar   (USER.267)
967  */
968 void ShowScrollBar( HWND hwnd, WORD wBar, BOOL fShow )
969 {
970     WND *wndPtr = WIN_FindWndPtr( hwnd );
971
972     if (!wndPtr) return;
973     dprintf_scroll( stddeb, "ShowScrollBar: hwnd=%04x bar=%d on=%d\n", hwnd, wBar, fShow );
974
975     switch(wBar)
976     {
977     case SB_CTL:
978         ShowWindow( hwnd, fShow ? SW_SHOW : SW_HIDE );
979         return;
980
981     case SB_HORZ:
982         if (fShow)
983         {
984             if (wndPtr->dwStyle & WS_HSCROLL) return;
985             wndPtr->dwStyle |= WS_HSCROLL;
986         }
987         else  /* hide it */
988         {
989             if (!(wndPtr->dwStyle & WS_HSCROLL)) return;
990             wndPtr->dwStyle &= ~WS_HSCROLL;
991         }
992         break;
993
994     case SB_VERT:
995         if (fShow)
996         {
997             if (wndPtr->dwStyle & WS_VSCROLL) return;
998             wndPtr->dwStyle |= WS_VSCROLL;
999         }
1000         else  /* hide it */
1001         {
1002             if (!(wndPtr->dwStyle & WS_VSCROLL)) return;
1003             wndPtr->dwStyle &= ~WS_VSCROLL;
1004         }
1005         break;
1006
1007     case SB_BOTH:
1008         if (fShow)
1009         {
1010             if ((wndPtr->dwStyle & WS_HSCROLL)
1011                 && (wndPtr->dwStyle & WS_VSCROLL)) return;
1012             wndPtr->dwStyle |= WS_HSCROLL | WS_VSCROLL;
1013         }
1014         else  /* hide it */
1015         {
1016             if (!(wndPtr->dwStyle & WS_HSCROLL)
1017                 && !(wndPtr->dwStyle & WS_HSCROLL)) return;
1018             wndPtr->dwStyle &= ~(WS_HSCROLL | WS_VSCROLL);
1019         }
1020         break;
1021
1022     default:
1023         return;  /* Nothing to do! */
1024     }
1025     SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE
1026                  | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
1027 }
1028
1029
1030 /*************************************************************************
1031  *           EnableScrollBar   (USER.482)
1032  */
1033 BOOL EnableScrollBar( HWND hwnd, UINT nBar, UINT flags )
1034 {
1035     SCROLLINFO *infoPtr;
1036     HDC hdc;
1037
1038     if (!(infoPtr = SCROLL_GetScrollInfo( hwnd, nBar ))) return FALSE;
1039     dprintf_scroll( stddeb, "EnableScrollBar: %04x %d %d\n", hwnd, nBar, flags );
1040     flags &= ESB_DISABLE_BOTH;
1041     if (infoPtr->flags == flags) return FALSE;
1042     infoPtr->flags = flags;
1043
1044       /* Redraw the whole scroll bar */
1045     hdc = (nBar == SB_CTL) ? GetDC(hwnd) : GetWindowDC(hwnd);
1046     SCROLL_DrawScrollBar( hwnd, hdc, nBar );
1047     ReleaseDC( hwnd, hdc );
1048     return TRUE;
1049 }