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