1 /*
2  * ReactOS User32 Library
3  * - ScrollBar control
4  *
5  * Copyright 2001 Casper S. Hornstrup
6  * Copyright 2003 Thomas Weidenmueller
7  * Copyright 2003 Filip Navara
8  *
9  * Based on Wine code.
10  *
11  * Copyright 1993 Martin Ayotte
12  * Copyright 1994, 1996 Alexandre Julliard
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Lesser General Public
16  * License as published by the Free Software Foundation; either
17  * version 2.1 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
27  */
28 
29 #include <user32.h>
30 
31 WINE_DEFAULT_DEBUG_CHANNEL(scrollbar);
32 
33 /* Definitions for scrollbar hit testing [See SCROLLBARINFO in MSDN] */
34 #define SCROLL_NOWHERE		0x00    /* Outside the scroll bar */
35 #define SCROLL_TOP_ARROW	0x01    /* Top or left arrow */
36 #define SCROLL_TOP_RECT		0x02    /* Rectangle between the top arrow and the thumb */
37 #define SCROLL_THUMB		0x03    /* Thumb rectangle */
38 #define SCROLL_BOTTOM_RECT	0x04    /* Rectangle between the thumb and the bottom arrow */
39 #define SCROLL_BOTTOM_ARROW	0x05    /* Bottom or right arrow */
40 
41 #define SCROLL_FIRST_DELAY   200        /* Delay (in ms) before first repetition when
42                                            holding the button down */
43 #define SCROLL_REPEAT_DELAY  50         /* Delay (in ms) between scroll repetitions */
44 
45 #define SCROLL_TIMER   0                /* Scroll timer id */
46 
47   /* Minimum size of the rectangle between the arrows */
48 #define SCROLL_MIN_RECT  4
49 
50   /* Minimum size of the thumb in pixels */
51 #define SCROLL_MIN_THUMB 6
52 
53   /* Overlap between arrows and thumb */
54 #define SCROLL_ARROW_THUMB_OVERLAP 0
55 
56  /* Thumb-tracking info */
57 static HWND ScrollTrackingWin = 0;
58 static INT  ScrollTrackingBar = 0;
59 static INT  ScrollTrackingPos = 0;
60 static INT  ScrollTrackingVal = 0;
61  /* Hit test code of the last button-down event */
62 static DWORD ScrollTrackHitTest = SCROLL_NOWHERE;
63 static BOOL ScrollTrackVertical;
64 
65  /* Is the moving thumb being displayed? */
66 static BOOL ScrollMovingThumb = FALSE;
67 
68 HBRUSH DefWndControlColor(HDC hDC, UINT ctlType);
69 
70 UINT_PTR WINAPI SetSystemTimer(HWND,UINT_PTR,UINT,TIMERPROC);
71 BOOL WINAPI KillSystemTimer(HWND,UINT_PTR);
72 
73 /*********************************************************************
74  * scrollbar class descriptor
75  */
76 const struct builtin_class_descr SCROLL_builtin_class =
77 {
78     L"ScrollBar",           /* name */
79     CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC, /* style */
80     ScrollBarWndProcA,      /* procA */
81     ScrollBarWndProcW,      /* procW */
82     sizeof(SBWND)-sizeof(WND), /* extra */
83     IDC_ARROW,              /* cursor */
84     0                       /* brush */
85 };
86 
87 /* PRIVATE FUNCTIONS **********************************************************/
88 
89 static PSBDATA
90 IntGetSBData(PWND pwnd, INT Bar)
91 {
92    PSBWND pSBWnd;
93    PSBINFO pSBInfo;
94 
95    pSBInfo = DesktopPtrToUser(pwnd->pSBInfo);
96    switch (Bar)
97    {
98        case SB_HORZ:
99          return &pSBInfo->Horz;
100        case SB_VERT:
101          return &pSBInfo->Vert;
102        case SB_CTL:
103          if ( pwnd->cbwndExtra < (sizeof(SBWND)-sizeof(WND)) )
104          {
105             ERR("IntGetSBData Wrong Extra bytes for CTL Scrollbar!\n");
106             return 0;
107          }
108          pSBWnd = (PSBWND)pwnd;
109          return (PSBDATA)&pSBWnd->SBCalc;
110        default:
111             ERR("IntGetSBData Bad Bar!\n");
112    }
113    return NULL;
114 }
115 
116 static void
117 IntDrawScrollInterior(HWND hWnd, HDC hDC, INT nBar, BOOL Vertical,
118    PSCROLLBARINFO ScrollBarInfo)
119 {
120    INT ThumbSize = ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop;
121    INT ThumbTop = ScrollBarInfo->xyThumbTop;
122    RECT Rect;
123    HBRUSH hSaveBrush, hBrush;
124    BOOL TopSelected = FALSE, BottomSelected = FALSE;
125 
126    if (ScrollBarInfo->rgstate[SCROLL_TOP_RECT] & STATE_SYSTEM_PRESSED)
127       TopSelected = TRUE;
128    if (ScrollBarInfo->rgstate[SCROLL_BOTTOM_RECT] & STATE_SYSTEM_PRESSED)
129       BottomSelected = TRUE;
130 
131    /*
132     * Only scrollbar controls send WM_CTLCOLORSCROLLBAR.
133     * The window-owned scrollbars need to call DefWndControlColor
134     * to correctly setup default scrollbar colors
135     */
136    if (nBar == SB_CTL)
137    {
138       hBrush = GetControlBrush( hWnd, hDC, WM_CTLCOLORSCROLLBAR);
139       if (!hBrush)
140          hBrush = GetSysColorBrush(COLOR_SCROLLBAR);
141    }
142    else
143    {
144       hBrush = DefWndControlColor(hDC, CTLCOLOR_SCROLLBAR);
145    }
146 
147    hSaveBrush = SelectObject(hDC, hBrush);
148 
149    /* Calculate the scroll rectangle */
150    if (Vertical)
151    {
152       Rect.top = ScrollBarInfo->rcScrollBar.top + ScrollBarInfo->dxyLineButton;
153       Rect.bottom = ScrollBarInfo->rcScrollBar.bottom - ScrollBarInfo->dxyLineButton;
154       Rect.left = ScrollBarInfo->rcScrollBar.left;
155       Rect.right = ScrollBarInfo->rcScrollBar.right;
156    }
157    else
158    {
159       Rect.top = ScrollBarInfo->rcScrollBar.top;
160       Rect.bottom = ScrollBarInfo->rcScrollBar.bottom;
161       Rect.left = ScrollBarInfo->rcScrollBar.left + ScrollBarInfo->dxyLineButton;
162       Rect.right = ScrollBarInfo->rcScrollBar.right - ScrollBarInfo->dxyLineButton;
163    }
164 
165    /* Draw the scroll rectangles and thumb */
166    if (!ScrollBarInfo->xyThumbBottom)
167    {
168       PatBlt(hDC, Rect.left, Rect.top, Rect.right - Rect.left,
169          Rect.bottom - Rect.top, PATCOPY);
170 
171       /* Cleanup and return */
172       SelectObject(hDC, hSaveBrush);
173       return;
174    }
175 
176    ThumbTop -= ScrollBarInfo->dxyLineButton;
177 
178    if (ScrollBarInfo->dxyLineButton)
179    {
180       if (Vertical)
181       {
182          if (ThumbSize)
183          {
184             PatBlt(hDC, Rect.left, Rect.top, Rect.right - Rect.left,
185                    ThumbTop, TopSelected ? BLACKNESS : PATCOPY);
186             Rect.top += ThumbTop;
187             PatBlt(hDC, Rect.left, Rect.top + ThumbSize, Rect.right - Rect.left,
188                Rect.bottom - Rect.top - ThumbSize, BottomSelected ? BLACKNESS : PATCOPY);
189             Rect.bottom = Rect.top + ThumbSize;
190          }
191          else
192          {
193             if (ThumbTop)
194             {
195                PatBlt(hDC, Rect.left, ScrollBarInfo->dxyLineButton,
196                   Rect.right - Rect.left, Rect.bottom - Rect.top, PATCOPY);
197             }
198          }
199       }
200       else
201       {
202          if (ThumbSize)
203          {
204             PatBlt(hDC, Rect.left, Rect.top, ThumbTop,
205                Rect.bottom - Rect.top, TopSelected ? BLACKNESS : PATCOPY);
206             Rect.left += ThumbTop;
207             PatBlt(hDC, Rect.left + ThumbSize, Rect.top,
208                Rect.right - Rect.left - ThumbSize, Rect.bottom - Rect.top,
209                BottomSelected ? BLACKNESS : PATCOPY);
210             Rect.right = Rect.left + ThumbSize;
211          }
212          else
213          {
214             if (ThumbTop)
215             {
216                PatBlt(hDC, ScrollBarInfo->dxyLineButton, Rect.top,
217                   Rect.right - Rect.left, Rect.bottom - Rect.top, PATCOPY);
218             }
219          }
220       }
221    }
222 
223    /* Draw the thumb */
224    if (ThumbSize)
225       DrawEdge(hDC, &Rect, EDGE_RAISED, BF_RECT | BF_MIDDLE);
226 
227    /* Cleanup */
228    SelectObject(hDC, hSaveBrush);
229 }
230 
231 static VOID FASTCALL
232 IntDrawScrollArrows(HDC hDC, PSCROLLBARINFO ScrollBarInfo, BOOL Vertical)
233 {
234   RECT RectLT, RectRB;
235   INT ScrollDirFlagLT, ScrollDirFlagRB;
236 
237   RectLT = RectRB = ScrollBarInfo->rcScrollBar;
238   if (Vertical)
239     {
240       ScrollDirFlagLT = DFCS_SCROLLUP;
241       ScrollDirFlagRB = DFCS_SCROLLDOWN;
242       RectLT.bottom = RectLT.top + ScrollBarInfo->dxyLineButton;
243       RectRB.top = RectRB.bottom - ScrollBarInfo->dxyLineButton;
244     }
245    else
246     {
247       ScrollDirFlagLT = DFCS_SCROLLLEFT;
248       ScrollDirFlagRB = DFCS_SCROLLRIGHT;
249       RectLT.right = RectLT.left + ScrollBarInfo->dxyLineButton;
250       RectRB.left = RectRB.right - ScrollBarInfo->dxyLineButton;
251     }
252 
253    if (ScrollBarInfo->rgstate[SCROLL_TOP_ARROW] & STATE_SYSTEM_PRESSED)
254      {
255        ScrollDirFlagLT |= DFCS_PUSHED | DFCS_FLAT;
256      }
257    if (ScrollBarInfo->rgstate[SCROLL_TOP_ARROW] & STATE_SYSTEM_UNAVAILABLE)
258      {
259        ScrollDirFlagLT |= DFCS_INACTIVE;
260      }
261    if (ScrollBarInfo->rgstate[SCROLL_BOTTOM_ARROW] & STATE_SYSTEM_PRESSED)
262      {
263        ScrollDirFlagRB |= DFCS_PUSHED | DFCS_FLAT;
264      }
265    if (ScrollBarInfo->rgstate[SCROLL_BOTTOM_ARROW] & STATE_SYSTEM_UNAVAILABLE)
266      {
267        ScrollDirFlagRB |= DFCS_INACTIVE;
268      }
269 
270    DrawFrameControl(hDC, &RectLT, DFC_SCROLL, ScrollDirFlagLT);
271    DrawFrameControl(hDC, &RectRB, DFC_SCROLL, ScrollDirFlagRB);
272 }
273 
274 static VOID FASTCALL
275 IntScrollDrawMovingThumb(HDC Dc, PSCROLLBARINFO ScrollBarInfo, BOOL Vertical)
276 {
277   INT Pos = ScrollTrackingPos;
278   INT MaxSize;
279   INT OldTop;
280 
281   if (Vertical)
282       MaxSize = ScrollBarInfo->rcScrollBar.bottom - ScrollBarInfo->rcScrollBar.top;
283   else
284       MaxSize = ScrollBarInfo->rcScrollBar.right - ScrollBarInfo->rcScrollBar.left;
285 
286   MaxSize -= ScrollBarInfo->dxyLineButton + ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop;
287 
288   if (Pos < ScrollBarInfo->dxyLineButton)
289       Pos = ScrollBarInfo->dxyLineButton;
290   else if (MaxSize < Pos)
291       Pos = MaxSize;
292 
293   OldTop = ScrollBarInfo->xyThumbTop;
294   ScrollBarInfo->xyThumbBottom = Pos + ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop;
295   ScrollBarInfo->xyThumbTop = Pos;
296   IntDrawScrollInterior(ScrollTrackingWin, Dc, ScrollTrackingBar, Vertical, ScrollBarInfo);
297   ScrollBarInfo->xyThumbBottom = OldTop + ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop;
298   ScrollBarInfo->xyThumbTop = OldTop;
299 
300   ScrollMovingThumb = !ScrollMovingThumb;
301 }
302 
303 static LONG FASTCALL
304 IntScrollGetObjectId(INT SBType)
305 {
306   if (SBType == SB_VERT)
307       return OBJID_VSCROLL;
308   if (SBType == SB_HORZ)
309       return OBJID_HSCROLL;
310   return OBJID_CLIENT;
311 }
312 
313 static BOOL FASTCALL
314 IntGetScrollBarInfo(HWND Wnd, INT Bar, PSCROLLBARINFO ScrollBarInfo)
315 {
316   ScrollBarInfo->cbSize = sizeof(SCROLLBARINFO);
317 
318   return NtUserGetScrollBarInfo(Wnd, IntScrollGetObjectId(Bar), ScrollBarInfo);
319 }
320 
321 static VOID FASTCALL
322 IntUpdateScrollArrows(HWND Wnd, HDC hDC, PSCROLLBARINFO ScrollBarInfo,
323                       SETSCROLLBARINFO *info, INT SBType, INT Arrow,
324                       BOOL Vertical, BOOL Pressed)
325 {
326    if (Pressed)
327    {
328       ScrollBarInfo->rgstate[Arrow] |= STATE_SYSTEM_PRESSED;
329    }
330    else
331    {
332       ScrollBarInfo->rgstate[Arrow] &= ~STATE_SYSTEM_PRESSED;
333    }
334    /* Update arrow state */
335    info->rgstate[Arrow] = ScrollBarInfo->rgstate[Arrow];
336    NtUserSetScrollBarInfo(Wnd, IntScrollGetObjectId(SBType), info);
337 
338    IntDrawScrollArrows(hDC, ScrollBarInfo, Vertical);
339 }
340 
341 void
342 IntDrawScrollBar(HWND Wnd, HDC DC, INT Bar)
343 {
344   //PSBWND pSBWnd;
345   //INT ThumbSize;
346   SCROLLBARINFO Info;
347   BOOL Vertical;
348 
349   /*
350    * Get scroll bar info.
351    */
352   switch (Bar)
353     {
354       case SB_HORZ:
355         Vertical = FALSE;
356         break;
357 
358       case SB_VERT:
359         Vertical = TRUE;
360         break;
361 
362       case SB_CTL:
363         Vertical = (GetWindowLongPtrW(Wnd, GWL_STYLE) & SBS_VERT) != 0;
364         break;
365 
366       default:
367         return;
368     }
369   if (!IntGetScrollBarInfo(Wnd, Bar, &Info))
370   {
371       return;
372   }
373 
374   if (IsRectEmpty(&Info.rcScrollBar))
375   {
376       return;
377   }
378 
379   //ThumbSize = pSBWnd->pSBCalc->pxThumbBottom - pSBWnd->pSBCalc->pxThumbTop;
380 
381   /*
382    * Draw the arrows.
383    */
384   if (Info.dxyLineButton)
385   {
386       IntDrawScrollArrows(DC, &Info, Vertical);
387   }
388 
389   /*
390    * Draw the interior.
391    */
392   IntDrawScrollInterior(Wnd, DC, Bar, Vertical, &Info);
393 
394   /*
395    * If scroll bar has focus, reposition the caret.
396    */
397   if (Wnd == GetFocus() && SB_CTL == Bar)
398   {
399       if (Vertical)
400       {
401           SetCaretPos(Info.rcScrollBar.top + 1, Info.dxyLineButton + 1);
402       }
403       else
404       {
405            SetCaretPos(Info.dxyLineButton + 1, Info.rcScrollBar.top + 1);
406       }
407   }
408 }
409 
410 static BOOL FASTCALL
411 IntScrollPtInRectEx(LPRECT Rect, POINT Pt, BOOL Vertical)
412 {
413   RECT TempRect = *Rect;
414   int scrollbarWidth;
415 
416   /* Pad hit rect to allow mouse to be dragged outside of scrollbar and
417    * still be considered in the scrollbar. */
418   if (Vertical)
419   {
420       scrollbarWidth = Rect->right - Rect->left;
421       TempRect.left -= scrollbarWidth*8;
422       TempRect.right += scrollbarWidth*8;
423       TempRect.top -= scrollbarWidth*2;
424       TempRect.bottom += scrollbarWidth*2;
425   }
426   else
427   {
428       scrollbarWidth = Rect->bottom - Rect->top;
429       TempRect.left -= scrollbarWidth*2;
430       TempRect.right += scrollbarWidth*2;
431       TempRect.top -= scrollbarWidth*8;
432       TempRect.bottom += scrollbarWidth*8;
433   }
434 
435   return PtInRect(&TempRect, Pt);
436 }
437 
438 static DWORD FASTCALL
439 IntScrollHitTest(PSCROLLBARINFO ScrollBarInfo, BOOL Vertical, POINT Pt, BOOL Dragging)
440 {
441   INT ArrowSize, ThumbSize, ThumbPos;
442 
443   if ((Dragging && ! IntScrollPtInRectEx(&ScrollBarInfo->rcScrollBar, Pt, Vertical)) ||
444       ! PtInRect(&ScrollBarInfo->rcScrollBar, Pt)) return SCROLL_NOWHERE;
445 
446   ThumbPos = ScrollBarInfo->xyThumbTop;
447   ThumbSize = ScrollBarInfo->xyThumbBottom - ThumbPos;
448   ArrowSize = ScrollBarInfo->dxyLineButton;
449 
450   if (Vertical)
451   {
452       if (Pt.y < ScrollBarInfo->rcScrollBar.top + ArrowSize) return SCROLL_TOP_ARROW;
453       if (Pt.y >= ScrollBarInfo->rcScrollBar.bottom - ArrowSize) return SCROLL_BOTTOM_ARROW;
454       if (!ThumbPos) return SCROLL_TOP_RECT;
455       Pt.y -= ScrollBarInfo->rcScrollBar.top;
456       if (Pt.y < ThumbPos) return SCROLL_TOP_RECT;
457       if (Pt.y >= ThumbPos + ThumbSize) return SCROLL_BOTTOM_RECT;
458   }
459   else
460   {
461       if (Pt.x < ScrollBarInfo->rcScrollBar.left + ArrowSize) return SCROLL_TOP_ARROW;
462       if (Pt.x >= ScrollBarInfo->rcScrollBar.right - ArrowSize) return SCROLL_BOTTOM_ARROW;
463       if (!ThumbPos) return SCROLL_TOP_RECT;
464       Pt.x -= ScrollBarInfo->rcScrollBar.left;
465       if (Pt.x < ThumbPos) return SCROLL_TOP_RECT;
466       if (Pt.x >= ThumbPos + ThumbSize) return SCROLL_BOTTOM_RECT;
467   }
468 
469   return SCROLL_THUMB;
470 }
471 
472 
473 /***********************************************************************
474  *           IntScrollGetScrollBarRect
475  *
476  * Compute the scroll bar rectangle, in drawing coordinates (i.e. client
477  * coords for SB_CTL, window coords for SB_VERT and SB_HORZ).
478  * 'arrowSize' returns the width or height of an arrow (depending on
479  * the orientation of the scrollbar), 'thumbSize' returns the size of
480  * the thumb, and 'thumbPos' returns the position of the thumb
481  * relative to the left or to the top.
482  * Return TRUE if the scrollbar is vertical, FALSE if horizontal.
483  */
484 static BOOL FASTCALL
485 IntScrollGetScrollBarRect(HWND Wnd, INT Bar, RECT *Rect,
486                           INT *ArrowSize, INT *ThumbSize,
487                           INT *ThumbPos)
488 {
489   INT Pixels;
490   BOOL Vertical;
491   PWND pWnd;
492   PSBINFO pSBInfo;
493   PSBDATA pSBData;
494   PSBWND pSBWnd;
495 
496   pWnd = ValidateHwnd( Wnd );
497   if (!pWnd) return FALSE;
498   pSBInfo = DesktopPtrToUser(pWnd->pSBInfo);
499 
500   *Rect = pWnd->rcClient;
501   OffsetRect( Rect, -pWnd->rcWindow.left, -pWnd->rcWindow.top );
502   if (pWnd->ExStyle & WS_EX_LAYOUTRTL)
503      mirror_rect( &pWnd->rcWindow, Rect );
504 
505   switch (Bar)
506     {
507       case SB_HORZ:
508 //        WIN_GetRectangles( Wnd, COORDS_WINDOW, NULL, Rect );
509         Rect->top = Rect->bottom;
510         Rect->bottom += GetSystemMetrics(SM_CYHSCROLL);
511 	if (pWnd->style & WS_BORDER)
512         {
513             Rect->left--;
514             Rect->right++;
515 	}
516         else if (pWnd->style & WS_VSCROLL)
517         {
518             Rect->right++;
519         }
520         Vertical = FALSE;
521         pSBData = &pSBInfo->Horz;
522 	break;
523 
524       case SB_VERT:
525 //        WIN_GetRectangles( Wnd, COORDS_WINDOW, NULL, Rect );
526         if (pWnd->ExStyle & WS_EX_LEFTSCROLLBAR)
527         {
528             Rect->right = Rect->left;
529             Rect->left -= GetSystemMetrics(SM_CXVSCROLL);
530         }
531         else
532         {
533             Rect->left = Rect->right;
534             Rect->right += GetSystemMetrics(SM_CXVSCROLL);
535         }
536 	if (pWnd->style & WS_BORDER)
537         {
538             Rect->top--;
539             Rect->bottom++;
540         }
541         else if (pWnd->style & WS_HSCROLL)
542         {
543             Rect->bottom++;
544         }
545         Vertical = TRUE;
546         pSBData = &pSBInfo->Vert;
547 	break;
548 
549       case SB_CTL:
550         GetClientRect( Wnd, Rect );
551         Vertical = (pWnd->style & SBS_VERT);
552         pSBWnd = (PSBWND)pWnd;
553         pSBData = (PSBDATA)&pSBWnd->SBCalc;
554 	break;
555 
556       default:
557         return FALSE;
558     }
559 
560   if (Vertical) Pixels = Rect->bottom - Rect->top;
561   else Pixels = Rect->right - Rect->left;
562 
563   if (Pixels <= 2 * GetSystemMetrics(SM_CXVSCROLL) + SCROLL_MIN_RECT)
564   {
565       if (SCROLL_MIN_RECT < Pixels)
566           *ArrowSize = (Pixels - SCROLL_MIN_RECT) / 2;
567       else
568           *ArrowSize = 0;
569       *ThumbPos = *ThumbSize = 0;
570   }
571   else
572   {
573       *ArrowSize = GetSystemMetrics(SM_CXVSCROLL);
574       Pixels -= (2 * (GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP));
575       if (pSBData->page)
576       {
577           *ThumbSize = MulDiv(Pixels, pSBData->page, (pSBData->posMax - pSBData->posMin + 1));
578           if (*ThumbSize < SCROLL_MIN_THUMB) *ThumbSize = SCROLL_MIN_THUMB;
579       }
580       else *ThumbSize = GetSystemMetrics(SM_CXVSCROLL);
581 
582       if (((Pixels -= *ThumbSize ) < 0) ||
583           (( pSBInfo->WSBflags & ESB_DISABLE_BOTH) == ESB_DISABLE_BOTH))
584       {
585           /* Rectangle too small or scrollbar disabled -> no thumb */
586           *ThumbPos = *ThumbSize = 0;
587       }
588       else
589       {
590           INT Max = pSBData->posMax - max(pSBData->page - 1, 0);
591           if (pSBData->posMin >= Max)
592               *ThumbPos = *ArrowSize - SCROLL_ARROW_THUMB_OVERLAP;
593           else
594               *ThumbPos = *ArrowSize - SCROLL_ARROW_THUMB_OVERLAP
595               + MulDiv(Pixels, (pSBData->pos - pSBData->posMin),(Max - pSBData->posMin));
596       }
597   }
598   return Vertical;
599 }
600 
601 /***********************************************************************
602  *           IntScrollGetThumbVal
603  *
604  * Compute the current scroll position based on the thumb position in pixels
605  * from the top of the scroll-bar.
606  */
607 static UINT FASTCALL
608 IntScrollGetThumbVal(HWND Wnd, INT SBType, PSCROLLBARINFO ScrollBarInfo,
609                      BOOL Vertical, INT Pos)
610 {
611   PWND pWnd;
612   PSBDATA pSBData;
613   INT Pixels = Vertical ? ScrollBarInfo->rcScrollBar.bottom
614                           - ScrollBarInfo->rcScrollBar.top
615                         : ScrollBarInfo->rcScrollBar.right
616                           - ScrollBarInfo->rcScrollBar.left;
617 
618   pWnd = ValidateHwnd( Wnd );
619   if (!pWnd) return FALSE;
620 
621   pSBData = IntGetSBData(pWnd, SBType);
622 
623   if ((Pixels -= 2 * ScrollBarInfo->dxyLineButton) <= 0)
624   {
625       return pSBData->posMin;
626   }
627 
628   if ((Pixels -= (ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop)) <= 0)
629   {
630       return pSBData->posMin;
631   }
632 
633   Pos = Pos - ScrollBarInfo->dxyLineButton;
634   if (Pos < 0)
635   {
636       Pos = 0;
637   }
638   if (Pos > Pixels) Pos = Pixels;
639 
640   if (!pSBData->page)
641       Pos *= pSBData->posMax - pSBData->posMin;
642   else
643       Pos *= pSBData->posMax - pSBData->posMin - pSBData->page + 1;
644 
645   return pSBData->posMin + ((Pos + Pixels / 2) / Pixels);
646 }
647 
648 /***********************************************************************
649  *           IntScrollClipPos
650  */
651 static POINT IntScrollClipPos(PRECT lpRect, POINT pt)
652 {
653     if( pt.x < lpRect->left )
654         pt.x = lpRect->left;
655     else
656     if( pt.x > lpRect->right )
657         pt.x = lpRect->right;
658 
659     if( pt.y < lpRect->top )
660         pt.y = lpRect->top;
661     else
662     if( pt.y > lpRect->bottom )
663         pt.y = lpRect->bottom;
664 
665     return pt;
666 }
667 
668 /***********************************************************************
669  *           IntScrollDrawSizeGrip
670  *
671  *  Draw the size grip.
672  */
673 static void FASTCALL
674 IntScrollDrawSizeGrip(HWND Wnd, HDC Dc)
675 {
676   RECT Rect;
677 
678   GetClientRect(Wnd, &Rect);
679   FillRect(Dc, &Rect, GetSysColorBrush(COLOR_SCROLLBAR));
680   Rect.left = max(Rect.left, Rect.right - GetSystemMetrics(SM_CXVSCROLL) - 1);
681   Rect.top  = max(Rect.top, Rect.bottom - GetSystemMetrics(SM_CYHSCROLL) - 1);
682   DrawFrameControl(Dc, &Rect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
683 }
684 
685 /***********************************************************************
686  *           SCROLL_RefreshScrollBar
687  *
688  * Repaint the scroll bar interior after a SetScrollRange() or
689  * SetScrollPos() call.
690  */
691 static void SCROLL_RefreshScrollBar( HWND hwnd, INT nBar,
692 				     BOOL arrows, BOOL interior )
693 {
694     HDC hdc = GetDCEx( hwnd, 0,
695                            DCX_CACHE | ((nBar == SB_CTL) ? 0 : DCX_WINDOW) );
696     if (!hdc) return;
697 
698     IntDrawScrollBar( hwnd, hdc, nBar);//, arrows, interior );
699     ReleaseDC( hwnd, hdc );
700 }
701 
702 
703 /***********************************************************************
704  *           IntScrollHandleKbdEvent
705  *
706  * Handle a keyboard event (only for SB_CTL scrollbars with focus).
707  */
708 static void FASTCALL
709 IntScrollHandleKbdEvent(
710   HWND Wnd /* [in] Handle of window with scrollbar(s) */,
711   WPARAM wParam /* [in] Variable input including enable state */,
712   LPARAM lParam /* [in] Variable input including input point */)
713 {
714   TRACE("Wnd=%p wParam=%ld lParam=%ld\n", Wnd, wParam, lParam);
715 
716   /* hide caret on first KEYDOWN to prevent flicker */
717   if (0 == (lParam & PFD_DOUBLEBUFFER_DONTCARE))
718     {
719       HideCaret(Wnd);
720     }
721 
722   switch(wParam)
723     {
724       case VK_PRIOR:
725         wParam = SB_PAGEUP;
726         break;
727 
728       case VK_NEXT:
729         wParam = SB_PAGEDOWN;
730         break;
731 
732       case VK_HOME:
733         wParam = SB_TOP;
734         break;
735 
736       case VK_END:
737         wParam = SB_BOTTOM;
738         break;
739 
740       case VK_UP:
741         wParam = SB_LINEUP;
742         break;
743 
744       case VK_DOWN:
745         wParam = SB_LINEDOWN;
746         break;
747 
748       default:
749         return;
750     }
751 
752   SendMessageW(GetParent(Wnd),
753                (0 != (GetWindowLongPtrW(Wnd, GWL_STYLE ) & SBS_VERT) ?
754                 WM_VSCROLL : WM_HSCROLL), wParam, (LPARAM) Wnd);
755 }
756 
757 /***********************************************************************
758  *           IntScrollHandleScrollEvent
759  *
760  * Handle a mouse or timer event for the scrollbar.
761  * 'Pt' is the location of the mouse event in drawing coordinates
762  */
763 static VOID FASTCALL
764 IntScrollHandleScrollEvent(HWND Wnd, INT SBType, UINT Msg, POINT Pt)
765 {
766   static POINT PrevPt;           /* Previous mouse position for timer events */
767   static UINT TrackThumbPos;     /* Thumb position when tracking started. */
768   static INT LastClickPos;       /* Position in the scroll-bar of the last
769                                     button-down event. */
770   static INT LastMousePos;       /* Position in the scroll-bar of the last
771                                     mouse event. */
772 
773   DWORD HitTest;
774   HWND WndOwner, WndCtl;
775   BOOL Vertical;
776   HDC Dc;
777   SCROLLBARINFO ScrollBarInfo;
778   SETSCROLLBARINFO NewInfo;
779 
780   if (! IntGetScrollBarInfo(Wnd, SBType, &ScrollBarInfo))
781   {
782      return;
783   }
784   if ((ScrollTrackHitTest == SCROLL_NOWHERE) && (Msg != WM_LBUTTONDOWN))
785   {
786      //// ReactOS : Justin Case something goes wrong.
787      if (Wnd == GetCapture())
788      {
789         ReleaseCapture();
790      }
791      ////
792      return;
793   }
794 
795   NewInfo.nTrackPos = ScrollTrackingVal;
796   NewInfo.reserved = ScrollBarInfo.reserved;
797   memcpy(NewInfo.rgstate, ScrollBarInfo.rgstate, (CCHILDREN_SCROLLBAR + 1) * sizeof(DWORD));
798 
799   if (SBType == SB_CTL && (GetWindowLongPtrW(Wnd, GWL_STYLE) & (SBS_SIZEGRIP | SBS_SIZEBOX)))
800   {
801       switch(Msg)
802       {
803           case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
804             HideCaret(Wnd);  /* hide caret while holding down LBUTTON */
805             SetCapture(Wnd);
806             PrevPt = Pt;
807             ScrollTrackHitTest = HitTest = SCROLL_THUMB;
808             break;
809           case WM_MOUSEMOVE:
810             GetClientRect(GetParent(GetParent(Wnd)), &ScrollBarInfo.rcScrollBar);
811             PrevPt = Pt;
812             break;
813           case WM_LBUTTONUP:
814             ReleaseCapture();
815             ScrollTrackHitTest = HitTest = SCROLL_NOWHERE;
816             if (Wnd == GetFocus()) ShowCaret(Wnd);
817             break;
818           case WM_SYSTIMER:
819             Pt = PrevPt;
820             break;
821       }
822       return;
823   }
824 
825   Dc = GetDCEx(Wnd, 0, DCX_CACHE | ((SB_CTL == SBType) ? 0 : DCX_WINDOW));
826   if (SB_VERT == SBType)
827   {
828       Vertical = TRUE;
829   }
830   else if (SB_HORZ == SBType)
831   {
832       Vertical = FALSE;
833   }
834   else
835   {
836       Vertical = (0 != (GetWindowLongPtrW(Wnd, GWL_STYLE) & SBS_VERT));
837   }
838   WndOwner = (SB_CTL == SBType) ? GetParent(Wnd) : Wnd;
839   WndCtl   = (SB_CTL == SBType) ? Wnd : NULL;
840 
841   switch (Msg)
842   {
843       case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
844         HideCaret(Wnd);     /* hide caret while holding down LBUTTON */
845         ScrollTrackVertical = Vertical;
846         ScrollTrackHitTest  = HitTest = IntScrollHitTest(&ScrollBarInfo, Vertical, Pt, FALSE );
847         LastClickPos  = Vertical ? (Pt.y - ScrollBarInfo.rcScrollBar.top)
848                         : (Pt.x - ScrollBarInfo.rcScrollBar.left);
849         LastMousePos  = LastClickPos;
850         TrackThumbPos = ScrollBarInfo.xyThumbTop;
851         PrevPt = Pt;
852         if (SBType == SB_CTL && (GetWindowLongPtrW(Wnd, GWL_STYLE) & WS_TABSTOP)) SetFocus(Wnd);
853         SetCapture(Wnd);
854         /* Don't update scrollbar if disabled. */
855         if (ScrollBarInfo.rgstate[ScrollTrackHitTest] != STATE_SYSTEM_UNAVAILABLE)
856         {
857             IntUpdateScrollArrows (Wnd, Dc, &ScrollBarInfo, &NewInfo, SBType, ScrollTrackHitTest, Vertical, TRUE);
858         }
859         break;
860 
861       case WM_MOUSEMOVE:
862         HitTest = IntScrollHitTest(&ScrollBarInfo, Vertical, Pt, TRUE);
863         PrevPt = Pt;
864         break;
865 
866       case WM_LBUTTONUP:
867         HitTest = SCROLL_NOWHERE;
868         ReleaseCapture();
869         /* if scrollbar has focus, show back caret */
870         if (Wnd == GetFocus()) ShowCaret(Wnd);
871         /* Don't update scrollbar if disabled. */
872         if (ScrollBarInfo.rgstate[ScrollTrackHitTest] != STATE_SYSTEM_UNAVAILABLE)
873         {
874             IntUpdateScrollArrows (Wnd, Dc, &ScrollBarInfo, &NewInfo, SBType, ScrollTrackHitTest, Vertical, FALSE);
875             IntDrawScrollInterior(Wnd,Dc,SBType,Vertical,&ScrollBarInfo);
876         }
877         break;
878 
879       case WM_SYSTIMER:
880         Pt = PrevPt;
881         HitTest = IntScrollHitTest(&ScrollBarInfo, Vertical, Pt, FALSE);
882         break;
883 
884       default:
885           return;  /* Should never happen */
886   }
887 
888   TRACE("Event: hwnd=%p bar=%d msg=%s pt=%d,%d hit=%d\n",
889             Wnd, SBType, SPY_GetMsgName(Msg,Wnd), Pt.x, Pt.y, HitTest );
890 
891   switch (ScrollTrackHitTest)
892     {
893       case SCROLL_NOWHERE:  /* No tracking in progress */
894         break;
895 
896       case SCROLL_TOP_ARROW:
897         if (HitTest == ScrollTrackHitTest)
898           {
899             if ((WM_LBUTTONDOWN == Msg) || (WM_SYSTIMER == Msg))
900               {
901                 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
902                              SB_LINEUP, (LPARAM) WndCtl);
903               }
904 	    SetSystemTimer(Wnd, SCROLL_TIMER, (WM_LBUTTONDOWN == Msg) ?
905                            SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
906                            (TIMERPROC) NULL);
907             if (ScrollBarInfo.rgstate[ScrollTrackHitTest] != STATE_SYSTEM_UNAVAILABLE)
908             {
909                if (!(ScrollBarInfo.rgstate[ScrollTrackHitTest] &= STATE_SYSTEM_PRESSED))
910                {
911                   IntUpdateScrollArrows (Wnd, Dc, &ScrollBarInfo, &NewInfo, SBType, ScrollTrackHitTest, Vertical, TRUE);
912                }
913             }
914           }
915         else
916           {
917             IntUpdateScrollArrows (Wnd, Dc, &ScrollBarInfo, &NewInfo, SBType, ScrollTrackHitTest, Vertical, FALSE);
918             KillSystemTimer(Wnd, SCROLL_TIMER);
919           }
920         break;
921 
922       case SCROLL_TOP_RECT:
923         if (HitTest == ScrollTrackHitTest)
924           {
925             if ((WM_LBUTTONDOWN == Msg) || (WM_SYSTIMER == Msg))
926               {
927                 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
928                              SB_PAGEUP, (LPARAM) WndCtl);
929               }
930             SetSystemTimer(Wnd, SCROLL_TIMER, (WM_LBUTTONDOWN == Msg) ?
931                            SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
932                            (TIMERPROC) NULL);
933           }
934         else
935           {
936             KillSystemTimer(Wnd, SCROLL_TIMER);
937           }
938         break;
939 
940       case SCROLL_THUMB:
941         if (Msg == WM_LBUTTONDOWN)
942           {
943             ScrollTrackingWin = Wnd;
944             ScrollTrackingBar = SBType;
945             ScrollTrackingPos = TrackThumbPos + LastMousePos - LastClickPos;
946             ScrollTrackingVal = IntScrollGetThumbVal(Wnd, SBType, &ScrollBarInfo,
947                                                      Vertical, ScrollTrackingPos);
948             NewInfo.nTrackPos = ScrollTrackingVal;
949             NtUserSetScrollBarInfo(Wnd, IntScrollGetObjectId(SBType), &NewInfo);
950             IntScrollDrawMovingThumb(Dc, &ScrollBarInfo, Vertical);
951           }
952         else if (Msg == WM_LBUTTONUP)
953           {
954             ScrollTrackingWin = 0;
955             ScrollTrackingVal = 0;
956             IntDrawScrollInterior(Wnd, Dc, SBType, Vertical, &ScrollBarInfo);
957           }
958         else  /* WM_MOUSEMOVE */
959           {
960             UINT Pos;
961 
962             if (! IntScrollPtInRectEx(&ScrollBarInfo.rcScrollBar, Pt, Vertical))
963               {
964                 Pos = LastClickPos;
965               }
966             else
967               {
968                 Pt = IntScrollClipPos(&ScrollBarInfo.rcScrollBar, Pt);
969 		Pos = Vertical ? (Pt.y - ScrollBarInfo.rcScrollBar.top)
970                                : (Pt.x - ScrollBarInfo.rcScrollBar.left);
971               }
972             if (Pos != LastMousePos || ! ScrollMovingThumb)
973               {
974                 LastMousePos = Pos;
975                 ScrollTrackingPos = TrackThumbPos + Pos - LastClickPos;
976                 ScrollTrackingVal = IntScrollGetThumbVal(Wnd, SBType, &ScrollBarInfo,
977                                                          Vertical, ScrollTrackingPos);
978                 NewInfo.nTrackPos = ScrollTrackingVal;
979                 NtUserSetScrollBarInfo(Wnd, IntScrollGetObjectId(SBType), &NewInfo);
980                 IntScrollDrawMovingThumb(Dc, &ScrollBarInfo, Vertical);
981                 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
982                              MAKEWPARAM(SB_THUMBTRACK, ScrollTrackingVal),
983                              (LPARAM) WndCtl);
984              }
985         }
986         break;
987 
988       case SCROLL_BOTTOM_RECT:
989         if (HitTest == ScrollTrackHitTest)
990           {
991             if ((Msg == WM_LBUTTONDOWN) || (Msg == WM_SYSTIMER))
992               {
993                 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
994                              SB_PAGEDOWN, (LPARAM) WndCtl);
995               }
996             SetSystemTimer(Wnd, SCROLL_TIMER, (WM_LBUTTONDOWN == Msg) ?
997                            SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
998                            (TIMERPROC) NULL);
999           }
1000         else
1001           {
1002             KillSystemTimer(Wnd, SCROLL_TIMER);
1003           }
1004         break;
1005 
1006       case SCROLL_BOTTOM_ARROW:
1007         if (HitTest == ScrollTrackHitTest)
1008           {
1009             if ((Msg == WM_LBUTTONDOWN) || (Msg == WM_SYSTIMER))
1010               {
1011                 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
1012                              SB_LINEDOWN, (LPARAM) WndCtl);
1013               }
1014 	    SetSystemTimer(Wnd, SCROLL_TIMER, (WM_LBUTTONDOWN == Msg) ?
1015                            SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
1016                            (TIMERPROC) NULL);
1017             if (ScrollBarInfo.rgstate[ScrollTrackHitTest] != STATE_SYSTEM_UNAVAILABLE)
1018             {
1019                if (!(ScrollBarInfo.rgstate[ScrollTrackHitTest] &= STATE_SYSTEM_PRESSED))
1020                {
1021                   TRACE("Set Arrow\n");
1022                   IntUpdateScrollArrows (Wnd, Dc, &ScrollBarInfo, &NewInfo, SBType, ScrollTrackHitTest, Vertical, TRUE);
1023                }
1024             }
1025           }
1026         else
1027         {
1028             IntUpdateScrollArrows (Wnd, Dc, &ScrollBarInfo, &NewInfo, SBType, ScrollTrackHitTest, Vertical, FALSE);
1029             KillSystemTimer(Wnd, SCROLL_TIMER);
1030         }
1031         break;
1032     }
1033 
1034   if (Msg == WM_LBUTTONDOWN)
1035     {
1036       if (SCROLL_THUMB == HitTest)
1037         {
1038           UINT Val = IntScrollGetThumbVal(Wnd, SBType, &ScrollBarInfo, Vertical,
1039                                           TrackThumbPos + LastMousePos - LastClickPos);
1040           SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
1041                        MAKEWPARAM(SB_THUMBTRACK, Val), (LPARAM) WndCtl);
1042         }
1043     }
1044 
1045   if (Msg == WM_LBUTTONUP)
1046     {
1047       HitTest = ScrollTrackHitTest;
1048       ScrollTrackHitTest = SCROLL_NOWHERE;  /* Terminate tracking */
1049 
1050       if (SCROLL_THUMB == HitTest)
1051         {
1052           UINT Val = IntScrollGetThumbVal(Wnd, SBType, &ScrollBarInfo, Vertical,
1053                                           TrackThumbPos + LastMousePos - LastClickPos);
1054           SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
1055                        MAKEWPARAM(SB_THUMBPOSITION, Val), (LPARAM) WndCtl);
1056         }
1057       /* SB_ENDSCROLL doesn't report thumb position */
1058       SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
1059                    SB_ENDSCROLL, (LPARAM) WndCtl);
1060 
1061       /* Terminate tracking */
1062       ScrollTrackingWin = 0;
1063     }
1064 
1065   ReleaseDC(Wnd, Dc);
1066 }
1067 
1068 
1069 /***********************************************************************
1070  *           IntScrollCreateScrollBar
1071  *
1072  *  Create a scroll bar
1073  */
1074 static void IntScrollCreateScrollBar(
1075   HWND Wnd /* [in] Handle of window with scrollbar(s) */,
1076   LPCREATESTRUCTW lpCreate /* [in] The style and place of the scroll bar */)
1077 {
1078   SCROLLINFO Info;
1079 
1080   Info.cbSize = sizeof(SCROLLINFO);
1081   Info.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
1082   Info.nMin = 0;
1083   Info.nMax = 0;
1084   Info.nPage = 0;
1085   Info.nPos = 0;
1086   Info.nTrackPos = 0;
1087   NtUserSetScrollInfo(Wnd, SB_CTL, &Info, FALSE);
1088 
1089   TRACE("hwnd=%p lpCreate=%p\n", Wnd, lpCreate);
1090 
1091 #if 0 /* FIXME */
1092   if (lpCreate->style & WS_DISABLED)
1093   {
1094   //    info->flags = ESB_DISABLE_BOTH;
1095       //NtUserEnableScrollBar(Wnd,SB_CTL,(wParam ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH));
1096       NtUserMessageCall( Wnd, WM_ENABLE, FALSE, 0, 0, FNID_SCROLLBAR, FALSE);
1097       ERR("Created WS_DISABLED scrollbar\n");
1098   }
1099 #endif
1100   if (0 != (lpCreate->style & (SBS_SIZEGRIP | SBS_SIZEBOX)))
1101     {
1102       if (0 != (lpCreate->style & SBS_SIZEBOXTOPLEFTALIGN))
1103         {
1104           MoveWindow(Wnd, lpCreate->x, lpCreate->y, GetSystemMetrics(SM_CXVSCROLL) + 1,
1105                      GetSystemMetrics(SM_CYHSCROLL) + 1, FALSE);
1106         }
1107       else if (0 != (lpCreate->style & SBS_SIZEBOXBOTTOMRIGHTALIGN))
1108         {
1109           MoveWindow(Wnd, lpCreate->x + lpCreate->cx - GetSystemMetrics(SM_CXVSCROLL) - 1,
1110                      lpCreate->y + lpCreate->cy - GetSystemMetrics(SM_CYHSCROLL) - 1,
1111                      GetSystemMetrics(SM_CXVSCROLL) + 1,
1112                      GetSystemMetrics(SM_CYHSCROLL) + 1, FALSE);
1113         }
1114     }
1115   else if (0 != (lpCreate->style & SBS_VERT))
1116     {
1117       if (0 != (lpCreate->style & SBS_LEFTALIGN))
1118         {
1119           MoveWindow(Wnd, lpCreate->x, lpCreate->y,
1120                      GetSystemMetrics(SM_CXVSCROLL) + 1, lpCreate->cy, FALSE);
1121         }
1122       else if (0 != (lpCreate->style & SBS_RIGHTALIGN))
1123         {
1124           MoveWindow(Wnd,
1125                      lpCreate->x + lpCreate->cx - GetSystemMetrics(SM_CXVSCROLL) - 1,
1126                      lpCreate->y,
1127                       GetSystemMetrics(SM_CXVSCROLL) + 1, lpCreate->cy, FALSE);
1128         }
1129     }
1130   else  /* SBS_HORZ */
1131     {
1132       if (0 != (lpCreate->style & SBS_TOPALIGN))
1133         {
1134           MoveWindow(Wnd, lpCreate->x, lpCreate->y,
1135                      lpCreate->cx, GetSystemMetrics(SM_CYHSCROLL) + 1, FALSE);
1136         }
1137       else if (0 != (lpCreate->style & SBS_BOTTOMALIGN))
1138         {
1139           MoveWindow(Wnd,
1140                      lpCreate->x,
1141                      lpCreate->y + lpCreate->cy - GetSystemMetrics(SM_CYHSCROLL) - 1,
1142                      lpCreate->cx, GetSystemMetrics(SM_CYHSCROLL) + 1, FALSE);
1143         }
1144     }
1145 }
1146 
1147 /* USER32 INTERNAL FUNCTIONS **************************************************/
1148 
1149 /***********************************************************************
1150  *           ScrollTrackScrollBar
1151  *
1152  * Track a mouse button press on a scroll-bar.
1153  * pt is in screen-coordinates for non-client scroll bars.
1154  */
1155 VOID FASTCALL
1156 ScrollTrackScrollBar(HWND Wnd, INT SBType, POINT Pt)
1157 {
1158   MSG Msg;
1159   UINT XOffset = 0, YOffset = 0;
1160 
1161   if (SBType != SB_CTL)
1162   { // Used with CMD mouse tracking.
1163       PWND pwnd = ValidateHwnd(Wnd);
1164       if (!pwnd) return;
1165       XOffset = pwnd->rcClient.left - pwnd->rcWindow.left;
1166       YOffset = pwnd->rcClient.top - pwnd->rcWindow.top;
1167 //      RECT rect;
1168 //      WIN_GetRectangles( Wnd, COORDS_CLIENT, &rect, NULL );
1169       ScreenToClient(Wnd, &Pt);
1170 //      Pt.x -= rect.left;
1171 //      Pt.y -= rect.top;
1172       Pt.x += XOffset;
1173       Pt.y += YOffset;
1174   }
1175 
1176   IntScrollHandleScrollEvent(Wnd, SBType, WM_LBUTTONDOWN, Pt);
1177 
1178   do
1179   {
1180       if (!GetMessageW(&Msg, 0, 0, 0)) break;
1181       if (CallMsgFilterW(&Msg, MSGF_SCROLLBAR)) continue;
1182       if ( Msg.message == WM_LBUTTONUP ||
1183            Msg.message == WM_MOUSEMOVE ||
1184           (Msg.message == WM_SYSTIMER && Msg.wParam == SCROLL_TIMER))
1185       {
1186           Pt.x = LOWORD(Msg.lParam) + XOffset;
1187           Pt.y = HIWORD(Msg.lParam) + YOffset;
1188           IntScrollHandleScrollEvent(Wnd, SBType, Msg.message, Pt);
1189       }
1190       else
1191       {
1192           TranslateMessage(&Msg);
1193           DispatchMessageW(&Msg);
1194       }
1195       if (!IsWindow(Wnd))
1196       {
1197           ReleaseCapture();
1198           break;
1199       }
1200    } while (Msg.message != WM_LBUTTONUP && GetCapture() == Wnd);
1201 }
1202 
1203 
1204 static DWORD FASTCALL
1205 IntSetScrollInfo(HWND Wnd, LPCSCROLLINFO Info, BOOL bRedraw)
1206 {
1207    DWORD Ret = NtUserSetScrollInfo(Wnd, SB_CTL, Info, bRedraw);
1208    if (Ret) IntNotifyWinEvent(EVENT_OBJECT_VALUECHANGE, Wnd, OBJID_CLIENT, CHILDID_SELF, WEF_SETBYWNDPTI);
1209    return Ret;
1210 }
1211 
1212 
1213 /***********************************************************************
1214  *           ScrollBarWndProc
1215  */
1216 LRESULT WINAPI
1217 ScrollBarWndProc_common(WNDPROC DefWindowProc, HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, BOOL unicode )
1218 {
1219 #ifdef __REACTOS__ // Do this now, remove after Server side is fixed.
1220   PWND pWnd;
1221   PSBWND pSBWnd;
1222   SCROLLINFO ScrollInfo;
1223 
1224   pWnd = ValidateHwnd(Wnd);
1225   if (pWnd)
1226   {
1227      if (!pWnd->fnid)
1228      {
1229         TRACE("ScrollBar CTL size %d\n", (sizeof(SBWND)-sizeof(WND)));
1230         if ( pWnd->cbwndExtra < (sizeof(SBWND)-sizeof(WND)) )
1231         {
1232            ERR("Wrong Extra bytes for Scrollbar!\n");
1233            return 0;
1234         }
1235 
1236         if (Msg != WM_CREATE)
1237         {
1238            return DefWindowProc(Wnd, Msg, wParam, lParam);
1239         }
1240         NtUserSetWindowFNID(Wnd, FNID_SCROLLBAR);
1241      }
1242      else
1243      {
1244         if (pWnd->fnid != FNID_SCROLLBAR)
1245         {
1246            ERR("Wrong window class for Scrollbar!\n");
1247            return 0;
1248         }
1249      }
1250   }
1251 #endif
1252 
1253   if (! IsWindow(Wnd))
1254     {
1255       return 0;
1256     }
1257 
1258   // Must be a scroll bar control!
1259   pSBWnd = (PSBWND)pWnd;
1260 
1261   switch (Msg)
1262     {
1263       case WM_CREATE:
1264         IntScrollCreateScrollBar(Wnd, (LPCREATESTRUCTW) lParam);
1265         break;
1266 
1267       case WM_ENABLE:
1268         {
1269           return SendMessageW( Wnd, SBM_ENABLE_ARROWS, wParam ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH, 0);
1270         }
1271 
1272       case WM_LBUTTONDBLCLK:
1273       case WM_LBUTTONDOWN:
1274         if (GetWindowLongW( Wnd, GWL_STYLE ) & SBS_SIZEGRIP)
1275         {
1276            SendMessageW( GetParent(Wnd), WM_SYSCOMMAND,
1277                          SC_SIZE + ((GetWindowLongW( Wnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) ?
1278                                    WMSZ_BOTTOMLEFT : WMSZ_BOTTOMRIGHT), lParam );
1279         }
1280         else
1281         {
1282           POINT Pt;
1283           Pt.x = (short)LOWORD(lParam);
1284           Pt.y = (short)HIWORD(lParam);
1285           ScrollTrackScrollBar(Wnd, SB_CTL, Pt);
1286 	}
1287         break;
1288 
1289       case WM_LBUTTONUP:
1290       case WM_MOUSEMOVE:
1291       case WM_SYSTIMER:
1292         {
1293           POINT Pt;
1294           Pt.x = (short)LOWORD(lParam);
1295           Pt.y = (short)HIWORD(lParam);
1296           IntScrollHandleScrollEvent(Wnd, SB_CTL, Msg, Pt);
1297         }
1298         break;
1299 
1300       case WM_KEYDOWN:
1301         IntScrollHandleKbdEvent(Wnd, wParam, lParam);
1302         break;
1303 
1304       case WM_KEYUP:
1305         ShowCaret(Wnd);
1306         break;
1307 
1308       case WM_SETFOCUS:
1309         {
1310           /* Create a caret when a ScrollBar get focus */
1311           RECT Rect;
1312           int ArrowSize, ThumbSize, ThumbPos, Vertical;
1313 
1314           Vertical = IntScrollGetScrollBarRect(Wnd, SB_CTL, &Rect,
1315                                                &ArrowSize, &ThumbSize, &ThumbPos);
1316           if (! Vertical)
1317             {
1318               CreateCaret(Wnd, (HBITMAP) 1, ThumbSize - 2, Rect.bottom - Rect.top - 2);
1319               SetCaretPos(ThumbPos + 1, Rect.top + 1);
1320             }
1321           else
1322             {
1323               CreateCaret(Wnd, (HBITMAP) 1, Rect.right - Rect.left - 2, ThumbSize - 2);
1324               SetCaretPos(Rect.top + 1, ThumbPos + 1);
1325             }
1326           ShowCaret(Wnd);
1327         }
1328         break;
1329 
1330       case WM_KILLFOCUS:
1331         {
1332           RECT Rect;
1333           int ArrowSize, ThumbSize, ThumbPos, Vertical;
1334 
1335           Vertical = IntScrollGetScrollBarRect(Wnd, SB_CTL, &Rect,
1336                                                &ArrowSize, &ThumbSize, &ThumbPos);
1337           if (! Vertical)
1338             {
1339               Rect.left = ThumbPos + 1;
1340               Rect.right = Rect.left + ThumbSize;
1341             }
1342           else
1343             {
1344               Rect.top = ThumbPos + 1;
1345               Rect.bottom = Rect.top + ThumbSize;
1346             }
1347           HideCaret(Wnd);
1348           InvalidateRect(Wnd, &Rect, FALSE);
1349           DestroyCaret();
1350         }
1351         break;
1352 
1353       case WM_ERASEBKGND:
1354          return 1;
1355 
1356       case WM_GETDLGCODE:
1357          return DLGC_WANTARROWS; /* Windows returns this value */
1358 
1359       case WM_PAINT:
1360         {
1361           PAINTSTRUCT Ps;
1362           HDC Dc;
1363 
1364           Dc = (0 != wParam ? (HDC) wParam : BeginPaint(Wnd, &Ps));
1365 
1366           if (GetWindowLongPtrW(Wnd, GWL_STYLE) & SBS_SIZEGRIP)
1367             {
1368               IntScrollDrawSizeGrip(Wnd, Dc);
1369             }
1370           else if (0 != (GetWindowLongPtrW(Wnd, GWL_STYLE) & SBS_SIZEBOX))
1371             {
1372               RECT Rect;
1373               GetClientRect(Wnd, &Rect);
1374               FillRect(Dc, &Rect, GetSysColorBrush(COLOR_SCROLLBAR));
1375             }
1376           else
1377             {
1378               IntDrawScrollBar(Wnd, Dc, SB_CTL/*, TRUE, TRUE*/);
1379             }
1380 
1381           if (0 == wParam)
1382             {
1383               EndPaint(Wnd, &Ps);
1384             }
1385         }
1386         break;
1387 
1388       case SBM_GETPOS:
1389         return pSBWnd->SBCalc.pos;
1390 
1391       case SBM_GETRANGE:
1392         *(LPINT)wParam = pSBWnd->SBCalc.posMin;
1393         *(LPINT)lParam = pSBWnd->SBCalc.posMax;
1394         // This message does not return a value.
1395         return 0;
1396 
1397       case SBM_ENABLE_ARROWS:
1398         return EnableScrollBar( Wnd, SB_CTL, wParam );
1399 
1400       case SBM_SETPOS:
1401         {
1402            ScrollInfo.cbSize = sizeof(SCROLLINFO);
1403            ScrollInfo.fMask = SIF_POS|SIF_PREVIOUSPOS;
1404            ScrollInfo.nPos = wParam;
1405            return IntSetScrollInfo(Wnd, &ScrollInfo, lParam);
1406         }
1407 
1408       case SBM_SETRANGEREDRAW:
1409       case SBM_SETRANGE:
1410         {
1411            ScrollInfo.cbSize = sizeof(SCROLLINFO);
1412            ScrollInfo.fMask = SIF_RANGE|SIF_PREVIOUSPOS;
1413            ScrollInfo.nMin = wParam;
1414            ScrollInfo.nMax = lParam;
1415            return IntSetScrollInfo(Wnd, &ScrollInfo, Msg == SBM_SETRANGEREDRAW ? TRUE : FALSE);
1416         }
1417 
1418       case SBM_SETSCROLLINFO:
1419         return IntSetScrollInfo(Wnd, (LPCSCROLLINFO)lParam, wParam);
1420 
1421       case SBM_GETSCROLLINFO:
1422         {
1423          PSBDATA pSBData = (PSBDATA)&pSBWnd->SBCalc;
1424          DWORD ret = NtUserSBGetParms(Wnd, SB_CTL, pSBData, (SCROLLINFO *) lParam);
1425          if (!ret)
1426          {
1427             ERR("SBM_GETSCROLLINFO No ScrollInfo\n");
1428          }
1429          return ret;
1430         }
1431       case SBM_GETSCROLLBARINFO:
1432         ((PSCROLLBARINFO)lParam)->cbSize = sizeof(SCROLLBARINFO);
1433         return NtUserGetScrollBarInfo(Wnd, OBJID_CLIENT, (PSCROLLBARINFO)lParam);
1434 
1435       case 0x00e5:
1436       case 0x00e7:
1437       case 0x00e8:
1438       case 0x00ec:
1439       case 0x00ed:
1440       case 0x00ee:
1441       case 0x00ef:
1442         WARN("unknown Win32 msg %04x wp=%08lx lp=%08lx\n",
1443 		Msg, wParam, lParam );
1444         break;
1445 
1446       default:
1447         if (WM_USER <= Msg)
1448           {
1449             WARN("unknown msg %04x wp=%04lx lp=%08lx\n", Msg, wParam, lParam);
1450           }
1451         if (unicode)
1452             return DefWindowProcW( Wnd, Msg, wParam, lParam );
1453         else
1454             return DefWindowProcA( Wnd, Msg, wParam, lParam );
1455     }
1456 
1457   return 0;
1458 }
1459 
1460 LRESULT WINAPI
1461 ScrollBarWndProcW(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam)
1462 {
1463   return ScrollBarWndProc_common(DefWindowProcW, Wnd, Msg, wParam, lParam, TRUE);
1464 }
1465 
1466 LRESULT WINAPI
1467 ScrollBarWndProcA(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam)
1468 {
1469   return ScrollBarWndProc_common(DefWindowProcA, Wnd, Msg, wParam, lParam, FALSE);
1470 }
1471 
1472 
1473 /* PUBLIC FUNCTIONS ***********************************************************/
1474 
1475 /*
1476  * @implemented
1477  */
1478 BOOL
1479 WINAPI
1480 DECLSPEC_HOTPATCH
1481 EnableScrollBar( HWND hwnd, UINT nBar, UINT flags )
1482 {
1483    BOOL Hook, Ret = FALSE;
1484 
1485    LoadUserApiHook();
1486 
1487    Hook = BeginIfHookedUserApiHook();
1488 
1489      /* Bypass SEH and go direct. */
1490    if (!Hook)
1491    {
1492       Ret = NtUserEnableScrollBar(hwnd, nBar, flags);
1493       if (!Ret) return Ret;
1494       SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, TRUE );
1495       return Ret;
1496    }
1497    _SEH2_TRY
1498    {
1499       Ret = guah.EnableScrollBar(hwnd, nBar, flags);
1500    }
1501    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1502    {
1503        ERR("Got exception in hooked EnableScrollBar!\n");
1504    }
1505    _SEH2_END;
1506 
1507    EndUserApiHook();
1508 
1509    return Ret;
1510 }
1511 
1512 BOOL WINAPI
1513 RealGetScrollInfo(HWND Wnd, INT SBType, LPSCROLLINFO Info)
1514 {
1515   PWND pWnd;
1516   PSBDATA pSBData;
1517 
1518   if (SB_CTL == SBType)
1519   {
1520      return SendMessageW(Wnd, SBM_GETSCROLLINFO, 0, (LPARAM) Info);
1521   }
1522 
1523   pWnd = ValidateHwnd(Wnd);
1524   if (!pWnd) return FALSE;
1525 
1526   if (SBType < SB_HORZ || SBType > SB_VERT)
1527   {
1528      SetLastError(ERROR_INVALID_PARAMETER);
1529      return FALSE;
1530   }
1531   if (!pWnd->pSBInfo)
1532   {
1533      SetLastError(ERROR_NO_SCROLLBARS);
1534      return FALSE;
1535   }
1536   pSBData = IntGetSBData(pWnd, SBType);
1537   return NtUserSBGetParms(Wnd, SBType, pSBData, Info);
1538 }
1539 
1540 /*
1541  * @implemented
1542  */
1543 BOOL WINAPI GetScrollBarInfo( _In_ HWND hwnd, _In_ LONG idObject, _Inout_ LPSCROLLBARINFO info)
1544 {
1545     BOOL Ret;
1546     PWND pWnd = ValidateHwnd(hwnd);
1547     TRACE("hwnd=%p idObject=%d info=%p\n", hwnd, idObject, info);
1548     if (!pWnd) return FALSE;
1549     Ret = NtUserGetScrollBarInfo(hwnd, idObject, info); // This will be fixed once SB is server side.
1550     /* rcScrollBar needs to be in screen coordinates */
1551     OffsetRect( &(info->rcScrollBar), pWnd->rcWindow.left, pWnd->rcWindow.top );
1552     return Ret;
1553 }
1554 
1555 /*
1556  * @implemented
1557  */
1558 BOOL
1559 WINAPI
1560 DECLSPEC_HOTPATCH
1561 GetScrollInfo(HWND Wnd, INT SBType, LPSCROLLINFO Info)
1562 {
1563    BOOL Hook, Ret = FALSE;
1564 
1565    LoadUserApiHook();
1566 
1567    Hook = BeginIfHookedUserApiHook();
1568 
1569      /* Bypass SEH and go direct. */
1570    if (!Hook) return RealGetScrollInfo(Wnd, SBType, Info);
1571 
1572    _SEH2_TRY
1573    {
1574       Ret = guah.GetScrollInfo(Wnd, SBType, Info);
1575    }
1576    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1577    {
1578        ERR("Got exception in hooked GetScrollInfo!\n");
1579    }
1580    _SEH2_END;
1581 
1582    EndUserApiHook();
1583 
1584    return Ret;
1585 }
1586 
1587 /*
1588  * @implemented
1589  */
1590 INT
1591 WINAPI
1592 DECLSPEC_HOTPATCH
1593 GetScrollPos(HWND Wnd, INT Bar)
1594 {
1595   PWND pwnd;
1596   PSBDATA pSBData;
1597 
1598   TRACE("Wnd=%p Bar=%d\n", Wnd, Bar);
1599 
1600   /* Refer SB_CTL requests to the window */
1601   if (SB_CTL == Bar)
1602   {
1603       return SendMessageW(Wnd, SBM_GETPOS, (WPARAM) 0, (LPARAM) 0);
1604   }
1605   else if (Bar == SB_HORZ || Bar == SB_VERT )
1606   {
1607      pwnd = ValidateHwnd(Wnd);
1608      if (!pwnd) return 0;
1609 
1610      if (pwnd->pSBInfo)
1611      {
1612         pSBData = IntGetSBData(pwnd, Bar);
1613         return pSBData->pos;
1614      }
1615 
1616      SetLastError(ERROR_NO_SCROLLBARS);
1617      TRACE("GetScrollPos No Scroll Info\n");
1618      return 0;
1619   }
1620   SetLastError(ERROR_INVALID_PARAMETER);
1621   return 0;
1622 }
1623 
1624 /*
1625  * @implemented
1626  */
1627 BOOL
1628 WINAPI
1629 DECLSPEC_HOTPATCH
1630 GetScrollRange(HWND Wnd, int Bar, LPINT MinPos, LPINT MaxPos)
1631 {
1632   PWND pwnd;
1633   PSBDATA pSBData;
1634 
1635   TRACE("Wnd=%x Bar=%d Min=%p Max=%p\n", Wnd, Bar, MinPos, MaxPos);
1636 
1637   /* Refer SB_CTL requests to the window */
1638   if (SB_CTL == Bar)
1639   {
1640       return SendMessageW(Wnd, SBM_GETRANGE, (WPARAM) MinPos, (LPARAM) MaxPos);
1641   }
1642   else if (Bar == SB_HORZ || Bar == SB_VERT )
1643   {
1644       pwnd = ValidateHwnd(Wnd);
1645       if (!pwnd) return FALSE;
1646 
1647       if (pwnd->pSBInfo)
1648       {
1649          pSBData = IntGetSBData(pwnd, Bar);
1650          *MinPos = pSBData->posMin;
1651          *MaxPos = pSBData->posMax;
1652       }
1653       else
1654       {
1655          SetLastError(ERROR_NO_SCROLLBARS);
1656          *MinPos = 0;
1657          *MaxPos = 0;
1658       }
1659       return TRUE;
1660   }
1661   SetLastError(ERROR_INVALID_PARAMETER);
1662   return FALSE;
1663 }
1664 
1665 INT WINAPI
1666 RealSetScrollInfo(HWND Wnd, int SBType, LPCSCROLLINFO Info, BOOL bRedraw)
1667 {
1668   if (SB_CTL == SBType)
1669     {
1670       return SendMessageW(Wnd, SBM_SETSCROLLINFO, (WPARAM) bRedraw, (LPARAM) Info);
1671     }
1672   else
1673     {
1674       return NtUserSetScrollInfo(Wnd, SBType, Info, bRedraw);
1675     }
1676 }
1677 
1678 /*
1679  * @implemented
1680  */
1681 INT
1682 WINAPI
1683 DECLSPEC_HOTPATCH
1684 SetScrollInfo(HWND Wnd, int SBType, LPCSCROLLINFO Info, BOOL bRedraw)
1685 {
1686    BOOL Hook;
1687    INT Ret = 0;
1688 
1689    LoadUserApiHook();
1690 
1691    Hook = BeginIfHookedUserApiHook();
1692 
1693      /* Bypass SEH and go direct. */
1694    if (!Hook) return RealSetScrollInfo(Wnd, SBType, Info, bRedraw);
1695 
1696    _SEH2_TRY
1697    {
1698       Ret = guah.SetScrollInfo(Wnd, SBType, Info, bRedraw);
1699    }
1700    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1701    {
1702        ERR("Got exception in hooked SetScrollInfo!\n");
1703    }
1704    _SEH2_END;
1705 
1706    EndUserApiHook();
1707 
1708    return Ret;
1709 
1710 }
1711 
1712 /*
1713  * @implemented
1714  */
1715 INT
1716 WINAPI
1717 DECLSPEC_HOTPATCH
1718 SetScrollPos(HWND hWnd, INT nBar, INT nPos, BOOL bRedraw)
1719 {
1720   SCROLLINFO ScrollInfo;
1721 
1722   ScrollInfo.cbSize = sizeof(SCROLLINFO);
1723   ScrollInfo.fMask = SIF_POS|SIF_PREVIOUSPOS;
1724   ScrollInfo.nPos = nPos;
1725 
1726   return SetScrollInfo(hWnd, nBar, &ScrollInfo, bRedraw);
1727 }
1728 
1729 /*
1730  * @implemented
1731  */
1732 BOOL
1733 WINAPI
1734 DECLSPEC_HOTPATCH
1735 SetScrollRange(HWND hWnd, INT nBar, INT nMinPos, INT nMaxPos, BOOL bRedraw)
1736 {
1737   PWND pWnd;
1738   SCROLLINFO ScrollInfo;
1739 
1740   pWnd = ValidateHwnd(hWnd);
1741   if ( !pWnd ) return FALSE;
1742 
1743   if (((LONGLONG)nMaxPos - nMinPos) > MAXLONG)
1744   {
1745      SetLastError(ERROR_INVALID_SCROLLBAR_RANGE);
1746      return FALSE;
1747   }
1748 
1749   ScrollInfo.cbSize = sizeof(SCROLLINFO);
1750   ScrollInfo.fMask = SIF_RANGE;
1751   ScrollInfo.nMin = nMinPos;
1752   ScrollInfo.nMax = nMaxPos;
1753   SetScrollInfo(hWnd, nBar, &ScrollInfo, bRedraw); // do not bypass themes.
1754   return TRUE;
1755 }
1756