Authors: Mike McCormack <mike@codeweavers.com>, Aric Stewart <aric@codeweavers.com...
[wine] / dlls / user / scroll.c
1 /*
2  * Scrollbar control
3  *
4  * Copyright 1993 Martin Ayotte
5  * Copyright 1994, 1996 Alexandre Julliard
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * NOTES
22  *
23  * This code was audited for completeness against the documented features
24  * of Comctl32.dll version 6.0 on Oct. 8, 2004, by Dimitrie O. Paun.
25  * 
26  * Unless otherwise noted, we believe this code to be complete, as per
27  * the specification mentioned above.
28  * If you discover missing features, or bugs, please note them below.
29  *
30  * TODO:
31  *   - GetScrollBarInfo
32  *   - SBM_GETSCROLLBARINFO
33  *   - SCROLLBARINFO
34  */
35
36 #include <stdarg.h>
37
38 #include "windef.h"
39 #include "winbase.h"
40 #include "wingdi.h"
41 #include "wine/winuser16.h"
42 #include "controls.h"
43 #include "win.h"
44 #include "wine/debug.h"
45 #include "user_private.h"
46 #include "message.h"
47
48 WINE_DEFAULT_DEBUG_CHANNEL(scroll);
49
50 typedef struct
51 {
52     INT   curVal;   /* Current scroll-bar value */
53     INT   minVal;   /* Minimum scroll-bar value */
54     INT   maxVal;   /* Maximum scroll-bar value */
55     INT   page;     /* Page size of scroll bar (Win32) */
56     UINT  flags;    /* EnableScrollBar flags */
57 } SCROLLBAR_INFO, *LPSCROLLBAR_INFO;
58
59
60   /* Minimum size of the rectangle between the arrows */
61 #define SCROLL_MIN_RECT  4
62
63   /* Minimum size of the thumb in pixels */
64 #define SCROLL_MIN_THUMB 6
65
66   /* Overlap between arrows and thumb */
67 #define SCROLL_ARROW_THUMB_OVERLAP 0
68
69   /* Delay (in ms) before first repetition when holding the button down */
70 #define SCROLL_FIRST_DELAY   200
71
72   /* Delay (in ms) between scroll repetitions */
73 #define SCROLL_REPEAT_DELAY  50
74
75   /* Scroll timer id */
76 #define SCROLL_TIMER   0
77
78   /* Scroll-bar hit testing */
79 enum SCROLL_HITTEST
80 {
81     SCROLL_NOWHERE,      /* Outside the scroll bar */
82     SCROLL_TOP_ARROW,    /* Top or left arrow */
83     SCROLL_TOP_RECT,     /* Rectangle between the top arrow and the thumb */
84     SCROLL_THUMB,        /* Thumb rectangle */
85     SCROLL_BOTTOM_RECT,  /* Rectangle between the thumb and the bottom arrow */
86     SCROLL_BOTTOM_ARROW  /* Bottom or right arrow */
87 };
88
89  /* What to do after SCROLL_SetScrollInfo() */
90 #define SA_SSI_HIDE             0x0001
91 #define SA_SSI_SHOW             0x0002
92 #define SA_SSI_REFRESH          0x0004
93 #define SA_SSI_REPAINT_ARROWS   0x0008
94
95  /* Thumb-tracking info */
96 static HWND SCROLL_TrackingWin = 0;
97 static INT  SCROLL_TrackingBar = 0;
98 static INT  SCROLL_TrackingPos = 0;
99 static INT  SCROLL_TrackingVal = 0;
100  /* Hit test code of the last button-down event */
101 static enum SCROLL_HITTEST SCROLL_trackHitTest;
102 static BOOL SCROLL_trackVertical;
103
104  /* Is the moving thumb being displayed? */
105 static BOOL SCROLL_MovingThumb = FALSE;
106
107  /* Local functions */
108 static BOOL SCROLL_ShowScrollBar( HWND hwnd, INT nBar,
109                                     BOOL fShowH, BOOL fShowV );
110 static INT SCROLL_SetScrollInfo( HWND hwnd, INT nBar,
111                                  const SCROLLINFO *info, BOOL bRedraw );
112 static void SCROLL_DrawInterior_9x( HWND hwnd, HDC hdc, INT nBar,
113                                     RECT *rect, INT arrowSize,
114                                     INT thumbSize, INT thumbPos,
115                                     UINT flags, BOOL vertical,
116                                     BOOL top_selected, BOOL bottom_selected );
117 static LRESULT WINAPI ScrollBarWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
118
119
120 /*********************************************************************
121  * scrollbar class descriptor
122  */
123 const struct builtin_class_descr SCROLL_builtin_class =
124 {
125     "ScrollBar",            /* name */
126     CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC, /* style  */
127     NULL,                   /* procA (winproc is Unicode only) */
128     ScrollBarWndProc,       /* procW */
129     sizeof(SCROLLBAR_INFO), /* extra */
130     IDC_ARROW,              /* cursor */
131     0                       /* brush */
132 };
133     
134 /***********************************************************************
135  *           SCROLL_ScrollInfoValid
136  *
137  *  Determine if the supplied SCROLLINFO struct is valid.
138  */
139 inline static BOOL SCROLL_ScrollInfoValid(
140 LPSCROLLINFO info /* [in] The SCROLLINFO struct to be tested */)
141 {
142     return !(info->fMask & ~(SIF_ALL | SIF_DISABLENOSCROLL)
143         || (info->cbSize != sizeof(*info)
144             && info->cbSize != sizeof(*info) - sizeof(info->nTrackPos)));
145 }
146
147
148 /***********************************************************************
149  *           SCROLL_GetScrollBarInfo
150  */
151 static SCROLLBAR_INFO *SCROLL_GetScrollBarInfo( HWND hwnd, INT nBar )
152 {
153     SCROLLBAR_INFO *infoPtr;
154     WND *wndPtr = WIN_FindWndPtr( hwnd );
155
156     if (!wndPtr) return NULL;
157     switch(nBar)
158     {
159         case SB_HORZ: infoPtr = (SCROLLBAR_INFO *)wndPtr->pHScroll; break;
160         case SB_VERT: infoPtr = (SCROLLBAR_INFO *)wndPtr->pVScroll; break;
161         case SB_CTL:  infoPtr = (SCROLLBAR_INFO *)wndPtr->wExtra; break;
162         default:
163             WIN_ReleaseWndPtr( wndPtr );
164             return NULL;
165     }
166
167     if (!infoPtr)  /* Create the info structure if needed */
168     {
169         if ((infoPtr = HeapAlloc( GetProcessHeap(), 0, sizeof(SCROLLBAR_INFO) )))
170         {
171             infoPtr->minVal = infoPtr->curVal = infoPtr->page = 0;
172             infoPtr->maxVal = 100;
173             infoPtr->flags  = ESB_ENABLE_BOTH;
174             if (nBar == SB_HORZ) wndPtr->pHScroll = infoPtr;
175             else wndPtr->pVScroll = infoPtr;
176         }
177     }
178     WIN_ReleaseWndPtr( wndPtr );
179     return infoPtr;
180 }
181
182
183 /***********************************************************************
184  *           SCROLL_GetScrollBarRect
185  *
186  * Compute the scroll bar rectangle, in drawing coordinates (i.e. client
187  * coords for SB_CTL, window coords for SB_VERT and SB_HORZ).
188  * 'arrowSize' returns the width or height of an arrow (depending on
189  * the orientation of the scrollbar), 'thumbSize' returns the size of
190  * the thumb, and 'thumbPos' returns the position of the thumb
191  * relative to the left or to the top.
192  * Return TRUE if the scrollbar is vertical, FALSE if horizontal.
193  */
194 static BOOL SCROLL_GetScrollBarRect( HWND hwnd, INT nBar, RECT *lprect,
195                                      INT *arrowSize, INT *thumbSize,
196                                      INT *thumbPos )
197 {
198     INT pixels;
199     BOOL vertical;
200     WND *wndPtr = WIN_FindWndPtr( hwnd );
201
202     switch(nBar)
203     {
204       case SB_HORZ:
205         lprect->left   = wndPtr->rectClient.left - wndPtr->rectWindow.left;
206         lprect->top    = wndPtr->rectClient.bottom - wndPtr->rectWindow.top;
207         lprect->right  = wndPtr->rectClient.right - wndPtr->rectWindow.left;
208         lprect->bottom = lprect->top + GetSystemMetrics(SM_CYHSCROLL);
209         if(wndPtr->dwStyle & WS_BORDER) {
210           lprect->left--;
211           lprect->right++;
212         } else if(wndPtr->dwStyle & WS_VSCROLL)
213           lprect->right++;
214         vertical = FALSE;
215         break;
216
217       case SB_VERT:
218         if((wndPtr->dwExStyle & WS_EX_LEFTSCROLLBAR) != 0)
219             lprect->left   = wndPtr->rectClient.left - wndPtr->rectWindow.left - GetSystemMetrics(SM_CXVSCROLL);
220         else
221             lprect->left   = wndPtr->rectClient.right - wndPtr->rectWindow.left;
222         lprect->top    = wndPtr->rectClient.top - wndPtr->rectWindow.top;
223         lprect->right  = lprect->left + GetSystemMetrics(SM_CXVSCROLL);
224         lprect->bottom = wndPtr->rectClient.bottom - wndPtr->rectWindow.top;
225         if(wndPtr->dwStyle & WS_BORDER) {
226           lprect->top--;
227           lprect->bottom++;
228         } else if(wndPtr->dwStyle & WS_HSCROLL)
229           lprect->bottom++;
230         vertical = TRUE;
231         break;
232
233       case SB_CTL:
234         GetClientRect( hwnd, lprect );
235         vertical = ((wndPtr->dwStyle & SBS_VERT) != 0);
236         break;
237
238     default:
239         WIN_ReleaseWndPtr(wndPtr);
240         return FALSE;
241     }
242
243     if (vertical) pixels = lprect->bottom - lprect->top;
244     else pixels = lprect->right - lprect->left;
245
246     if (pixels <= 2*GetSystemMetrics(SM_CXVSCROLL) + SCROLL_MIN_RECT)
247     {
248         if (pixels > SCROLL_MIN_RECT)
249             *arrowSize = (pixels - SCROLL_MIN_RECT) / 2;
250         else
251             *arrowSize = 0;
252         *thumbPos = *thumbSize = 0;
253     }
254     else
255     {
256         SCROLLBAR_INFO *info = SCROLL_GetScrollBarInfo( hwnd, nBar );
257
258         *arrowSize = GetSystemMetrics(SM_CXVSCROLL);
259         pixels -= (2 * (GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP));
260
261         if (info->page)
262         {
263             *thumbSize = MulDiv(pixels,info->page,(info->maxVal-info->minVal+1));
264             if (*thumbSize < SCROLL_MIN_THUMB) *thumbSize = SCROLL_MIN_THUMB;
265         }
266         else *thumbSize = GetSystemMetrics(SM_CXVSCROLL);
267
268         if (((pixels -= *thumbSize ) < 0) ||
269             ((info->flags & ESB_DISABLE_BOTH) == ESB_DISABLE_BOTH))
270         {
271             /* Rectangle too small or scrollbar disabled -> no thumb */
272             *thumbPos = *thumbSize = 0;
273         }
274         else
275         {
276             INT max = info->maxVal - max( info->page-1, 0 );
277             if (info->minVal >= max)
278                 *thumbPos = *arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
279             else
280                 *thumbPos = *arrowSize - SCROLL_ARROW_THUMB_OVERLAP
281                   + MulDiv(pixels, (info->curVal-info->minVal),(max - info->minVal));
282         }
283     }
284     WIN_ReleaseWndPtr(wndPtr);
285     return vertical;
286 }
287
288
289 /***********************************************************************
290  *           SCROLL_GetThumbVal
291  *
292  * Compute the current scroll position based on the thumb position in pixels
293  * from the top of the scroll-bar.
294  */
295 static UINT SCROLL_GetThumbVal( SCROLLBAR_INFO *infoPtr, RECT *rect,
296                                   BOOL vertical, INT pos )
297 {
298     INT thumbSize;
299     INT pixels = vertical ? rect->bottom-rect->top : rect->right-rect->left;
300
301     if ((pixels -= 2*(GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP)) <= 0)
302         return infoPtr->minVal;
303
304     if (infoPtr->page)
305     {
306         thumbSize = MulDiv(pixels,infoPtr->page,(infoPtr->maxVal-infoPtr->minVal+1));
307         if (thumbSize < SCROLL_MIN_THUMB) thumbSize = SCROLL_MIN_THUMB;
308     }
309     else thumbSize = GetSystemMetrics(SM_CXVSCROLL);
310
311     if ((pixels -= thumbSize) <= 0) return infoPtr->minVal;
312
313     pos = max( 0, pos - (GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP) );
314     if (pos > pixels) pos = pixels;
315
316     if (!infoPtr->page) pos *= infoPtr->maxVal - infoPtr->minVal;
317     else pos *= infoPtr->maxVal - infoPtr->minVal - infoPtr->page + 1;
318     return infoPtr->minVal + ((pos + pixels / 2) / pixels);
319 }
320
321 /***********************************************************************
322  *           SCROLL_PtInRectEx
323  */
324 static BOOL SCROLL_PtInRectEx( LPRECT lpRect, POINT pt, BOOL vertical )
325 {
326     RECT rect = *lpRect;
327
328     if (vertical)
329     {
330         rect.left -= lpRect->right - lpRect->left;
331         rect.right += lpRect->right - lpRect->left;
332     }
333     else
334     {
335         rect.top -= lpRect->bottom - lpRect->top;
336         rect.bottom += lpRect->bottom - lpRect->top;
337     }
338     return PtInRect( &rect, pt );
339 }
340
341 /***********************************************************************
342  *           SCROLL_ClipPos
343  */
344 static POINT SCROLL_ClipPos( LPRECT lpRect, POINT pt )
345 {
346     if( pt.x < lpRect->left )
347         pt.x = lpRect->left;
348     else
349     if( pt.x > lpRect->right )
350         pt.x = lpRect->right;
351
352     if( pt.y < lpRect->top )
353         pt.y = lpRect->top;
354     else
355     if( pt.y > lpRect->bottom )
356         pt.y = lpRect->bottom;
357
358     return pt;
359 }
360
361
362 /***********************************************************************
363  *           SCROLL_HitTest
364  *
365  * Scroll-bar hit testing (don't confuse this with WM_NCHITTEST!).
366  */
367 static enum SCROLL_HITTEST SCROLL_HitTest( HWND hwnd, INT nBar,
368                                            POINT pt, BOOL bDragging )
369 {
370     INT arrowSize, thumbSize, thumbPos;
371     RECT rect;
372
373     BOOL vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
374                                            &arrowSize, &thumbSize, &thumbPos );
375
376     if ( (bDragging && !SCROLL_PtInRectEx( &rect, pt, vertical )) ||
377          (!PtInRect( &rect, pt )) ) return SCROLL_NOWHERE;
378
379     if (vertical)
380     {
381         if (pt.y < rect.top + arrowSize) return SCROLL_TOP_ARROW;
382         if (pt.y >= rect.bottom - arrowSize) return SCROLL_BOTTOM_ARROW;
383         if (!thumbPos) return SCROLL_TOP_RECT;
384         pt.y -= rect.top;
385         if (pt.y < thumbPos) return SCROLL_TOP_RECT;
386         if (pt.y >= thumbPos + thumbSize) return SCROLL_BOTTOM_RECT;
387     }
388     else  /* horizontal */
389     {
390         if (pt.x < rect.left + arrowSize) return SCROLL_TOP_ARROW;
391         if (pt.x >= rect.right - arrowSize) return SCROLL_BOTTOM_ARROW;
392         if (!thumbPos) return SCROLL_TOP_RECT;
393         pt.x -= rect.left;
394         if (pt.x < thumbPos) return SCROLL_TOP_RECT;
395         if (pt.x >= thumbPos + thumbSize) return SCROLL_BOTTOM_RECT;
396     }
397     return SCROLL_THUMB;
398 }
399
400
401 /***********************************************************************
402  *           SCROLL_DrawArrows
403  *
404  * Draw the scroll bar arrows.
405  */
406 static void SCROLL_DrawArrows( HDC hdc, SCROLLBAR_INFO *infoPtr,
407                                RECT *rect, INT arrowSize, BOOL vertical,
408                                BOOL top_pressed, BOOL bottom_pressed )
409 {
410   RECT r;
411
412   r = *rect;
413   if( vertical )
414     r.bottom = r.top + arrowSize;
415   else
416     r.right = r.left + arrowSize;
417
418   DrawFrameControl( hdc, &r, DFC_SCROLL,
419                     (vertical ? DFCS_SCROLLUP : DFCS_SCROLLLEFT)
420                     | (top_pressed ? (DFCS_PUSHED | DFCS_FLAT) : 0 )
421                     | (infoPtr->flags&ESB_DISABLE_LTUP ? DFCS_INACTIVE : 0 ) );
422
423   r = *rect;
424   if( vertical )
425     r.top = r.bottom-arrowSize;
426   else
427     r.left = r.right-arrowSize;
428
429   DrawFrameControl( hdc, &r, DFC_SCROLL,
430                     (vertical ? DFCS_SCROLLDOWN : DFCS_SCROLLRIGHT)
431                     | (bottom_pressed ? (DFCS_PUSHED | DFCS_FLAT) : 0 )
432                     | (infoPtr->flags&ESB_DISABLE_RTDN ? DFCS_INACTIVE : 0) );
433 }
434
435 static void SCROLL_DrawMovingThumb( HDC hdc, RECT *rect, BOOL vertical,
436                                     INT arrowSize, INT thumbSize )
437 {
438   INT pos = SCROLL_TrackingPos;
439   INT max_size;
440
441   if( vertical )
442     max_size = rect->bottom - rect->top;
443   else
444     max_size = rect->right - rect->left;
445
446   max_size -= (arrowSize-SCROLL_ARROW_THUMB_OVERLAP) + thumbSize;
447
448   if( pos < (arrowSize-SCROLL_ARROW_THUMB_OVERLAP) )
449     pos = (arrowSize-SCROLL_ARROW_THUMB_OVERLAP);
450   else if( pos > max_size )
451     pos = max_size;
452
453   SCROLL_DrawInterior_9x( SCROLL_TrackingWin, hdc, SCROLL_TrackingBar,
454                           rect, arrowSize, thumbSize, pos,
455                           0, vertical, FALSE, FALSE );
456
457   SCROLL_MovingThumb = !SCROLL_MovingThumb;
458 }
459
460 /***********************************************************************
461  *           SCROLL_DrawInterior
462  *
463  * Draw the scroll bar interior (everything except the arrows).
464  */
465 static void SCROLL_DrawInterior_9x( HWND hwnd, HDC hdc, INT nBar,
466                                     RECT *rect, INT arrowSize,
467                                     INT thumbSize, INT thumbPos,
468                                     UINT flags, BOOL vertical,
469                                     BOOL top_selected, BOOL bottom_selected )
470 {
471     RECT r;
472     HPEN hSavePen;
473     HBRUSH hSaveBrush,hBrush;
474
475     /* Only scrollbar controls send WM_CTLCOLORSCROLLBAR.
476      * The window-owned scrollbars need to call DEFWND_ControlColor
477      * to correctly setup default scrollbar colors
478      */
479     if (nBar == SB_CTL)
480     {
481       hBrush = (HBRUSH)SendMessageW( GetParent(hwnd), WM_CTLCOLORSCROLLBAR,
482                                      (WPARAM)hdc,(LPARAM)hwnd);
483     }
484     else
485     {
486       hBrush = DEFWND_ControlColor( hdc, CTLCOLOR_SCROLLBAR );
487     }
488
489     hSavePen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
490     hSaveBrush = SelectObject( hdc, hBrush );
491
492     /* Calculate the scroll rectangle */
493     r = *rect;
494     if (vertical)
495     {
496         r.top    += arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
497         r.bottom -= (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
498     }
499     else
500     {
501         r.left  += arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
502         r.right -= (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
503     }
504
505     /* Draw the scroll rectangles and thumb */
506     if (!thumbPos)  /* No thumb to draw */
507     {
508         PatBlt( hdc, r.left, r.top,
509                      r.right - r.left, r.bottom - r.top,
510                      PATCOPY );
511
512         /* cleanup and return */
513         SelectObject( hdc, hSavePen );
514         SelectObject( hdc, hSaveBrush );
515         return;
516     }
517
518     if (vertical)
519     {
520         PatBlt( hdc, r.left, r.top,
521                   r.right - r.left,
522                   thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP),
523                   top_selected ? 0x0f0000 : PATCOPY );
524         r.top += thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
525         PatBlt( hdc, r.left, r.top + thumbSize,
526                   r.right - r.left,
527                   r.bottom - r.top - thumbSize,
528                   bottom_selected ? 0x0f0000 : PATCOPY );
529         r.bottom = r.top + thumbSize;
530     }
531     else  /* horizontal */
532     {
533         PatBlt( hdc, r.left, r.top,
534                   thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP),
535                   r.bottom - r.top,
536                   top_selected ? 0x0f0000 : PATCOPY );
537         r.left += thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
538         PatBlt( hdc, r.left + thumbSize, r.top,
539                   r.right - r.left - thumbSize,
540                   r.bottom - r.top,
541                   bottom_selected ? 0x0f0000 : PATCOPY );
542         r.right = r.left + thumbSize;
543     }
544
545     /* Draw the thumb */
546     DrawEdge( hdc, &r, EDGE_RAISED, BF_RECT | BF_MIDDLE  );
547
548     /* cleanup */
549     SelectObject( hdc, hSavePen );
550     SelectObject( hdc, hSaveBrush );
551 }
552
553
554 static void SCROLL_DrawInterior( HWND hwnd, HDC hdc, INT nBar,
555                                  RECT *rect, INT arrowSize,
556                                  INT thumbSize, INT thumbPos,
557                                  UINT flags, BOOL vertical,
558                                  BOOL top_selected, BOOL bottom_selected )
559 {
560     RECT r;
561     HPEN hSavePen;
562     HBRUSH hSaveBrush,hBrush;
563     BOOL Save_SCROLL_MovingThumb = SCROLL_MovingThumb;
564
565     if (Save_SCROLL_MovingThumb &&
566         (SCROLL_TrackingWin == hwnd) &&
567         (SCROLL_TrackingBar == nBar))
568         SCROLL_DrawMovingThumb( hdc, rect, vertical, arrowSize, thumbSize );
569
570       /* Select the correct brush and pen */
571
572     /* Only scrollbar controls send WM_CTLCOLORSCROLLBAR.
573      * The window-owned scrollbars need to call DEFWND_ControlColor
574      * to correctly setup default scrollbar colors
575      */
576     if (nBar == SB_CTL) {
577         hBrush = (HBRUSH)SendMessageW( GetParent(hwnd), WM_CTLCOLORSCROLLBAR,
578                                        (WPARAM)hdc,(LPARAM)hwnd);
579     } else {
580         hBrush = DEFWND_ControlColor( hdc, CTLCOLOR_SCROLLBAR );
581     }
582     hSavePen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
583     hSaveBrush = SelectObject( hdc, hBrush );
584
585       /* Calculate the scroll rectangle */
586
587     r = *rect;
588     if (vertical)
589     {
590         r.top    += arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
591         r.bottom -= (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
592     }
593     else
594     {
595         r.left  += arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
596         r.right -= (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
597     }
598
599       /* Draw the scroll bar frame */
600
601       /* Draw the scroll rectangles and thumb */
602
603     if (!thumbPos)  /* No thumb to draw */
604     {
605         PatBlt( hdc, r.left, r.top, r.right - r.left, r.bottom - r.top, PATCOPY );
606
607         /* cleanup and return */
608         SelectObject( hdc, hSavePen );
609         SelectObject( hdc, hSaveBrush );
610         return;
611     }
612
613     if (vertical)
614     {
615         PatBlt( hdc, r.left, r.top, r.right - r.left,
616                 thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP),
617                 top_selected ? 0x0f0000 : PATCOPY );
618         r.top += thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
619         PatBlt( hdc, r.left, r.top + thumbSize, r.right - r.left,
620                 r.bottom - r.top - thumbSize,
621                 bottom_selected ? 0x0f0000 : PATCOPY );
622         r.bottom = r.top + thumbSize;
623     }
624     else  /* horizontal */
625     {
626         PatBlt( hdc, r.left, r.top,
627                 thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP),
628                 r.bottom - r.top, top_selected ? 0x0f0000 : PATCOPY );
629         r.left += thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
630         PatBlt( hdc, r.left + thumbSize, r.top, r.right - r.left - thumbSize,
631                 r.bottom - r.top, bottom_selected ? 0x0f0000 : PATCOPY );
632         r.right = r.left + thumbSize;
633     }
634
635       /* Draw the thumb */
636
637     SelectObject( hdc, GetSysColorBrush(COLOR_BTNFACE) );
638     Rectangle( hdc, r.left+1, r.top+1, r.right-1, r.bottom-1 );
639     DrawEdge( hdc, &r, EDGE_RAISED, BF_RECT );
640
641     if (Save_SCROLL_MovingThumb &&
642         (SCROLL_TrackingWin == hwnd) &&
643         (SCROLL_TrackingBar == nBar))
644         SCROLL_DrawMovingThumb( hdc, rect, vertical, arrowSize, thumbSize );
645
646     /* cleanup */
647     SelectObject( hdc, hSavePen );
648     SelectObject( hdc, hSaveBrush );
649 }
650
651
652 /***********************************************************************
653  *           SCROLL_DrawScrollBar
654  *
655  * Redraw the whole scrollbar.
656  */
657 void SCROLL_DrawScrollBar( HWND hwnd, HDC hdc, INT nBar,
658                            BOOL arrows, BOOL interior )
659 {
660     INT arrowSize, thumbSize, thumbPos;
661     RECT rect;
662     BOOL vertical;
663     WND *wndPtr = WIN_FindWndPtr( hwnd );
664     SCROLLBAR_INFO *infoPtr = SCROLL_GetScrollBarInfo( hwnd, nBar );
665     BOOL Save_SCROLL_MovingThumb = SCROLL_MovingThumb;
666
667     if (!wndPtr || !infoPtr ||
668         ((nBar == SB_VERT) && !(wndPtr->dwStyle & WS_VSCROLL)) ||
669         ((nBar == SB_HORZ) && !(wndPtr->dwStyle & WS_HSCROLL))) goto END;
670     if (!WIN_IsWindowDrawable( hwnd, FALSE )) goto END;
671     hwnd = wndPtr->hwndSelf;  /* make it a full handle */
672
673     vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
674                                         &arrowSize, &thumbSize, &thumbPos );
675
676     /* do not draw if the scrollbar rectangle is empty */
677     if(IsRectEmpty(&rect))
678       goto END;
679
680     if (Save_SCROLL_MovingThumb &&
681         (SCROLL_TrackingWin == hwnd) &&
682         (SCROLL_TrackingBar == nBar))
683         SCROLL_DrawMovingThumb( hdc, &rect, vertical, arrowSize, thumbSize );
684
685       /* Draw the arrows */
686
687     if (arrows && arrowSize)
688     {
689         if( vertical == SCROLL_trackVertical && GetCapture() == hwnd )
690             SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
691                                (SCROLL_trackHitTest == SCROLL_TOP_ARROW),
692                                (SCROLL_trackHitTest == SCROLL_BOTTOM_ARROW) );
693         else
694             SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
695                                                                FALSE, FALSE );
696     }
697     if( interior )
698         SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbSize,
699                          thumbPos, infoPtr->flags, vertical, FALSE, FALSE );
700
701     if (Save_SCROLL_MovingThumb &&
702         (SCROLL_TrackingWin == hwnd) &&
703         (SCROLL_TrackingBar == nBar))
704         SCROLL_DrawMovingThumb( hdc, &rect, vertical, arrowSize, thumbSize );
705
706     /* if scroll bar has focus, reposition the caret */
707     if(hwnd==GetFocus() && (nBar==SB_CTL))
708     {
709         if (!vertical)
710         {
711             SetCaretPos(thumbPos+1, rect.top+1);
712         }
713         else
714         {
715             SetCaretPos(rect.top+1, thumbPos+1);
716         }
717     }
718
719 END:
720     WIN_ReleaseWndPtr(wndPtr);
721 }
722
723 /***********************************************************************
724  *           SCROLL_DrawSizeGrip
725  *
726  *  Draw the size grip.
727  */
728 static void SCROLL_DrawSizeGrip( HWND hwnd,  HDC hdc)
729 {
730     RECT rc;
731
732     GetClientRect( hwnd, &rc );
733     FillRect( hdc, &rc, GetSysColorBrush(COLOR_SCROLLBAR) );
734     rc.left = max( rc.left, rc.right - GetSystemMetrics(SM_CXVSCROLL) - 1 );
735     rc.top  = max( rc.top, rc.bottom - GetSystemMetrics(SM_CYHSCROLL) - 1 );
736     DrawFrameControl( hdc, &rc, DFC_SCROLL, DFCS_SCROLLSIZEGRIP );
737 }
738
739
740 /***********************************************************************
741  *           SCROLL_RefreshScrollBar
742  *
743  * Repaint the scroll bar interior after a SetScrollRange() or
744  * SetScrollPos() call.
745  */
746 static void SCROLL_RefreshScrollBar( HWND hwnd, INT nBar,
747                                      BOOL arrows, BOOL interior )
748 {
749     HDC hdc = GetDCEx( hwnd, 0,
750                            DCX_CACHE | ((nBar == SB_CTL) ? 0 : DCX_WINDOW) );
751     if (!hdc) return;
752
753     SCROLL_DrawScrollBar( hwnd, hdc, nBar, arrows, interior );
754     ReleaseDC( hwnd, hdc );
755 }
756
757
758 /***********************************************************************
759  *           SCROLL_HandleKbdEvent
760  *
761  * Handle a keyboard event (only for SB_CTL scrollbars with focus).
762  *
763  * PARAMS
764  *    hwnd   [I] Handle of window with scrollbar(s)
765  *    wParam [I] Variable input including enable state
766  *    lParam [I] Variable input including input point
767  */
768 static void SCROLL_HandleKbdEvent(HWND hwnd, WPARAM wParam, LPARAM lParam)
769 {
770     TRACE("hwnd=%p wParam=%d lParam=%ld\n", hwnd, wParam, lParam);
771
772     /* hide caret on first KEYDOWN to prevent flicker */
773     if ((lParam & PFD_DOUBLEBUFFER_DONTCARE) == 0)
774         HideCaret(hwnd);
775
776     switch(wParam)
777     {
778     case VK_PRIOR: wParam = SB_PAGEUP; break;
779     case VK_NEXT:  wParam = SB_PAGEDOWN; break;
780     case VK_HOME:  wParam = SB_TOP; break;
781     case VK_END:   wParam = SB_BOTTOM; break;
782     case VK_UP:    wParam = SB_LINEUP; break;
783     case VK_DOWN:  wParam = SB_LINEDOWN; break;
784     default: return;
785     }
786     SendMessageW(GetParent(hwnd),
787         ((GetWindowLongW( hwnd, GWL_STYLE ) & SBS_VERT) ?
788             WM_VSCROLL : WM_HSCROLL), wParam, (LPARAM)hwnd);
789 }
790
791
792 /***********************************************************************
793  *           SCROLL_HandleScrollEvent
794  *
795  * Handle a mouse or timer event for the scrollbar.
796  * 'pt' is the location of the mouse event in client (for SB_CTL) or
797  * windows coordinates.
798  */
799 static void SCROLL_HandleScrollEvent( HWND hwnd, INT nBar, UINT msg, POINT pt)
800 {
801       /* Previous mouse position for timer events */
802     static POINT prevPt;
803       /* Thumb position when tracking started. */
804     static UINT trackThumbPos;
805       /* Position in the scroll-bar of the last button-down event. */
806     static INT lastClickPos;
807       /* Position in the scroll-bar of the last mouse event. */
808     static INT lastMousePos;
809
810     enum SCROLL_HITTEST hittest;
811     HWND hwndOwner, hwndCtl;
812     BOOL vertical;
813     INT arrowSize, thumbSize, thumbPos;
814     RECT rect;
815     HDC hdc;
816
817     SCROLLBAR_INFO *infoPtr = SCROLL_GetScrollBarInfo( hwnd, nBar );
818     if (!infoPtr) return;
819     if ((SCROLL_trackHitTest == SCROLL_NOWHERE) && (msg != WM_LBUTTONDOWN))
820                   return;
821
822     if (nBar == SB_CTL && (GetWindowLongW( hwnd, GWL_STYLE ) & (SBS_SIZEGRIP | SBS_SIZEBOX)))
823     {
824         switch(msg)
825         {
826             case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
827                 HideCaret(hwnd);  /* hide caret while holding down LBUTTON */
828                 SetCapture( hwnd );
829                 prevPt = pt;
830                 SCROLL_trackHitTest  = hittest = SCROLL_THUMB;
831                 break;
832             case WM_MOUSEMOVE:
833                 GetClientRect(GetParent(GetParent(hwnd)),&rect);
834                 prevPt = pt;
835                 break;
836             case WM_LBUTTONUP:
837                 ReleaseCapture();
838                 SCROLL_trackHitTest  = hittest = SCROLL_NOWHERE;
839                 if (hwnd==GetFocus()) ShowCaret(hwnd);
840                 break;
841             case WM_SYSTIMER:
842                 pt = prevPt;
843                 break;
844         }
845         return;
846     }
847
848     hdc = GetDCEx( hwnd, 0, DCX_CACHE | ((nBar == SB_CTL) ? 0 : DCX_WINDOW));
849     vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
850                                         &arrowSize, &thumbSize, &thumbPos );
851     hwndOwner = (nBar == SB_CTL) ? GetParent(hwnd) : hwnd;
852     hwndCtl   = (nBar == SB_CTL) ? hwnd : 0;
853
854     switch(msg)
855     {
856       case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
857           HideCaret(hwnd);  /* hide caret while holding down LBUTTON */
858           SCROLL_trackVertical = vertical;
859           SCROLL_trackHitTest  = hittest = SCROLL_HitTest( hwnd, nBar, pt, FALSE );
860           lastClickPos  = vertical ? (pt.y - rect.top) : (pt.x - rect.left);
861           lastMousePos  = lastClickPos;
862           trackThumbPos = thumbPos;
863           prevPt = pt;
864           if (nBar == SB_CTL && (GetWindowLongW(hwnd, GWL_STYLE) & WS_TABSTOP)) SetFocus( hwnd );
865           SetCapture( hwnd );
866           break;
867
868       case WM_MOUSEMOVE:
869           hittest = SCROLL_HitTest( hwnd, nBar, pt, TRUE );
870           prevPt = pt;
871           break;
872
873       case WM_LBUTTONUP:
874           hittest = SCROLL_NOWHERE;
875           ReleaseCapture();
876           /* if scrollbar has focus, show back caret */
877           if (hwnd==GetFocus()) ShowCaret(hwnd);
878           break;
879
880       case WM_SYSTIMER:
881           pt = prevPt;
882           hittest = SCROLL_HitTest( hwnd, nBar, pt, FALSE );
883           break;
884
885       default:
886           return;  /* Should never happen */
887     }
888
889     TRACE("Event: hwnd=%p bar=%d msg=%s pt=%ld,%ld hit=%d\n",
890           hwnd, nBar, SPY_GetMsgName(msg,hwnd), pt.x, pt.y, hittest );
891
892     switch(SCROLL_trackHitTest)
893     {
894     case SCROLL_NOWHERE:  /* No tracking in progress */
895         break;
896
897     case SCROLL_TOP_ARROW:
898         SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
899                            (hittest == SCROLL_trackHitTest), FALSE );
900         if (hittest == SCROLL_trackHitTest)
901         {
902             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
903             {
904                 SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
905                                 SB_LINEUP, (LPARAM)hwndCtl );
906             }
907
908             SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
909                             SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
910                             (TIMERPROC)0 );
911         }
912         else KillSystemTimer( hwnd, SCROLL_TIMER );
913         break;
914
915     case SCROLL_TOP_RECT:
916         SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbSize,
917                              thumbPos, infoPtr->flags, vertical,
918                              (hittest == SCROLL_trackHitTest), FALSE );
919         if (hittest == SCROLL_trackHitTest)
920         {
921             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
922             {
923                 SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
924                                 SB_PAGEUP, (LPARAM)hwndCtl );
925             }
926             SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
927                               SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
928                               (TIMERPROC)0 );
929         }
930         else KillSystemTimer( hwnd, SCROLL_TIMER );
931         break;
932
933     case SCROLL_THUMB:
934         if (msg == WM_LBUTTONDOWN)
935         {
936             SCROLL_TrackingWin = hwnd;
937             SCROLL_TrackingBar = nBar;
938             SCROLL_TrackingPos = trackThumbPos + lastMousePos - lastClickPos;
939             SCROLL_TrackingVal = SCROLL_GetThumbVal( infoPtr, &rect,
940                                                         vertical,
941                                                         SCROLL_TrackingPos );
942             if (!SCROLL_MovingThumb)
943                 SCROLL_DrawMovingThumb(hdc, &rect, vertical, arrowSize, thumbSize);
944         }
945         else if (msg == WM_LBUTTONUP)
946         {
947             if (SCROLL_MovingThumb)
948                 SCROLL_DrawMovingThumb(hdc, &rect, vertical, arrowSize, thumbSize);
949             SCROLL_TrackingWin = 0;
950             SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbSize,
951                                  thumbPos, infoPtr->flags, vertical,
952                                  FALSE, FALSE );
953         }
954         else  /* WM_MOUSEMOVE */
955         {
956             UINT pos;
957
958             if (!SCROLL_PtInRectEx( &rect, pt, vertical )) pos = lastClickPos;
959             else
960             {
961                 pt = SCROLL_ClipPos( &rect, pt );
962                 pos = vertical ? (pt.y - rect.top) : (pt.x - rect.left);
963             }
964             if ( (pos != lastMousePos) || (!SCROLL_MovingThumb) )
965             {
966                 if (SCROLL_MovingThumb)
967                     SCROLL_DrawMovingThumb( hdc, &rect, vertical,
968                                         arrowSize, thumbSize );
969                 lastMousePos = pos;
970                 SCROLL_TrackingPos = trackThumbPos + pos - lastClickPos;
971                 SCROLL_TrackingVal = SCROLL_GetThumbVal( infoPtr, &rect,
972                                                          vertical,
973                                                          SCROLL_TrackingPos );
974                 SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
975                                 MAKEWPARAM( SB_THUMBTRACK, SCROLL_TrackingVal),
976                                 (LPARAM)hwndCtl );
977                 if (!SCROLL_MovingThumb)
978                     SCROLL_DrawMovingThumb( hdc, &rect, vertical,
979                                         arrowSize, thumbSize );
980             }
981         }
982         break;
983
984     case SCROLL_BOTTOM_RECT:
985         SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbSize,
986                              thumbPos, infoPtr->flags, vertical,
987                              FALSE, (hittest == SCROLL_trackHitTest) );
988         if (hittest == SCROLL_trackHitTest)
989         {
990             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
991             {
992                 SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
993                                 SB_PAGEDOWN, (LPARAM)hwndCtl );
994             }
995             SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
996                               SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
997                               (TIMERPROC)0 );
998         }
999         else KillSystemTimer( hwnd, SCROLL_TIMER );
1000         break;
1001
1002     case SCROLL_BOTTOM_ARROW:
1003         SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
1004                            FALSE, (hittest == SCROLL_trackHitTest) );
1005         if (hittest == SCROLL_trackHitTest)
1006         {
1007             if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
1008             {
1009                 SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
1010                                 SB_LINEDOWN, (LPARAM)hwndCtl );
1011             }
1012
1013             SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
1014                             SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
1015                             (TIMERPROC)0 );
1016         }
1017         else KillSystemTimer( hwnd, SCROLL_TIMER );
1018         break;
1019     }
1020
1021     if (msg == WM_LBUTTONDOWN)
1022     {
1023
1024         if (hittest == SCROLL_THUMB)
1025         {
1026             UINT val = SCROLL_GetThumbVal( infoPtr, &rect, vertical,
1027                                  trackThumbPos + lastMousePos - lastClickPos );
1028             SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
1029                             MAKEWPARAM( SB_THUMBTRACK, val ), (LPARAM)hwndCtl );
1030         }
1031     }
1032
1033     if (msg == WM_LBUTTONUP)
1034     {
1035         hittest = SCROLL_trackHitTest;
1036         SCROLL_trackHitTest = SCROLL_NOWHERE;  /* Terminate tracking */
1037
1038         if (hittest == SCROLL_THUMB)
1039         {
1040             UINT val = SCROLL_GetThumbVal( infoPtr, &rect, vertical,
1041                                  trackThumbPos + lastMousePos - lastClickPos );
1042             SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
1043                             MAKEWPARAM( SB_THUMBPOSITION, val ), (LPARAM)hwndCtl );
1044         }
1045         SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
1046                           SB_ENDSCROLL, (LPARAM)hwndCtl );
1047     }
1048
1049     ReleaseDC( hwnd, hdc );
1050 }
1051
1052
1053 /***********************************************************************
1054  *           SCROLL_TrackScrollBar
1055  *
1056  * Track a mouse button press on a scroll-bar.
1057  * pt is in screen-coordinates for non-client scroll bars.
1058  */
1059 void SCROLL_TrackScrollBar( HWND hwnd, INT scrollbar, POINT pt )
1060 {
1061     MSG msg;
1062     INT xoffset = 0, yoffset = 0;
1063
1064     if (scrollbar != SB_CTL)
1065     {
1066         WND *wndPtr = WIN_GetPtr( hwnd );
1067         if (!wndPtr || wndPtr == WND_OTHER_PROCESS) return;
1068         xoffset = wndPtr->rectClient.left - wndPtr->rectWindow.left;
1069         yoffset = wndPtr->rectClient.top - wndPtr->rectWindow.top;
1070         WIN_ReleasePtr( wndPtr );
1071         ScreenToClient( hwnd, &pt );
1072         pt.x += xoffset;
1073         pt.y += yoffset;
1074     }
1075
1076     SCROLL_HandleScrollEvent( hwnd, scrollbar, WM_LBUTTONDOWN, pt );
1077
1078     do
1079     {
1080         if (!GetMessageW( &msg, 0, 0, 0 )) break;
1081         if (CallMsgFilterW( &msg, MSGF_SCROLLBAR )) continue;
1082         switch(msg.message)
1083         {
1084         case WM_LBUTTONUP:
1085         case WM_MOUSEMOVE:
1086         case WM_SYSTIMER:
1087             pt.x = (short)LOWORD(msg.lParam) + xoffset;
1088             pt.y = (short)HIWORD(msg.lParam) + yoffset;
1089             SCROLL_HandleScrollEvent( hwnd, scrollbar, msg.message, pt );
1090             break;
1091         default:
1092             TranslateMessage( &msg );
1093             DispatchMessageW( &msg );
1094             break;
1095         }
1096         if (!IsWindow( hwnd ))
1097         {
1098             ReleaseCapture();
1099             break;
1100         }
1101     } while (msg.message != WM_LBUTTONUP);
1102 }
1103
1104
1105 /***********************************************************************
1106  *           SCROLL_CreateScrollBar
1107  *
1108  * Create a scroll bar
1109  *
1110  * PARAMS
1111  *    hwnd     [I] Handle of window with scrollbar(s)
1112  *    lpCreate [I] The style and place of the scroll bar
1113  */
1114 static void SCROLL_CreateScrollBar(HWND hwnd, LPCREATESTRUCTW lpCreate)
1115 {
1116     LPSCROLLBAR_INFO info = SCROLL_GetScrollBarInfo(hwnd, SB_CTL);
1117     if (!info) return;
1118
1119     TRACE("hwnd=%p lpCreate=%p\n", hwnd, lpCreate);
1120
1121     if (lpCreate->style & WS_DISABLED)
1122     {
1123         info->flags = ESB_DISABLE_BOTH;
1124         TRACE("Created WS_DISABLED scrollbar\n");
1125     }
1126
1127
1128     if (lpCreate->style & (SBS_SIZEGRIP | SBS_SIZEBOX))
1129     {
1130         if (lpCreate->style & SBS_SIZEBOXTOPLEFTALIGN)
1131             MoveWindow( hwnd, lpCreate->x, lpCreate->y, GetSystemMetrics(SM_CXVSCROLL)+1,
1132                         GetSystemMetrics(SM_CYHSCROLL)+1, FALSE );
1133         else if(lpCreate->style & SBS_SIZEBOXBOTTOMRIGHTALIGN)
1134             MoveWindow( hwnd, lpCreate->x+lpCreate->cx-GetSystemMetrics(SM_CXVSCROLL)-1, 
1135                         lpCreate->y+lpCreate->cy-GetSystemMetrics(SM_CYHSCROLL)-1,
1136                         GetSystemMetrics(SM_CXVSCROLL)+1,
1137                         GetSystemMetrics(SM_CYHSCROLL)+1, FALSE );
1138     }
1139     else if (lpCreate->style & SBS_VERT)
1140     {
1141         if (lpCreate->style & SBS_LEFTALIGN)
1142             MoveWindow( hwnd, lpCreate->x, lpCreate->y,
1143                         GetSystemMetrics(SM_CXVSCROLL)+1, lpCreate->cy, FALSE );
1144         else if (lpCreate->style & SBS_RIGHTALIGN)
1145             MoveWindow( hwnd,
1146                         lpCreate->x+lpCreate->cx-GetSystemMetrics(SM_CXVSCROLL)-1,
1147                         lpCreate->y,
1148                         GetSystemMetrics(SM_CXVSCROLL)+1, lpCreate->cy, FALSE );
1149     }
1150     else  /* SBS_HORZ */
1151     {
1152         if (lpCreate->style & SBS_TOPALIGN)
1153             MoveWindow( hwnd, lpCreate->x, lpCreate->y,
1154                         lpCreate->cx, GetSystemMetrics(SM_CYHSCROLL)+1, FALSE );
1155         else if (lpCreate->style & SBS_BOTTOMALIGN)
1156             MoveWindow( hwnd,
1157                         lpCreate->x,
1158                         lpCreate->y+lpCreate->cy-GetSystemMetrics(SM_CYHSCROLL)-1,
1159                         lpCreate->cx, GetSystemMetrics(SM_CYHSCROLL)+1, FALSE );
1160     }
1161 }
1162
1163
1164 /*************************************************************************
1165  *           SCROLL_GetScrollInfo
1166  *
1167  * Internal helper for the API function
1168  *
1169  * PARAMS
1170  *    hwnd [I]  Handle of window with scrollbar(s)
1171  *    nBar [I]  One of SB_HORZ, SB_VERT, or SB_CTL
1172  *    info [IO] fMask specifies which values to retrieve
1173  */
1174 static BOOL SCROLL_GetScrollInfo(HWND hwnd, INT nBar, LPSCROLLINFO info)
1175 {
1176     LPSCROLLBAR_INFO infoPtr;
1177
1178     /* handle invalid data structure */
1179     if (!SCROLL_ScrollInfoValid(info)
1180         || !(infoPtr = SCROLL_GetScrollBarInfo(hwnd, nBar)))
1181             return FALSE;
1182
1183     /* fill in the desired scroll info structure */
1184     if (info->fMask & SIF_PAGE) info->nPage = infoPtr->page;
1185     if (info->fMask & SIF_POS) info->nPos = infoPtr->curVal;
1186     if ((info->fMask & SIF_TRACKPOS) && (info->cbSize == sizeof(*info)))
1187         info->nTrackPos = (SCROLL_TrackingWin == WIN_GetFullHandle(hwnd)) ? SCROLL_TrackingVal : infoPtr->curVal;
1188     if (info->fMask & SIF_RANGE)
1189     {
1190         info->nMin = infoPtr->minVal;
1191         info->nMax = infoPtr->maxVal;
1192     }
1193
1194     return (info->fMask & SIF_ALL) != 0;
1195 }
1196
1197
1198 /*************************************************************************
1199  *           SCROLL_GetScrollPos
1200  *
1201  *  Internal helper for the API function
1202  *
1203  * PARAMS
1204  *    hwnd [I]  Handle of window with scrollbar(s)
1205  *    nBar [I]  One of SB_HORZ, SB_VERT, or SB_CTL
1206  */
1207 static INT SCROLL_GetScrollPos(HWND hwnd, INT nBar)
1208 {
1209     LPSCROLLBAR_INFO infoPtr = SCROLL_GetScrollBarInfo(hwnd, nBar);
1210     return infoPtr ? infoPtr->curVal: 0;
1211 }
1212
1213
1214 /*************************************************************************
1215  *           SCROLL_GetScrollRange
1216  *
1217  *  Internal helper for the API function
1218  *
1219  * PARAMS
1220  *    hwnd  [I]  Handle of window with scrollbar(s)
1221  *    nBar  [I]  One of SB_HORZ, SB_VERT, or SB_CTL
1222  *    lpMin [O]  Where to store minimum value
1223  *    lpMax [O]  Where to store maximum value
1224  *
1225  * RETURNS STD
1226  */
1227 static BOOL SCROLL_GetScrollRange(HWND hwnd, INT nBar, LPINT lpMin, LPINT lpMax)
1228 {
1229     LPSCROLLBAR_INFO infoPtr = SCROLL_GetScrollBarInfo(hwnd, nBar);
1230
1231     if (lpMin) *lpMin = infoPtr ? infoPtr->minVal : 0;
1232     if (lpMax) *lpMax = infoPtr ? infoPtr->maxVal : 0;
1233
1234     return TRUE;
1235 }
1236
1237
1238 /*************************************************************************
1239  *           SCROLL_SetScrollRange
1240  *
1241  * PARAMS
1242  *    hwnd  [I]  Handle of window with scrollbar(s)
1243  *    nBar  [I]  One of SB_HORZ, SB_VERT, or SB_CTL
1244  *    lpMin [I]  Minimum value
1245  *    lpMax [I]  Maximum value
1246  *
1247  */
1248 static BOOL SCROLL_SetScrollRange(HWND hwnd, INT nBar, INT minVal, INT maxVal)
1249 {
1250     LPSCROLLBAR_INFO infoPtr = SCROLL_GetScrollBarInfo(hwnd, nBar);
1251
1252     TRACE("hwnd=%p nBar=%d min=%d max=%d\n", hwnd, nBar, minVal, maxVal);
1253
1254     if (infoPtr)
1255     {
1256         infoPtr->minVal = minVal;
1257         infoPtr->maxVal = maxVal;
1258     }
1259     return TRUE;
1260 }
1261
1262
1263 /***********************************************************************
1264  *           ScrollBarWndProc
1265  */
1266 static LRESULT WINAPI ScrollBarWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1267 {
1268     if (!IsWindow( hwnd )) return 0;
1269
1270     switch(message)
1271     {
1272     case WM_CREATE:
1273         SCROLL_CreateScrollBar(hwnd, (LPCREATESTRUCTW)lParam);
1274         break;
1275
1276     case WM_ENABLE:
1277         {
1278             SCROLLBAR_INFO *infoPtr;
1279             if ((infoPtr = SCROLL_GetScrollBarInfo( hwnd, SB_CTL )))
1280             {
1281                 infoPtr->flags = wParam ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH;
1282                 SCROLL_RefreshScrollBar(hwnd, SB_CTL, TRUE, TRUE);
1283             }
1284         }
1285         return 0;
1286
1287     case WM_LBUTTONDBLCLK:
1288     case WM_LBUTTONDOWN:
1289         {
1290             POINT pt;
1291             pt.x = (short)LOWORD(lParam);
1292             pt.y = (short)HIWORD(lParam);
1293             SCROLL_TrackScrollBar( hwnd, SB_CTL, pt );
1294         }
1295         break;
1296     case WM_LBUTTONUP:
1297     case WM_MOUSEMOVE:
1298     case WM_SYSTIMER:
1299         {
1300             POINT pt;
1301             pt.x = (short)LOWORD(lParam);
1302             pt.y = (short)HIWORD(lParam);
1303             SCROLL_HandleScrollEvent( hwnd, SB_CTL, message, pt );
1304         }
1305         break;
1306
1307     case WM_KEYDOWN:
1308         SCROLL_HandleKbdEvent(hwnd, wParam, lParam);
1309         break;
1310
1311     case WM_KEYUP:
1312         ShowCaret(hwnd);
1313         break;
1314
1315     case WM_SETFOCUS:
1316         {
1317             /* Create a caret when a ScrollBar get focus */
1318             RECT rect;
1319             int arrowSize, thumbSize, thumbPos, vertical;
1320             vertical = SCROLL_GetScrollBarRect( hwnd, SB_CTL, &rect,
1321                                                 &arrowSize, &thumbSize, &thumbPos );
1322             if (!vertical)
1323             {
1324                 CreateCaret(hwnd, (HBITMAP)1, thumbSize-2, rect.bottom-rect.top-2);
1325                 SetCaretPos(thumbPos+1, rect.top+1);
1326             }
1327             else
1328             {
1329                 CreateCaret(hwnd, (HBITMAP)1, rect.right-rect.left-2,thumbSize-2);
1330                 SetCaretPos(rect.top+1, thumbPos+1);
1331             }
1332             ShowCaret(hwnd);
1333         }
1334         break;
1335
1336     case WM_KILLFOCUS:
1337         {
1338             RECT rect;
1339             int arrowSize, thumbSize, thumbPos, vertical;
1340             vertical = SCROLL_GetScrollBarRect( hwnd, SB_CTL, &rect,&arrowSize, &thumbSize, &thumbPos );
1341             if (!vertical){
1342                 rect.left=thumbPos+1;
1343                 rect.right=rect.left+thumbSize;
1344             }
1345             else
1346             {
1347                 rect.top=thumbPos+1;
1348                 rect.bottom=rect.top+thumbSize;
1349             }
1350             HideCaret(hwnd);
1351             InvalidateRect(hwnd,&rect,0);
1352             DestroyCaret();
1353         }
1354         break;
1355
1356     case WM_ERASEBKGND:
1357          return 1;
1358
1359     case WM_GETDLGCODE:
1360          return DLGC_WANTARROWS; /* Windows returns this value */
1361
1362     case WM_PAINT:
1363         {
1364             PAINTSTRUCT ps;
1365             HDC hdc = wParam ? (HDC)wParam : BeginPaint(hwnd, &ps);
1366             if (GetWindowLongW( hwnd, GWL_STYLE ) & SBS_SIZEGRIP)
1367             {
1368                 SCROLL_DrawSizeGrip( hwnd, hdc);
1369             }
1370             else if (GetWindowLongW( hwnd, GWL_STYLE ) & SBS_SIZEBOX)
1371             {
1372                 RECT rc;
1373                 GetClientRect( hwnd, &rc );
1374                 FillRect( hdc, &rc, GetSysColorBrush(COLOR_SCROLLBAR) );
1375             }
1376             else
1377                 SCROLL_DrawScrollBar( hwnd, hdc, SB_CTL, TRUE, TRUE );
1378             if (!wParam) EndPaint(hwnd, &ps);
1379         }
1380         break;
1381
1382     case SBM_SETPOS16:
1383     case SBM_SETPOS:
1384         return SetScrollPos( hwnd, SB_CTL, wParam, (BOOL)lParam );
1385
1386     case SBM_GETPOS16:
1387     case SBM_GETPOS:
1388        return SCROLL_GetScrollPos(hwnd, SB_CTL);
1389
1390     case SBM_SETRANGE16:
1391         if (wParam) message = SBM_SETRANGEREDRAW;
1392         wParam = LOWORD(lParam);
1393         lParam = HIWORD(lParam);
1394         /* fall through */
1395     case SBM_SETRANGEREDRAW:
1396     case SBM_SETRANGE:
1397         {
1398             INT oldPos = SCROLL_GetScrollPos( hwnd, SB_CTL );
1399             SCROLL_SetScrollRange( hwnd, SB_CTL, wParam, lParam );
1400             if (message == SBM_SETRANGEREDRAW)
1401                 SCROLL_RefreshScrollBar( hwnd, SB_CTL, TRUE, TRUE );
1402             if (oldPos != SCROLL_GetScrollPos( hwnd, SB_CTL )) return oldPos;
1403         }
1404         return 0;
1405
1406     case SBM_GETRANGE16:
1407     {
1408         INT min, max;
1409
1410         SCROLL_GetScrollRange(hwnd, SB_CTL, &min, &max);
1411         return MAKELRESULT(min, max);
1412     }
1413
1414     case SBM_GETRANGE:
1415         return SCROLL_GetScrollRange(hwnd, SB_CTL, (LPINT)wParam, (LPINT)lParam);
1416
1417     case SBM_ENABLE_ARROWS16:
1418     case SBM_ENABLE_ARROWS:
1419         return EnableScrollBar( hwnd, SB_CTL, wParam );
1420
1421     case SBM_SETSCROLLINFO:
1422         return SCROLL_SetScrollInfo( hwnd, SB_CTL, (SCROLLINFO *)lParam, wParam );
1423
1424     case SBM_GETSCROLLINFO:
1425         return SCROLL_GetScrollInfo(hwnd, SB_CTL, (SCROLLINFO *)lParam);
1426
1427     case 0x00e5:
1428     case 0x00e7:
1429     case 0x00e8:
1430     case 0x00eb:
1431     case 0x00ec:
1432     case 0x00ed:
1433     case 0x00ee:
1434     case 0x00ef:
1435         ERR("unknown Win32 msg %04x wp=%08x lp=%08lx\n",
1436                     message, wParam, lParam );
1437         break;
1438
1439     default:
1440         if (message >= WM_USER)
1441             WARN("unknown msg %04x wp=%04x lp=%08lx\n",
1442                          message, wParam, lParam );
1443         return DefWindowProcW( hwnd, message, wParam, lParam );
1444     }
1445     return 0;
1446 }
1447
1448
1449 /*************************************************************************
1450  *           SetScrollInfo   (USER32.@)
1451  * SetScrollInfo can be used to set the position, upper bound,
1452  * lower bound, and page size of a scrollbar control.
1453  *
1454  * PARAMS
1455  *    hwnd    [I]  Handle of window with scrollbar(s)
1456  *    nBar    [I]  One of SB_HORZ, SB_VERT, or SB_CTL
1457  *    info    [I]  Specifies what to change and new values
1458  *    bRedraw [I]  Should scrollbar be redrawn afterwards?
1459  *
1460  * RETURNS
1461  *    Scrollbar position
1462  *
1463  * NOTE
1464  *    For 100 lines of text to be displayed in a window of 25 lines,
1465  *  one would for instance use info->nMin=0, info->nMax=75
1466  *  (corresponding to the 76 different positions of the window on
1467  *  the text), and info->nPage=25.
1468  */
1469 INT WINAPI SetScrollInfo(HWND hwnd, INT nBar, const SCROLLINFO *info, BOOL bRedraw)
1470 {
1471     TRACE("hwnd=%p nBar=%d info=%p, bRedraw=%d\n", hwnd, nBar, info, bRedraw);
1472
1473     /* Refer SB_CTL requests to the window */
1474     if (nBar == SB_CTL)
1475         return SendMessageW(hwnd, SBM_SETSCROLLINFO, bRedraw, (LPARAM)info);
1476     else
1477         return SCROLL_SetScrollInfo( hwnd, nBar, info, bRedraw );
1478 }
1479
1480 static INT SCROLL_SetScrollInfo( HWND hwnd, INT nBar, const SCROLLINFO *info, BOOL bRedraw )
1481 {
1482     /* Update the scrollbar state and set action flags according to
1483      * what has to be done graphics wise. */
1484
1485     SCROLLBAR_INFO *infoPtr;
1486     UINT new_flags;
1487     BOOL bChangeParams = FALSE; /* don't show/hide scrollbar if params don't change */
1488     INT action = 0;
1489
1490     if (!(infoPtr = SCROLL_GetScrollBarInfo(hwnd, nBar))) return 0;
1491     if (info->fMask & ~(SIF_ALL | SIF_DISABLENOSCROLL)) return 0;
1492     if ((info->cbSize != sizeof(*info)) &&
1493         (info->cbSize != sizeof(*info)-sizeof(info->nTrackPos))) return 0;
1494
1495     if (TRACE_ON(scroll))
1496     {
1497         TRACE("hwnd=%p bar=%d", hwnd, nBar);
1498         if (info->fMask & SIF_PAGE) TRACE( " page=%d", info->nPage );
1499         if (info->fMask & SIF_POS) TRACE( " pos=%d", info->nPos );
1500         if (info->fMask & SIF_RANGE) TRACE( " min=%d max=%d", info->nMin, info->nMax );
1501         TRACE("\n");
1502     }
1503
1504     /* Set the page size */
1505
1506     if (info->fMask & SIF_PAGE)
1507     {
1508         if( infoPtr->page != info->nPage )
1509         {
1510             infoPtr->page = info->nPage;
1511             action |= SA_SSI_REFRESH;
1512            bChangeParams = TRUE;
1513         }
1514     }
1515
1516     /* Set the scroll pos */
1517
1518     if (info->fMask & SIF_POS)
1519     {
1520         if( infoPtr->curVal != info->nPos )
1521         {
1522             infoPtr->curVal = info->nPos;
1523             action |= SA_SSI_REFRESH;
1524         }
1525     }
1526
1527     /* Set the scroll range */
1528
1529     if (info->fMask & SIF_RANGE)
1530     {
1531         /* Invalid range -> range is set to (0,0) */
1532         if ((info->nMin > info->nMax) ||
1533             ((UINT)(info->nMax - info->nMin) >= 0x80000000))
1534         {
1535             infoPtr->minVal = 0;
1536             infoPtr->maxVal = 0;
1537             bChangeParams = TRUE;
1538         }
1539         else
1540         {
1541             if( infoPtr->minVal != info->nMin ||
1542                 infoPtr->maxVal != info->nMax )
1543             {
1544                 action |= SA_SSI_REFRESH;
1545                 infoPtr->minVal = info->nMin;
1546                 infoPtr->maxVal = info->nMax;
1547                 bChangeParams = TRUE;
1548             }
1549         }
1550     }
1551
1552     /* Make sure the page size is valid */
1553
1554     if (infoPtr->page < 0) infoPtr->page = 0;
1555     else if (infoPtr->page > infoPtr->maxVal - infoPtr->minVal + 1 )
1556         infoPtr->page = infoPtr->maxVal - infoPtr->minVal + 1;
1557
1558     /* Make sure the pos is inside the range */
1559
1560     if (infoPtr->curVal < infoPtr->minVal)
1561         infoPtr->curVal = infoPtr->minVal;
1562     else if (infoPtr->curVal > infoPtr->maxVal - max( infoPtr->page-1, 0 ))
1563         infoPtr->curVal = infoPtr->maxVal - max( infoPtr->page-1, 0 );
1564
1565     TRACE("    new values: page=%d pos=%d min=%d max=%d\n",
1566                  infoPtr->page, infoPtr->curVal,
1567                  infoPtr->minVal, infoPtr->maxVal );
1568
1569     /* don't change the scrollbar state if SetScrollInfo
1570      * is just called with SIF_DISABLENOSCROLL
1571      */
1572     if(!(info->fMask & SIF_ALL)) goto done;
1573
1574     /* Check if the scrollbar should be hidden or disabled */
1575
1576     if (info->fMask & (SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL))
1577     {
1578         new_flags = infoPtr->flags;
1579         if (infoPtr->minVal >= infoPtr->maxVal - max( infoPtr->page-1, 0 ))
1580         {
1581             /* Hide or disable scroll-bar */
1582             if (info->fMask & SIF_DISABLENOSCROLL)
1583             {
1584                 new_flags = ESB_DISABLE_BOTH;
1585                 action |= SA_SSI_REFRESH;
1586             }
1587             else if ((nBar != SB_CTL) && bChangeParams)
1588             {
1589                 action = SA_SSI_HIDE;
1590                 goto done;
1591             }
1592         }
1593         else  /* Show and enable scroll-bar */
1594         {
1595             new_flags = 0;
1596             if ((nBar != SB_CTL) && bChangeParams)
1597                 action |= SA_SSI_SHOW;
1598         }
1599
1600         if (infoPtr->flags != new_flags) /* check arrow flags */
1601         {
1602             infoPtr->flags = new_flags;
1603             action |= SA_SSI_REPAINT_ARROWS;
1604         }
1605     }
1606
1607 done:
1608     if( action & SA_SSI_HIDE )
1609         SCROLL_ShowScrollBar( hwnd, nBar, FALSE, FALSE );
1610     else
1611     {
1612         if( action & SA_SSI_SHOW )
1613             if( SCROLL_ShowScrollBar( hwnd, nBar, TRUE, TRUE ) )
1614                 return infoPtr->curVal; /* SetWindowPos() already did the painting */
1615
1616         if( bRedraw )
1617             SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, TRUE );
1618         else if( action & SA_SSI_REPAINT_ARROWS )
1619             SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, FALSE );
1620     }
1621
1622     /* Return current position */
1623     return infoPtr->curVal;
1624 }
1625
1626
1627 /*************************************************************************
1628  *           GetScrollInfo   (USER32.@)
1629  *
1630  * GetScrollInfo can be used to retrieve the position, upper bound,
1631  * lower bound, and page size of a scrollbar control.
1632  *
1633  * PARAMS
1634  *  hwnd [I]  Handle of window with scrollbar(s)
1635  *  nBar [I]  One of SB_HORZ, SB_VERT, or SB_CTL
1636  *  info [IO] fMask specifies which values to retrieve
1637  *
1638  * RETURNS STD
1639  */
1640 BOOL WINAPI GetScrollInfo(HWND hwnd, INT nBar, LPSCROLLINFO info)
1641 {
1642     TRACE("hwnd=%p nBar=%d info=%p\n", hwnd, nBar, info);
1643
1644     /* Refer SB_CTL requests to the window */
1645     if (nBar == SB_CTL)
1646         SendMessageW(hwnd, SBM_GETSCROLLINFO, (WPARAM)0, (LPARAM)info);
1647     else
1648         SCROLL_GetScrollInfo(hwnd, nBar, info);
1649
1650     return TRUE;
1651 }
1652
1653
1654 /*************************************************************************
1655  *           SetScrollPos   (USER32.@)
1656  *
1657  * PARAMS
1658  *    hwnd    [I]  Handle of window with scrollbar(s)
1659  *    nBar    [I]  One of SB_HORZ, SB_VERT, or SB_CTL
1660  *    nPos    [I]  New value
1661  *    bRedraw [I]  Should scrollbar be redrawn afterwards?
1662  *
1663  * RETURNS
1664  *    Success: Scrollbar position
1665  *    Failure: 0
1666  *
1667  * REMARKS
1668  *    Note the ambiguity when 0 is returned.  Use GetLastError
1669  *    to make sure there was an error (and to know which one).
1670  */
1671 INT WINAPI SetScrollPos( HWND hwnd, INT nBar, INT nPos, BOOL bRedraw)
1672 {
1673     SCROLLINFO info;
1674     SCROLLBAR_INFO *infoPtr;
1675     INT oldPos;
1676
1677     if (!(infoPtr = SCROLL_GetScrollBarInfo( hwnd, nBar ))) return 0;
1678     oldPos      = infoPtr->curVal;
1679     info.cbSize = sizeof(info);
1680     info.nPos   = nPos;
1681     info.fMask  = SIF_POS;
1682     SetScrollInfo( hwnd, nBar, &info, bRedraw );
1683     return oldPos;
1684 }
1685
1686
1687 /*************************************************************************
1688  *           GetScrollPos   (USER32.@)
1689  *
1690  * PARAMS
1691  *    hwnd    [I]  Handle of window with scrollbar(s)
1692  *    nBar    [I]  One of SB_HORZ, SB_VERT, or SB_CTL
1693  *
1694  * RETURNS
1695  *    Success: Current position
1696  *    Failure: 0
1697  *
1698  * REMARKS
1699  *    There is ambiguity when 0 is returned.  Use GetLastError
1700  *    to make sure there was an error (and to know which one).
1701  */
1702 INT WINAPI GetScrollPos(HWND hwnd, INT nBar)
1703 {
1704     TRACE("hwnd=%p nBar=%d\n", hwnd, nBar);
1705
1706     /* Refer SB_CTL requests to the window */
1707     if (nBar == SB_CTL)
1708         return SendMessageW(hwnd, SBM_GETPOS, (WPARAM)0, (LPARAM)0);
1709     else
1710         return SCROLL_GetScrollPos(hwnd, nBar);
1711 }
1712
1713
1714 /*************************************************************************
1715  *           SetScrollRange   (USER32.@)
1716  *
1717  * PARAMS
1718  *    hwnd    [I]  Handle of window with scrollbar(s)
1719  *    nBar    [I]  One of SB_HORZ, SB_VERT, or SB_CTL
1720  *    minVal  [I]  New minimum value
1721  *    maxVal  [I]  New Maximum value
1722  *    bRedraw [I]  Should scrollbar be redrawn afterwards?
1723  *
1724  * RETURNS STD
1725  */
1726 BOOL WINAPI SetScrollRange(HWND hwnd, INT nBar, INT minVal, INT maxVal, BOOL bRedraw)
1727 {
1728     SCROLLINFO info;
1729  
1730     TRACE("hwnd=%p nBar=%d min=%d max=%d, bRedraw=%d\n", hwnd, nBar, minVal, maxVal, bRedraw);
1731  
1732     info.cbSize = sizeof(info);
1733     info.fMask  = SIF_RANGE;
1734     info.nMin   = minVal;
1735     info.nMax   = maxVal;
1736     SetScrollInfo( hwnd, nBar, &info, bRedraw );
1737     return TRUE;
1738 }
1739
1740
1741 /*************************************************************************
1742  *           SCROLL_SetNCSbState
1743  *
1744  * Updates both scrollbars at the same time. Used by MDI CalcChildScroll().
1745  */
1746 INT SCROLL_SetNCSbState(HWND hwnd, int vMin, int vMax, int vPos,
1747                         int hMin, int hMax, int hPos)
1748 {
1749     SCROLLINFO vInfo, hInfo;
1750
1751     vInfo.cbSize = hInfo.cbSize = sizeof(SCROLLINFO);
1752     vInfo.nMin   = vMin;
1753     vInfo.nMax   = vMax;
1754     vInfo.nPos   = vPos;
1755     hInfo.nMin   = hMin;
1756     hInfo.nMax   = hMax;
1757     hInfo.nPos   = hPos;
1758     vInfo.fMask  = hInfo.fMask = SIF_RANGE | SIF_POS;
1759
1760     SCROLL_SetScrollInfo( hwnd, SB_VERT, &vInfo, TRUE );
1761     SCROLL_SetScrollInfo( hwnd, SB_HORZ, &hInfo, TRUE );
1762
1763     return 0;
1764 }
1765
1766
1767 /*************************************************************************
1768  *           GetScrollRange   (USER32.@)
1769  *
1770  * PARAMS
1771  *    hwnd    [I]  Handle of window with scrollbar(s)
1772  *    nBar    [I]  One of SB_HORZ, SB_VERT, or SB_CTL
1773  *    lpMin   [O]  Where to store minimum value
1774  *    lpMax   [O]  Where to store maximum value
1775  *
1776  * RETURNS STD
1777  */
1778 BOOL WINAPI GetScrollRange(HWND hwnd, INT nBar, LPINT lpMin, LPINT lpMax)
1779 {
1780     TRACE("hwnd=%p nBar=%d lpMin=%p lpMax=%p\n", hwnd, nBar, lpMin, lpMax);
1781
1782     /* Refer SB_CTL requests to the window */
1783     if (nBar == SB_CTL)
1784         SendMessageW(hwnd, SBM_GETRANGE, (WPARAM)lpMin, (LPARAM)lpMax);
1785     else
1786         SCROLL_GetScrollRange(hwnd, nBar, lpMin, lpMax);
1787
1788     return TRUE;
1789 }
1790
1791
1792 /*************************************************************************
1793  *           SCROLL_ShowScrollBar()
1794  *
1795  * Back-end for ShowScrollBar(). Returns FALSE if no action was taken.
1796  */
1797 BOOL SCROLL_ShowScrollBar( HWND hwnd, INT nBar,
1798                              BOOL fShowH, BOOL fShowV )
1799 {
1800     LONG style = GetWindowLongW( hwnd, GWL_STYLE );
1801
1802     TRACE("hwnd=%p bar=%d horz=%d, vert=%d\n", hwnd, nBar, fShowH, fShowV );
1803
1804     switch(nBar)
1805     {
1806     case SB_CTL:
1807         ShowWindow( hwnd, fShowH ? SW_SHOW : SW_HIDE );
1808         return TRUE;
1809
1810     case SB_BOTH:
1811     case SB_HORZ:
1812         if (fShowH)
1813         {
1814             fShowH = !(style & WS_HSCROLL);
1815             style |= WS_HSCROLL;
1816         }
1817         else  /* hide it */
1818         {
1819             fShowH = (style & WS_HSCROLL);
1820             style &= ~WS_HSCROLL;
1821         }
1822         if( nBar == SB_HORZ ) {
1823             fShowV = FALSE;
1824             break;
1825         }
1826         /* fall through */
1827
1828     case SB_VERT:
1829         if (fShowV)
1830         {
1831             fShowV = !(style & WS_VSCROLL);
1832             style |= WS_VSCROLL;
1833         }
1834         else  /* hide it */
1835         {
1836             fShowV = (style & WS_VSCROLL);
1837             style &= ~WS_VSCROLL;
1838         }
1839         if ( nBar == SB_VERT )
1840            fShowH = FALSE;
1841         break;
1842
1843     default:
1844         return FALSE;  /* Nothing to do! */
1845     }
1846
1847     if( fShowH || fShowV ) /* frame has been changed, let the window redraw itself */
1848     {
1849         WIN_SetStyle( hwnd, style );
1850         SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE
1851                     | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
1852         return TRUE;
1853     }
1854     return FALSE; /* no frame changes */
1855 }
1856
1857
1858 /*************************************************************************
1859  *           ShowScrollBar   (USER32.@)
1860  *
1861  * PARAMS
1862  *    hwnd    [I]  Handle of window with scrollbar(s)
1863  *    nBar    [I]  One of SB_HORZ, SB_VERT, or SB_CTL
1864  *    fShow   [I]  TRUE = show, FALSE = hide
1865  *
1866  * RETURNS STD
1867  */
1868 BOOL WINAPI ShowScrollBar(HWND hwnd, INT nBar, BOOL fShow)
1869 {
1870     SCROLL_ShowScrollBar( hwnd, nBar, (nBar == SB_VERT) ? 0 : fShow,
1871                                       (nBar == SB_HORZ) ? 0 : fShow );
1872     return TRUE;
1873 }
1874
1875
1876 /*************************************************************************
1877  *           EnableScrollBar   (USER32.@)
1878  */
1879 BOOL WINAPI EnableScrollBar( HWND hwnd, INT nBar, UINT flags )
1880 {
1881     BOOL bFineWithMe;
1882     SCROLLBAR_INFO *infoPtr;
1883
1884     TRACE("%p %d %d\n", hwnd, nBar, flags );
1885
1886     flags &= ESB_DISABLE_BOTH;
1887
1888     if (nBar == SB_BOTH)
1889     {
1890         if (!(infoPtr = SCROLL_GetScrollBarInfo( hwnd, SB_VERT ))) return FALSE;
1891         if (!(bFineWithMe = (infoPtr->flags == flags)) )
1892         {
1893             infoPtr->flags = flags;
1894             SCROLL_RefreshScrollBar( hwnd, SB_VERT, TRUE, TRUE );
1895         }
1896         nBar = SB_HORZ;
1897     }
1898     else
1899         bFineWithMe = TRUE;
1900
1901     if (!(infoPtr = SCROLL_GetScrollBarInfo( hwnd, nBar ))) return FALSE;
1902     if (bFineWithMe && infoPtr->flags == flags) return FALSE;
1903     infoPtr->flags = flags;
1904
1905     SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, TRUE );
1906     return TRUE;
1907 }