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 void
322 IntDrawScrollBar(HWND Wnd, HDC DC, INT Bar)
323 {
324   //PSBWND pSBWnd;
325   //INT ThumbSize;
326   SCROLLBARINFO Info;
327   BOOL Vertical;
328 
329   /*
330    * Get scroll bar info.
331    */
332   switch (Bar)
333     {
334       case SB_HORZ:
335         Vertical = FALSE;
336         break;
337 
338       case SB_VERT:
339         Vertical = TRUE;
340         break;
341 
342       case SB_CTL:
343         Vertical = (GetWindowLongPtrW(Wnd, GWL_STYLE) & SBS_VERT) != 0;
344         break;
345 
346       default:
347         return;
348     }
349   if (!IntGetScrollBarInfo(Wnd, Bar, &Info))
350   {
351       return;
352   }
353 
354   if (IsRectEmpty(&Info.rcScrollBar))
355   {
356       return;
357   }
358 
359   //ThumbSize = pSBWnd->pSBCalc->pxThumbBottom - pSBWnd->pSBCalc->pxThumbTop;
360 
361   /*
362    * Draw the arrows.
363    */
364   if (Info.dxyLineButton)
365   {
366       IntDrawScrollArrows(DC, &Info, Vertical);
367   }
368 
369   /*
370    * Draw the interior.
371    */
372   IntDrawScrollInterior(Wnd, DC, Bar, Vertical, &Info);
373 
374   /*
375    * If scroll bar has focus, reposition the caret.
376    */
377   if (Wnd == GetFocus() && SB_CTL == Bar)
378   {
379       if (Vertical)
380       {
381           SetCaretPos(Info.rcScrollBar.top + 1, Info.dxyLineButton + 1);
382       }
383       else
384       {
385            SetCaretPos(Info.dxyLineButton + 1, Info.rcScrollBar.top + 1);
386       }
387   }
388 }
389 
390 static BOOL FASTCALL
391 IntScrollPtInRectEx(LPRECT Rect, POINT Pt, BOOL Vertical)
392 {
393   RECT TempRect = *Rect;
394   int scrollbarWidth;
395 
396   /* Pad hit rect to allow mouse to be dragged outside of scrollbar and
397    * still be considered in the scrollbar. */
398   if (Vertical)
399   {
400       scrollbarWidth = Rect->right - Rect->left;
401       TempRect.left -= scrollbarWidth*8;
402       TempRect.right += scrollbarWidth*8;
403       TempRect.top -= scrollbarWidth*2;
404       TempRect.bottom += scrollbarWidth*2;
405   }
406   else
407   {
408       scrollbarWidth = Rect->bottom - Rect->top;
409       TempRect.left -= scrollbarWidth*2;
410       TempRect.right += scrollbarWidth*2;
411       TempRect.top -= scrollbarWidth*8;
412       TempRect.bottom += scrollbarWidth*8;
413   }
414 
415   return PtInRect(&TempRect, Pt);
416 }
417 
418 static DWORD FASTCALL
419 IntScrollHitTest(PSCROLLBARINFO ScrollBarInfo, BOOL Vertical, POINT Pt, BOOL Dragging)
420 {
421   INT ArrowSize, ThumbSize, ThumbPos;
422 
423   if ((Dragging && ! IntScrollPtInRectEx(&ScrollBarInfo->rcScrollBar, Pt, Vertical)) ||
424       ! PtInRect(&ScrollBarInfo->rcScrollBar, Pt)) return SCROLL_NOWHERE;
425 
426   ThumbPos = ScrollBarInfo->xyThumbTop;
427   ThumbSize = ScrollBarInfo->xyThumbBottom - ThumbPos;
428   ArrowSize = ScrollBarInfo->dxyLineButton;
429 
430   if (Vertical)
431   {
432       if (Pt.y < ScrollBarInfo->rcScrollBar.top + ArrowSize) return SCROLL_TOP_ARROW;
433       if (Pt.y >= ScrollBarInfo->rcScrollBar.bottom - ArrowSize) return SCROLL_BOTTOM_ARROW;
434       if (!ThumbPos) return SCROLL_TOP_RECT;
435       Pt.y -= ScrollBarInfo->rcScrollBar.top;
436       if (Pt.y < ThumbPos) return SCROLL_TOP_RECT;
437       if (Pt.y >= ThumbPos + ThumbSize) return SCROLL_BOTTOM_RECT;
438   }
439   else
440   {
441       if (Pt.x < ScrollBarInfo->rcScrollBar.left + ArrowSize) return SCROLL_TOP_ARROW;
442       if (Pt.x >= ScrollBarInfo->rcScrollBar.right - ArrowSize) return SCROLL_BOTTOM_ARROW;
443       if (!ThumbPos) return SCROLL_TOP_RECT;
444       Pt.x -= ScrollBarInfo->rcScrollBar.left;
445       if (Pt.x < ThumbPos) return SCROLL_TOP_RECT;
446       if (Pt.x >= ThumbPos + ThumbSize) return SCROLL_BOTTOM_RECT;
447   }
448 
449   return SCROLL_THUMB;
450 }
451 
452 
453 /***********************************************************************
454  *           IntScrollGetScrollBarRect
455  *
456  * Compute the scroll bar rectangle, in drawing coordinates (i.e. client
457  * coords for SB_CTL, window coords for SB_VERT and SB_HORZ).
458  * 'arrowSize' returns the width or height of an arrow (depending on
459  * the orientation of the scrollbar), 'thumbSize' returns the size of
460  * the thumb, and 'thumbPos' returns the position of the thumb
461  * relative to the left or to the top.
462  * Return TRUE if the scrollbar is vertical, FALSE if horizontal.
463  */
464 static BOOL FASTCALL
465 IntScrollGetScrollBarRect(HWND Wnd, INT Bar, RECT *Rect,
466                           INT *ArrowSize, INT *ThumbSize,
467                           INT *ThumbPos)
468 {
469   INT Pixels;
470   BOOL Vertical;
471   PWND pWnd;
472   PSBINFO pSBInfo;
473   PSBDATA pSBData;
474   PSBWND pSBWnd;
475 
476   pWnd = ValidateHwnd( Wnd );
477   if (!pWnd) return FALSE;
478   pSBInfo = DesktopPtrToUser(pWnd->pSBInfo);
479 
480   *Rect = pWnd->rcClient;
481   OffsetRect( Rect, -pWnd->rcWindow.left, -pWnd->rcWindow.top );
482   if (pWnd->ExStyle & WS_EX_LAYOUTRTL)
483      mirror_rect( &pWnd->rcWindow, Rect );
484 
485   switch (Bar)
486     {
487       case SB_HORZ:
488 //        WIN_GetRectangles( Wnd, COORDS_WINDOW, NULL, Rect );
489         Rect->top = Rect->bottom;
490         Rect->bottom += GetSystemMetrics(SM_CYHSCROLL);
491 	if (pWnd->style & WS_BORDER)
492         {
493             Rect->left--;
494             Rect->right++;
495 	}
496         else if (pWnd->style & WS_VSCROLL)
497         {
498             Rect->right++;
499         }
500         Vertical = FALSE;
501         pSBData = &pSBInfo->Horz;
502 	break;
503 
504       case SB_VERT:
505 //        WIN_GetRectangles( Wnd, COORDS_WINDOW, NULL, Rect );
506         if (pWnd->ExStyle & WS_EX_LEFTSCROLLBAR)
507         {
508             Rect->right = Rect->left;
509             Rect->left -= GetSystemMetrics(SM_CXVSCROLL);
510         }
511         else
512         {
513             Rect->left = Rect->right;
514             Rect->right += GetSystemMetrics(SM_CXVSCROLL);
515         }
516 	if (pWnd->style & WS_BORDER)
517         {
518             Rect->top--;
519             Rect->bottom++;
520         }
521         else if (pWnd->style & WS_HSCROLL)
522         {
523             Rect->bottom++;
524         }
525         Vertical = TRUE;
526         pSBData = &pSBInfo->Vert;
527 	break;
528 
529       case SB_CTL:
530         GetClientRect( Wnd, Rect );
531         Vertical = (pWnd->style & SBS_VERT);
532         pSBWnd = (PSBWND)pWnd;
533         pSBData = (PSBDATA)&pSBWnd->SBCalc;
534 	break;
535 
536       default:
537         return FALSE;
538     }
539 
540   if (Vertical) Pixels = Rect->bottom - Rect->top;
541   else Pixels = Rect->right - Rect->left;
542 
543   if (Pixels <= 2 * GetSystemMetrics(SM_CXVSCROLL) + SCROLL_MIN_RECT)
544   {
545       if (SCROLL_MIN_RECT < Pixels)
546           *ArrowSize = (Pixels - SCROLL_MIN_RECT) / 2;
547       else
548           *ArrowSize = 0;
549       *ThumbPos = *ThumbSize = 0;
550   }
551   else
552   {
553       *ArrowSize = GetSystemMetrics(SM_CXVSCROLL);
554       Pixels -= (2 * (GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP));
555       if (pSBData->page)
556       {
557           *ThumbSize = MulDiv(Pixels, pSBData->page, (pSBData->posMax - pSBData->posMin + 1));
558           if (*ThumbSize < SCROLL_MIN_THUMB) *ThumbSize = SCROLL_MIN_THUMB;
559       }
560       else *ThumbSize = GetSystemMetrics(SM_CXVSCROLL);
561 
562       if (((Pixels -= *ThumbSize ) < 0) ||
563           (( pSBInfo->WSBflags & ESB_DISABLE_BOTH) == ESB_DISABLE_BOTH))
564       {
565           /* Rectangle too small or scrollbar disabled -> no thumb */
566           *ThumbPos = *ThumbSize = 0;
567       }
568       else
569       {
570           INT Max = pSBData->posMax - max(pSBData->page - 1, 0);
571           if (pSBData->posMin >= Max)
572               *ThumbPos = *ArrowSize - SCROLL_ARROW_THUMB_OVERLAP;
573           else
574               *ThumbPos = *ArrowSize - SCROLL_ARROW_THUMB_OVERLAP
575               + MulDiv(Pixels, (pSBData->pos - pSBData->posMin),(Max - pSBData->posMin));
576       }
577   }
578   return Vertical;
579 }
580 
581 /***********************************************************************
582  *           IntScrollGetThumbVal
583  *
584  * Compute the current scroll position based on the thumb position in pixels
585  * from the top of the scroll-bar.
586  */
587 static UINT FASTCALL
588 IntScrollGetThumbVal(HWND Wnd, INT SBType, PSCROLLBARINFO ScrollBarInfo,
589                      BOOL Vertical, INT Pos)
590 {
591   PWND pWnd;
592   PSBDATA pSBData;
593   INT Pixels = Vertical ? ScrollBarInfo->rcScrollBar.bottom
594                           - ScrollBarInfo->rcScrollBar.top
595                         : ScrollBarInfo->rcScrollBar.right
596                           - ScrollBarInfo->rcScrollBar.left;
597 
598   pWnd = ValidateHwnd( Wnd );
599   if (!pWnd) return FALSE;
600 
601   pSBData = IntGetSBData(pWnd, SBType);
602 
603   if ((Pixels -= 2 * ScrollBarInfo->dxyLineButton) <= 0)
604   {
605       return pSBData->posMin;
606   }
607 
608   if ((Pixels -= (ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop)) <= 0)
609   {
610       return pSBData->posMin;
611   }
612 
613   Pos = Pos - ScrollBarInfo->dxyLineButton;
614   if (Pos < 0)
615   {
616       Pos = 0;
617   }
618   if (Pos > Pixels) Pos = Pixels;
619 
620   if (!pSBData->page)
621       Pos *= pSBData->posMax - pSBData->posMin;
622   else
623       Pos *= pSBData->posMax - pSBData->posMin - pSBData->page + 1;
624 
625   return pSBData->posMin + ((Pos + Pixels / 2) / Pixels);
626 }
627 
628 /***********************************************************************
629  *           IntScrollClipPos
630  */
631 static POINT IntScrollClipPos(PRECT lpRect, POINT pt)
632 {
633     if( pt.x < lpRect->left )
634         pt.x = lpRect->left;
635     else
636     if( pt.x > lpRect->right )
637         pt.x = lpRect->right;
638 
639     if( pt.y < lpRect->top )
640         pt.y = lpRect->top;
641     else
642     if( pt.y > lpRect->bottom )
643         pt.y = lpRect->bottom;
644 
645     return pt;
646 }
647 
648 /***********************************************************************
649  *           IntScrollDrawSizeGrip
650  *
651  *  Draw the size grip.
652  */
653 static void FASTCALL
654 IntScrollDrawSizeGrip(HWND Wnd, HDC Dc)
655 {
656   RECT Rect;
657 
658   GetClientRect(Wnd, &Rect);
659   FillRect(Dc, &Rect, GetSysColorBrush(COLOR_SCROLLBAR));
660   Rect.left = max(Rect.left, Rect.right - GetSystemMetrics(SM_CXVSCROLL) - 1);
661   Rect.top  = max(Rect.top, Rect.bottom - GetSystemMetrics(SM_CYHSCROLL) - 1);
662   DrawFrameControl(Dc, &Rect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
663 }
664 
665 /***********************************************************************
666  *           SCROLL_RefreshScrollBar
667  *
668  * Repaint the scroll bar interior after a SetScrollRange() or
669  * SetScrollPos() call.
670  */
671 static void SCROLL_RefreshScrollBar( HWND hwnd, INT nBar,
672 				     BOOL arrows, BOOL interior )
673 {
674     HDC hdc = GetDCEx( hwnd, 0,
675                            DCX_CACHE | ((nBar == SB_CTL) ? 0 : DCX_WINDOW) );
676     if (!hdc) return;
677 
678     IntDrawScrollBar( hwnd, hdc, nBar);//, arrows, interior );
679     ReleaseDC( hwnd, hdc );
680 }
681 
682 
683 /***********************************************************************
684  *           IntScrollHandleKbdEvent
685  *
686  * Handle a keyboard event (only for SB_CTL scrollbars with focus).
687  */
688 static void FASTCALL
689 IntScrollHandleKbdEvent(
690   HWND Wnd /* [in] Handle of window with scrollbar(s) */,
691   WPARAM wParam /* [in] Variable input including enable state */,
692   LPARAM lParam /* [in] Variable input including input point */)
693 {
694   TRACE("Wnd=%p wParam=%ld lParam=%ld\n", Wnd, wParam, lParam);
695 
696   /* hide caret on first KEYDOWN to prevent flicker */
697   if (0 == (lParam & PFD_DOUBLEBUFFER_DONTCARE))
698     {
699       HideCaret(Wnd);
700     }
701 
702   switch(wParam)
703     {
704       case VK_PRIOR:
705         wParam = SB_PAGEUP;
706         break;
707 
708       case VK_NEXT:
709         wParam = SB_PAGEDOWN;
710         break;
711 
712       case VK_HOME:
713         wParam = SB_TOP;
714         break;
715 
716       case VK_END:
717         wParam = SB_BOTTOM;
718         break;
719 
720       case VK_UP:
721         wParam = SB_LINEUP;
722         break;
723 
724       case VK_DOWN:
725         wParam = SB_LINEDOWN;
726         break;
727 
728       default:
729         return;
730     }
731 
732   SendMessageW(GetParent(Wnd),
733                (0 != (GetWindowLongPtrW(Wnd, GWL_STYLE ) & SBS_VERT) ?
734                 WM_VSCROLL : WM_HSCROLL), wParam, (LPARAM) Wnd);
735 }
736 
737 /***********************************************************************
738  *           IntScrollHandleScrollEvent
739  *
740  * Handle a mouse or timer event for the scrollbar.
741  * 'Pt' is the location of the mouse event in drawing coordinates
742  */
743 static VOID FASTCALL
744 IntScrollHandleScrollEvent(HWND Wnd, INT SBType, UINT Msg, POINT Pt)
745 {
746   static POINT PrevPt;           /* Previous mouse position for timer events */
747   static UINT TrackThumbPos;     /* Thumb position when tracking started. */
748   static INT LastClickPos;       /* Position in the scroll-bar of the last
749                                     button-down event. */
750   static INT LastMousePos;       /* Position in the scroll-bar of the last
751                                     mouse event. */
752 
753   DWORD HitTest;
754   HWND WndOwner, WndCtl;
755   BOOL Vertical;
756   HDC Dc;
757   SCROLLBARINFO ScrollBarInfo;
758   SETSCROLLBARINFO NewInfo;
759 
760   if (! IntGetScrollBarInfo(Wnd, SBType, &ScrollBarInfo))
761   {
762      return;
763   }
764   if ((ScrollTrackHitTest == SCROLL_NOWHERE) && (Msg != WM_LBUTTONDOWN))
765   {
766      //// ReactOS : Justin Case something goes wrong.
767      if (Wnd == GetCapture())
768      {
769         ReleaseCapture();
770      }
771      ////
772      return;
773   }
774 
775   NewInfo.nTrackPos = ScrollTrackingVal;
776   NewInfo.reserved = ScrollBarInfo.reserved;
777   memcpy(NewInfo.rgstate, ScrollBarInfo.rgstate, (CCHILDREN_SCROLLBAR + 1) * sizeof(DWORD));
778 
779   if (SBType == SB_CTL && (GetWindowLongPtrW(Wnd, GWL_STYLE) & (SBS_SIZEGRIP | SBS_SIZEBOX)))
780   {
781       switch(Msg)
782       {
783           case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
784             HideCaret(Wnd);  /* hide caret while holding down LBUTTON */
785             SetCapture(Wnd);
786             PrevPt = Pt;
787             ScrollTrackHitTest = HitTest = SCROLL_THUMB;
788             break;
789           case WM_MOUSEMOVE:
790             GetClientRect(GetParent(GetParent(Wnd)), &ScrollBarInfo.rcScrollBar);
791             PrevPt = Pt;
792             break;
793           case WM_LBUTTONUP:
794             ReleaseCapture();
795             ScrollTrackHitTest = HitTest = SCROLL_NOWHERE;
796             if (Wnd == GetFocus()) ShowCaret(Wnd);
797             break;
798           case WM_SYSTIMER:
799             Pt = PrevPt;
800             break;
801       }
802       return;
803   }
804 
805   Dc = GetDCEx(Wnd, 0, DCX_CACHE | ((SB_CTL == SBType) ? 0 : DCX_WINDOW));
806   if (SB_VERT == SBType)
807   {
808       Vertical = TRUE;
809   }
810   else if (SB_HORZ == SBType)
811   {
812       Vertical = FALSE;
813   }
814   else
815   {
816       Vertical = (0 != (GetWindowLongPtrW(Wnd, GWL_STYLE) & SBS_VERT));
817   }
818   WndOwner = (SB_CTL == SBType) ? GetParent(Wnd) : Wnd;
819   WndCtl   = (SB_CTL == SBType) ? Wnd : NULL;
820 
821   switch (Msg)
822   {
823       case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
824         HideCaret(Wnd);     /* hide caret while holding down LBUTTON */
825         ScrollTrackVertical = Vertical;
826         ScrollTrackHitTest  = HitTest = IntScrollHitTest(&ScrollBarInfo, Vertical, Pt, FALSE );
827         LastClickPos  = Vertical ? (Pt.y - ScrollBarInfo.rcScrollBar.top)
828                         : (Pt.x - ScrollBarInfo.rcScrollBar.left);
829         LastMousePos  = LastClickPos;
830         TrackThumbPos = ScrollBarInfo.xyThumbTop;
831         PrevPt = Pt;
832         if (SBType == SB_CTL && (GetWindowLongPtrW(Wnd, GWL_STYLE) & WS_TABSTOP)) SetFocus(Wnd);
833         SetCapture(Wnd);
834         /* Don't update scrollbar if disabled. */
835         if (ScrollBarInfo.rgstate[ScrollTrackHitTest] != STATE_SYSTEM_UNAVAILABLE)
836         {
837             ScrollBarInfo.rgstate[ScrollTrackHitTest] |= STATE_SYSTEM_PRESSED;
838             NewInfo.rgstate[ScrollTrackHitTest] = ScrollBarInfo.rgstate[ScrollTrackHitTest];
839             NtUserSetScrollBarInfo(Wnd, IntScrollGetObjectId(SBType), &NewInfo);
840         }
841         break;
842 
843       case WM_MOUSEMOVE:
844         HitTest = IntScrollHitTest(&ScrollBarInfo, Vertical, Pt, TRUE);
845         PrevPt = Pt;
846         break;
847 
848       case WM_LBUTTONUP:
849         HitTest = SCROLL_NOWHERE;
850         ReleaseCapture();
851         /* if scrollbar has focus, show back caret */
852         if (Wnd == GetFocus()) ShowCaret(Wnd);
853         /* Don't update scrollbar if disabled. */
854         if (ScrollBarInfo.rgstate[ScrollTrackHitTest] != STATE_SYSTEM_UNAVAILABLE)
855         {
856             ScrollBarInfo.rgstate[ScrollTrackHitTest] &= ~STATE_SYSTEM_PRESSED;
857             NewInfo.rgstate[ScrollTrackHitTest] = ScrollBarInfo.rgstate[ScrollTrackHitTest];
858             NtUserSetScrollBarInfo(Wnd, IntScrollGetObjectId(SBType), &NewInfo);
859 
860             IntDrawScrollInterior(Wnd,Dc,SBType,Vertical,&ScrollBarInfo);
861             IntDrawScrollArrows(Dc, &ScrollBarInfo, Vertical);
862         }
863         break;
864 
865       case WM_SYSTIMER:
866         Pt = PrevPt;
867         HitTest = IntScrollHitTest(&ScrollBarInfo, Vertical, Pt, FALSE);
868         break;
869 
870       default:
871           return;  /* Should never happen */
872   }
873 
874   TRACE("Event: hwnd=%p bar=%d msg=%s pt=%d,%d hit=%d\n",
875             Wnd, SBType, SPY_GetMsgName(Msg,Wnd), Pt.x, Pt.y, HitTest );
876 
877   switch (ScrollTrackHitTest)
878     {
879       case SCROLL_NOWHERE:  /* No tracking in progress */
880         break;
881 
882       case SCROLL_TOP_ARROW:
883         if (HitTest == ScrollTrackHitTest)
884           {
885             if ((WM_LBUTTONDOWN == Msg) || (WM_SYSTIMER == Msg))
886               {
887                 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
888                              SB_LINEUP, (LPARAM) WndCtl);
889               }
890 	    SetSystemTimer(Wnd, SCROLL_TIMER, (WM_LBUTTONDOWN == Msg) ?
891                            SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
892                            (TIMERPROC) NULL);
893           }
894         else
895           {
896             KillSystemTimer(Wnd, SCROLL_TIMER);
897           }
898         break;
899 
900       case SCROLL_TOP_RECT:
901         if (HitTest == ScrollTrackHitTest)
902           {
903             if ((WM_LBUTTONDOWN == Msg) || (WM_SYSTIMER == Msg))
904               {
905                 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
906                              SB_PAGEUP, (LPARAM) WndCtl);
907               }
908             SetSystemTimer(Wnd, SCROLL_TIMER, (WM_LBUTTONDOWN == Msg) ?
909                            SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
910                            (TIMERPROC) NULL);
911           }
912         else
913           {
914             KillSystemTimer(Wnd, SCROLL_TIMER);
915           }
916         break;
917 
918       case SCROLL_THUMB:
919         if (Msg == WM_LBUTTONDOWN)
920           {
921             ScrollTrackingWin = Wnd;
922             ScrollTrackingBar = SBType;
923             ScrollTrackingPos = TrackThumbPos + LastMousePos - LastClickPos;
924             ScrollTrackingVal = IntScrollGetThumbVal(Wnd, SBType, &ScrollBarInfo,
925                                                      Vertical, ScrollTrackingPos);
926             NewInfo.nTrackPos = ScrollTrackingVal;
927             NtUserSetScrollBarInfo(Wnd, IntScrollGetObjectId(SBType), &NewInfo);
928             IntScrollDrawMovingThumb(Dc, &ScrollBarInfo, Vertical);
929           }
930         else if (Msg == WM_LBUTTONUP)
931           {
932             ScrollTrackingWin = 0;
933             ScrollTrackingVal = 0;
934             IntDrawScrollInterior(Wnd, Dc, SBType, Vertical, &ScrollBarInfo);
935           }
936         else  /* WM_MOUSEMOVE */
937           {
938             UINT Pos;
939 
940             if (! IntScrollPtInRectEx(&ScrollBarInfo.rcScrollBar, Pt, Vertical))
941               {
942                 Pos = LastClickPos;
943               }
944             else
945               {
946                 Pt = IntScrollClipPos(&ScrollBarInfo.rcScrollBar, Pt);
947 		Pos = Vertical ? (Pt.y - ScrollBarInfo.rcScrollBar.top)
948                                : (Pt.x - ScrollBarInfo.rcScrollBar.left);
949               }
950             if (Pos != LastMousePos || ! ScrollMovingThumb)
951               {
952                 LastMousePos = Pos;
953                 ScrollTrackingPos = TrackThumbPos + Pos - LastClickPos;
954                 ScrollTrackingVal = IntScrollGetThumbVal(Wnd, SBType, &ScrollBarInfo,
955                                                          Vertical, ScrollTrackingPos);
956                 NewInfo.nTrackPos = ScrollTrackingVal;
957                 NtUserSetScrollBarInfo(Wnd, IntScrollGetObjectId(SBType), &NewInfo);
958                 IntScrollDrawMovingThumb(Dc, &ScrollBarInfo, Vertical);
959                 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
960                              MAKEWPARAM(SB_THUMBTRACK, ScrollTrackingVal),
961                              (LPARAM) WndCtl);
962              }
963         }
964         break;
965 
966       case SCROLL_BOTTOM_RECT:
967         if (HitTest == ScrollTrackHitTest)
968           {
969             if ((Msg == WM_LBUTTONDOWN) || (Msg == WM_SYSTIMER))
970               {
971                 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
972                              SB_PAGEDOWN, (LPARAM) WndCtl);
973               }
974             SetSystemTimer(Wnd, SCROLL_TIMER, (WM_LBUTTONDOWN == Msg) ?
975                            SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
976                            (TIMERPROC) NULL);
977           }
978         else
979           {
980             KillSystemTimer(Wnd, SCROLL_TIMER);
981           }
982         break;
983 
984       case SCROLL_BOTTOM_ARROW:
985         if (HitTest == ScrollTrackHitTest)
986           {
987             if ((Msg == WM_LBUTTONDOWN) || (Msg == WM_SYSTIMER))
988               {
989                 SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
990                              SB_LINEDOWN, (LPARAM) WndCtl);
991               }
992 	    SetSystemTimer(Wnd, SCROLL_TIMER, (WM_LBUTTONDOWN == Msg) ?
993                            SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY,
994                            (TIMERPROC) NULL);
995           }
996         else KillSystemTimer(Wnd, SCROLL_TIMER);
997         break;
998     }
999 
1000   if (Msg == WM_LBUTTONDOWN)
1001     {
1002       if (SCROLL_THUMB == HitTest)
1003         {
1004           UINT Val = IntScrollGetThumbVal(Wnd, SBType, &ScrollBarInfo, Vertical,
1005                                           TrackThumbPos + LastMousePos - LastClickPos);
1006           SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
1007                        MAKEWPARAM(SB_THUMBTRACK, Val), (LPARAM) WndCtl);
1008         }
1009     }
1010 
1011   if (Msg == WM_LBUTTONUP)
1012     {
1013       HitTest = ScrollTrackHitTest;
1014       ScrollTrackHitTest = SCROLL_NOWHERE;  /* Terminate tracking */
1015 
1016       if (SCROLL_THUMB == HitTest)
1017         {
1018           UINT Val = IntScrollGetThumbVal(Wnd, SBType, &ScrollBarInfo, Vertical,
1019                                           TrackThumbPos + LastMousePos - LastClickPos);
1020           SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
1021                        MAKEWPARAM(SB_THUMBPOSITION, Val), (LPARAM) WndCtl);
1022         }
1023       /* SB_ENDSCROLL doesn't report thumb position */
1024       SendMessageW(WndOwner, Vertical ? WM_VSCROLL : WM_HSCROLL,
1025                    SB_ENDSCROLL, (LPARAM) WndCtl);
1026 
1027       /* Terminate tracking */
1028       ScrollTrackingWin = 0;
1029     }
1030 
1031   ReleaseDC(Wnd, Dc);
1032 }
1033 
1034 
1035 /***********************************************************************
1036  *           IntScrollCreateScrollBar
1037  *
1038  *  Create a scroll bar
1039  */
1040 static void IntScrollCreateScrollBar(
1041   HWND Wnd /* [in] Handle of window with scrollbar(s) */,
1042   LPCREATESTRUCTW lpCreate /* [in] The style and place of the scroll bar */)
1043 {
1044   SCROLLINFO Info;
1045 
1046   Info.cbSize = sizeof(SCROLLINFO);
1047   Info.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
1048   Info.nMin = 0;
1049   Info.nMax = 0;
1050   Info.nPage = 0;
1051   Info.nPos = 0;
1052   Info.nTrackPos = 0;
1053   NtUserSetScrollInfo(Wnd, SB_CTL, &Info, FALSE);
1054 
1055   TRACE("hwnd=%p lpCreate=%p\n", Wnd, lpCreate);
1056 
1057 #if 0 /* FIXME */
1058   if (lpCreate->style & WS_DISABLED)
1059   {
1060   //    info->flags = ESB_DISABLE_BOTH;
1061       //NtUserEnableScrollBar(Wnd,SB_CTL,(wParam ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH));
1062       NtUserMessageCall( Wnd, WM_ENABLE, FALSE, 0, 0, FNID_SCROLLBAR, FALSE);
1063       ERR("Created WS_DISABLED scrollbar\n");
1064   }
1065 #endif
1066   if (0 != (lpCreate->style & (SBS_SIZEGRIP | SBS_SIZEBOX)))
1067     {
1068       if (0 != (lpCreate->style & SBS_SIZEBOXTOPLEFTALIGN))
1069         {
1070           MoveWindow(Wnd, lpCreate->x, lpCreate->y, GetSystemMetrics(SM_CXVSCROLL) + 1,
1071                      GetSystemMetrics(SM_CYHSCROLL) + 1, FALSE);
1072         }
1073       else if (0 != (lpCreate->style & SBS_SIZEBOXBOTTOMRIGHTALIGN))
1074         {
1075           MoveWindow(Wnd, lpCreate->x + lpCreate->cx - GetSystemMetrics(SM_CXVSCROLL) - 1,
1076                      lpCreate->y + lpCreate->cy - GetSystemMetrics(SM_CYHSCROLL) - 1,
1077                      GetSystemMetrics(SM_CXVSCROLL) + 1,
1078                      GetSystemMetrics(SM_CYHSCROLL) + 1, FALSE);
1079         }
1080     }
1081   else if (0 != (lpCreate->style & SBS_VERT))
1082     {
1083       if (0 != (lpCreate->style & SBS_LEFTALIGN))
1084         {
1085           MoveWindow(Wnd, lpCreate->x, lpCreate->y,
1086                      GetSystemMetrics(SM_CXVSCROLL) + 1, lpCreate->cy, FALSE);
1087         }
1088       else if (0 != (lpCreate->style & SBS_RIGHTALIGN))
1089         {
1090           MoveWindow(Wnd,
1091                      lpCreate->x + lpCreate->cx - GetSystemMetrics(SM_CXVSCROLL) - 1,
1092                      lpCreate->y,
1093                       GetSystemMetrics(SM_CXVSCROLL) + 1, lpCreate->cy, FALSE);
1094         }
1095     }
1096   else  /* SBS_HORZ */
1097     {
1098       if (0 != (lpCreate->style & SBS_TOPALIGN))
1099         {
1100           MoveWindow(Wnd, lpCreate->x, lpCreate->y,
1101                      lpCreate->cx, GetSystemMetrics(SM_CYHSCROLL) + 1, FALSE);
1102         }
1103       else if (0 != (lpCreate->style & SBS_BOTTOMALIGN))
1104         {
1105           MoveWindow(Wnd,
1106                      lpCreate->x,
1107                      lpCreate->y + lpCreate->cy - GetSystemMetrics(SM_CYHSCROLL) - 1,
1108                      lpCreate->cx, GetSystemMetrics(SM_CYHSCROLL) + 1, FALSE);
1109         }
1110     }
1111 }
1112 
1113 /* USER32 INTERNAL FUNCTIONS **************************************************/
1114 
1115 /***********************************************************************
1116  *           ScrollTrackScrollBar
1117  *
1118  * Track a mouse button press on a scroll-bar.
1119  * pt is in screen-coordinates for non-client scroll bars.
1120  */
1121 VOID FASTCALL
1122 ScrollTrackScrollBar(HWND Wnd, INT SBType, POINT Pt)
1123 {
1124   MSG Msg;
1125   UINT XOffset = 0, YOffset = 0;
1126 
1127   if (SBType != SB_CTL)
1128   { // Used with CMD mouse tracking.
1129       PWND pwnd = ValidateHwnd(Wnd);
1130       if (!pwnd) return;
1131       XOffset = pwnd->rcClient.left - pwnd->rcWindow.left;
1132       YOffset = pwnd->rcClient.top - pwnd->rcWindow.top;
1133 //      RECT rect;
1134 //      WIN_GetRectangles( Wnd, COORDS_CLIENT, &rect, NULL );
1135       ScreenToClient(Wnd, &Pt);
1136 //      Pt.x -= rect.left;
1137 //      Pt.y -= rect.top;
1138       Pt.x += XOffset;
1139       Pt.y += YOffset;
1140   }
1141 
1142   IntScrollHandleScrollEvent(Wnd, SBType, WM_LBUTTONDOWN, Pt);
1143 
1144   do
1145   {
1146       if (!GetMessageW(&Msg, 0, 0, 0)) break;
1147       if (CallMsgFilterW(&Msg, MSGF_SCROLLBAR)) continue;
1148       if ( Msg.message == WM_LBUTTONUP ||
1149            Msg.message == WM_MOUSEMOVE ||
1150           (Msg.message == WM_SYSTIMER && Msg.wParam == SCROLL_TIMER))
1151       {
1152           Pt.x = LOWORD(Msg.lParam) + XOffset;
1153           Pt.y = HIWORD(Msg.lParam) + YOffset;
1154           IntScrollHandleScrollEvent(Wnd, SBType, Msg.message, Pt);
1155       }
1156       else
1157       {
1158           TranslateMessage(&Msg);
1159           DispatchMessageW(&Msg);
1160       }
1161       if (!IsWindow(Wnd))
1162       {
1163           ReleaseCapture();
1164           break;
1165       }
1166    } while (Msg.message != WM_LBUTTONUP && GetCapture() == Wnd);
1167 }
1168 
1169 
1170 static DWORD FASTCALL
1171 IntSetScrollInfo(HWND Wnd, LPCSCROLLINFO Info, BOOL bRedraw)
1172 {
1173    DWORD Ret = NtUserSetScrollInfo(Wnd, SB_CTL, Info, bRedraw);
1174    if (Ret) IntNotifyWinEvent(EVENT_OBJECT_VALUECHANGE, Wnd, OBJID_CLIENT, CHILDID_SELF, WEF_SETBYWNDPTI);
1175    return Ret;
1176 }
1177 
1178 
1179 /***********************************************************************
1180  *           ScrollBarWndProc
1181  */
1182 LRESULT WINAPI
1183 ScrollBarWndProc_common(WNDPROC DefWindowProc, HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, BOOL unicode )
1184 {
1185 #ifdef __REACTOS__ // Do this now, remove after Server side is fixed.
1186   PWND pWnd;
1187   PSBWND pSBWnd;
1188   SCROLLINFO ScrollInfo;
1189 
1190   pWnd = ValidateHwnd(Wnd);
1191   if (pWnd)
1192   {
1193      if (!pWnd->fnid)
1194      {
1195         TRACE("ScrollBar CTL size %d\n", (sizeof(SBWND)-sizeof(WND)));
1196         if ( pWnd->cbwndExtra < (sizeof(SBWND)-sizeof(WND)) )
1197         {
1198            ERR("Wrong Extra bytes for Scrollbar!\n");
1199            return 0;
1200         }
1201 
1202         if (Msg != WM_CREATE)
1203         {
1204            return DefWindowProc(Wnd, Msg, wParam, lParam);
1205         }
1206         NtUserSetWindowFNID(Wnd, FNID_SCROLLBAR);
1207      }
1208      else
1209      {
1210         if (pWnd->fnid != FNID_SCROLLBAR)
1211         {
1212            ERR("Wrong window class for Scrollbar!\n");
1213            return 0;
1214         }
1215      }
1216   }
1217 #endif
1218 
1219   if (! IsWindow(Wnd))
1220     {
1221       return 0;
1222     }
1223 
1224   // Must be a scroll bar control!
1225   pSBWnd = (PSBWND)pWnd;
1226 
1227   switch (Msg)
1228     {
1229       case WM_CREATE:
1230         IntScrollCreateScrollBar(Wnd, (LPCREATESTRUCTW) lParam);
1231         break;
1232 
1233       case WM_ENABLE:
1234         {
1235           return SendMessageW( Wnd, SBM_ENABLE_ARROWS, wParam ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH, 0);
1236         }
1237 
1238       case WM_LBUTTONDBLCLK:
1239       case WM_LBUTTONDOWN:
1240         if (GetWindowLongW( Wnd, GWL_STYLE ) & SBS_SIZEGRIP)
1241         {
1242            SendMessageW( GetParent(Wnd), WM_SYSCOMMAND,
1243                          SC_SIZE + ((GetWindowLongW( Wnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) ?
1244                                    WMSZ_BOTTOMLEFT : WMSZ_BOTTOMRIGHT), lParam );
1245         }
1246         else
1247         {
1248           POINT Pt;
1249           Pt.x = (short)LOWORD(lParam);
1250           Pt.y = (short)HIWORD(lParam);
1251           ScrollTrackScrollBar(Wnd, SB_CTL, Pt);
1252 	}
1253         break;
1254 
1255       case WM_LBUTTONUP:
1256       case WM_MOUSEMOVE:
1257       case WM_SYSTIMER:
1258         {
1259           POINT Pt;
1260           Pt.x = (short)LOWORD(lParam);
1261           Pt.y = (short)HIWORD(lParam);
1262           IntScrollHandleScrollEvent(Wnd, SB_CTL, Msg, Pt);
1263         }
1264         break;
1265 
1266       case WM_KEYDOWN:
1267         IntScrollHandleKbdEvent(Wnd, wParam, lParam);
1268         break;
1269 
1270       case WM_KEYUP:
1271         ShowCaret(Wnd);
1272         break;
1273 
1274       case WM_SETFOCUS:
1275         {
1276           /* Create a caret when a ScrollBar get focus */
1277           RECT Rect;
1278           int ArrowSize, ThumbSize, ThumbPos, Vertical;
1279 
1280           Vertical = IntScrollGetScrollBarRect(Wnd, SB_CTL, &Rect,
1281                                                &ArrowSize, &ThumbSize, &ThumbPos);
1282           if (! Vertical)
1283             {
1284               CreateCaret(Wnd, (HBITMAP) 1, ThumbSize - 2, Rect.bottom - Rect.top - 2);
1285               SetCaretPos(ThumbPos + 1, Rect.top + 1);
1286             }
1287           else
1288             {
1289               CreateCaret(Wnd, (HBITMAP) 1, Rect.right - Rect.left - 2, ThumbSize - 2);
1290               SetCaretPos(Rect.top + 1, ThumbPos + 1);
1291             }
1292           ShowCaret(Wnd);
1293         }
1294         break;
1295 
1296       case WM_KILLFOCUS:
1297         {
1298           RECT Rect;
1299           int ArrowSize, ThumbSize, ThumbPos, Vertical;
1300 
1301           Vertical = IntScrollGetScrollBarRect(Wnd, SB_CTL, &Rect,
1302                                                &ArrowSize, &ThumbSize, &ThumbPos);
1303           if (! Vertical)
1304             {
1305               Rect.left = ThumbPos + 1;
1306               Rect.right = Rect.left + ThumbSize;
1307             }
1308           else
1309             {
1310               Rect.top = ThumbPos + 1;
1311               Rect.bottom = Rect.top + ThumbSize;
1312             }
1313           HideCaret(Wnd);
1314           InvalidateRect(Wnd, &Rect, FALSE);
1315           DestroyCaret();
1316         }
1317         break;
1318 
1319       case WM_ERASEBKGND:
1320          return 1;
1321 
1322       case WM_GETDLGCODE:
1323          return DLGC_WANTARROWS; /* Windows returns this value */
1324 
1325       case WM_PAINT:
1326         {
1327           PAINTSTRUCT Ps;
1328           HDC Dc;
1329 
1330           Dc = (0 != wParam ? (HDC) wParam : BeginPaint(Wnd, &Ps));
1331 
1332           if (GetWindowLongPtrW(Wnd, GWL_STYLE) & SBS_SIZEGRIP)
1333             {
1334               IntScrollDrawSizeGrip(Wnd, Dc);
1335             }
1336           else if (0 != (GetWindowLongPtrW(Wnd, GWL_STYLE) & SBS_SIZEBOX))
1337             {
1338               RECT Rect;
1339               GetClientRect(Wnd, &Rect);
1340               FillRect(Dc, &Rect, GetSysColorBrush(COLOR_SCROLLBAR));
1341             }
1342           else
1343             {
1344               IntDrawScrollBar(Wnd, Dc, SB_CTL/*, TRUE, TRUE*/);
1345             }
1346 
1347           if (0 == wParam)
1348             {
1349               EndPaint(Wnd, &Ps);
1350             }
1351         }
1352         break;
1353 
1354       case SBM_GETPOS:
1355         return pSBWnd->SBCalc.pos;
1356 
1357       case SBM_GETRANGE:
1358         *(LPINT)wParam = pSBWnd->SBCalc.posMin;
1359         *(LPINT)lParam = pSBWnd->SBCalc.posMax;
1360         // This message does not return a value.
1361         return 0;
1362 
1363       case SBM_ENABLE_ARROWS:
1364         return EnableScrollBar( Wnd, SB_CTL, wParam );
1365 
1366       case SBM_SETPOS:
1367         {
1368            ScrollInfo.cbSize = sizeof(SCROLLINFO);
1369            ScrollInfo.fMask = SIF_POS|SIF_PREVIOUSPOS;
1370            ScrollInfo.nPos = wParam;
1371            return IntSetScrollInfo(Wnd, &ScrollInfo, lParam);
1372         }
1373 
1374       case SBM_SETRANGEREDRAW:
1375       case SBM_SETRANGE:
1376         {
1377            ScrollInfo.cbSize = sizeof(SCROLLINFO);
1378            ScrollInfo.fMask = SIF_RANGE|SIF_PREVIOUSPOS;
1379            ScrollInfo.nMin = wParam;
1380            ScrollInfo.nMax = lParam;
1381            return IntSetScrollInfo(Wnd, &ScrollInfo, Msg == SBM_SETRANGEREDRAW ? TRUE : FALSE);
1382         }
1383 
1384       case SBM_SETSCROLLINFO:
1385         return IntSetScrollInfo(Wnd, (LPCSCROLLINFO)lParam, wParam);
1386 
1387       case SBM_GETSCROLLINFO:
1388         {
1389          PSBDATA pSBData = (PSBDATA)&pSBWnd->SBCalc;
1390          DWORD ret = NtUserSBGetParms(Wnd, SB_CTL, pSBData, (SCROLLINFO *) lParam);
1391          if (!ret)
1392          {
1393             ERR("SBM_GETSCROLLINFO No ScrollInfo\n");
1394          }
1395          return ret;
1396         }
1397       case SBM_GETSCROLLBARINFO:
1398         ((PSCROLLBARINFO)lParam)->cbSize = sizeof(SCROLLBARINFO);
1399         return NtUserGetScrollBarInfo(Wnd, OBJID_CLIENT, (PSCROLLBARINFO)lParam);
1400 
1401       case 0x00e5:
1402       case 0x00e7:
1403       case 0x00e8:
1404       case 0x00ec:
1405       case 0x00ed:
1406       case 0x00ee:
1407       case 0x00ef:
1408         WARN("unknown Win32 msg %04x wp=%08lx lp=%08lx\n",
1409 		Msg, wParam, lParam );
1410         break;
1411 
1412       default:
1413         if (WM_USER <= Msg)
1414           {
1415             WARN("unknown msg %04x wp=%04lx lp=%08lx\n", Msg, wParam, lParam);
1416           }
1417         if (unicode)
1418             return DefWindowProcW( Wnd, Msg, wParam, lParam );
1419         else
1420             return DefWindowProcA( Wnd, Msg, wParam, lParam );
1421     }
1422 
1423   return 0;
1424 }
1425 
1426 LRESULT WINAPI
1427 ScrollBarWndProcW(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam)
1428 {
1429   return ScrollBarWndProc_common(DefWindowProcW, Wnd, Msg, wParam, lParam, TRUE);
1430 }
1431 
1432 LRESULT WINAPI
1433 ScrollBarWndProcA(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam)
1434 {
1435   return ScrollBarWndProc_common(DefWindowProcA, Wnd, Msg, wParam, lParam, FALSE);
1436 }
1437 
1438 
1439 /* PUBLIC FUNCTIONS ***********************************************************/
1440 
1441 /*
1442  * @implemented
1443  */
1444 BOOL
1445 WINAPI
1446 DECLSPEC_HOTPATCH
1447 EnableScrollBar( HWND hwnd, UINT nBar, UINT flags )
1448 {
1449    BOOL Hook, Ret = FALSE;
1450 
1451    LoadUserApiHook();
1452 
1453    Hook = BeginIfHookedUserApiHook();
1454 
1455      /* Bypass SEH and go direct. */
1456    if (!Hook)
1457    {
1458       Ret = NtUserEnableScrollBar(hwnd, nBar, flags);
1459       if (!Ret) return Ret;
1460       SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, TRUE );
1461       return Ret;
1462    }
1463    _SEH2_TRY
1464    {
1465       Ret = guah.EnableScrollBar(hwnd, nBar, flags);
1466    }
1467    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1468    {
1469        ERR("Got exception in hooked EnableScrollBar!\n");
1470    }
1471    _SEH2_END;
1472 
1473    EndUserApiHook();
1474 
1475    return Ret;
1476 }
1477 
1478 BOOL WINAPI
1479 RealGetScrollInfo(HWND Wnd, INT SBType, LPSCROLLINFO Info)
1480 {
1481   PWND pWnd;
1482   PSBDATA pSBData;
1483 
1484   if (SB_CTL == SBType)
1485   {
1486      return SendMessageW(Wnd, SBM_GETSCROLLINFO, 0, (LPARAM) Info);
1487   }
1488 
1489   pWnd = ValidateHwnd(Wnd);
1490   if (!pWnd) return FALSE;
1491 
1492   if (SBType < SB_HORZ || SBType > SB_VERT)
1493   {
1494      SetLastError(ERROR_INVALID_PARAMETER);
1495      return FALSE;
1496   }
1497   if (!pWnd->pSBInfo)
1498   {
1499      SetLastError(ERROR_NO_SCROLLBARS);
1500      return FALSE;
1501   }
1502   pSBData = IntGetSBData(pWnd, SBType);
1503   return NtUserSBGetParms(Wnd, SBType, pSBData, Info);
1504 }
1505 
1506 /*
1507  * @implemented
1508  */
1509 BOOL WINAPI GetScrollBarInfo( _In_ HWND hwnd, _In_ LONG idObject, _Inout_ LPSCROLLBARINFO info)
1510 {
1511     BOOL Ret;
1512     PWND pWnd = ValidateHwnd(hwnd);
1513     TRACE("hwnd=%p idObject=%d info=%p\n", hwnd, idObject, info);
1514     if (!pWnd) return FALSE;
1515     Ret = NtUserGetScrollBarInfo(hwnd, idObject, info); // This will be fixed once SB is server side.
1516     /* rcScrollBar needs to be in screen coordinates */
1517     OffsetRect( &(info->rcScrollBar), pWnd->rcWindow.left, pWnd->rcWindow.top );
1518     return Ret;
1519 }
1520 
1521 /*
1522  * @implemented
1523  */
1524 BOOL
1525 WINAPI
1526 DECLSPEC_HOTPATCH
1527 GetScrollInfo(HWND Wnd, INT SBType, LPSCROLLINFO Info)
1528 {
1529    BOOL Hook, Ret = FALSE;
1530 
1531    LoadUserApiHook();
1532 
1533    Hook = BeginIfHookedUserApiHook();
1534 
1535      /* Bypass SEH and go direct. */
1536    if (!Hook) return RealGetScrollInfo(Wnd, SBType, Info);
1537 
1538    _SEH2_TRY
1539    {
1540       Ret = guah.GetScrollInfo(Wnd, SBType, Info);
1541    }
1542    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1543    {
1544        ERR("Got exception in hooked GetScrollInfo!\n");
1545    }
1546    _SEH2_END;
1547 
1548    EndUserApiHook();
1549 
1550    return Ret;
1551 }
1552 
1553 /*
1554  * @implemented
1555  */
1556 INT
1557 WINAPI
1558 DECLSPEC_HOTPATCH
1559 GetScrollPos(HWND Wnd, INT Bar)
1560 {
1561   PWND pwnd;
1562   PSBDATA pSBData;
1563 
1564   TRACE("Wnd=%p Bar=%d\n", Wnd, Bar);
1565 
1566   /* Refer SB_CTL requests to the window */
1567   if (SB_CTL == Bar)
1568   {
1569       return SendMessageW(Wnd, SBM_GETPOS, (WPARAM) 0, (LPARAM) 0);
1570   }
1571   else if (Bar == SB_HORZ || Bar == SB_VERT )
1572   {
1573      pwnd = ValidateHwnd(Wnd);
1574      if (!pwnd) return 0;
1575 
1576      if (pwnd->pSBInfo)
1577      {
1578         pSBData = IntGetSBData(pwnd, Bar);
1579         return pSBData->pos;
1580      }
1581 
1582      SetLastError(ERROR_NO_SCROLLBARS);
1583      TRACE("GetScrollPos No Scroll Info\n");
1584      return 0;
1585   }
1586   SetLastError(ERROR_INVALID_PARAMETER);
1587   return 0;
1588 }
1589 
1590 /*
1591  * @implemented
1592  */
1593 BOOL
1594 WINAPI
1595 DECLSPEC_HOTPATCH
1596 GetScrollRange(HWND Wnd, int Bar, LPINT MinPos, LPINT MaxPos)
1597 {
1598   PWND pwnd;
1599   PSBDATA pSBData;
1600 
1601   TRACE("Wnd=%x Bar=%d Min=%p Max=%p\n", Wnd, Bar, MinPos, MaxPos);
1602 
1603   /* Refer SB_CTL requests to the window */
1604   if (SB_CTL == Bar)
1605   {
1606       return SendMessageW(Wnd, SBM_GETRANGE, (WPARAM) MinPos, (LPARAM) MaxPos);
1607   }
1608   else if (Bar == SB_HORZ || Bar == SB_VERT )
1609   {
1610       pwnd = ValidateHwnd(Wnd);
1611       if (!pwnd) return FALSE;
1612 
1613       if (pwnd->pSBInfo)
1614       {
1615          pSBData = IntGetSBData(pwnd, Bar);
1616          *MinPos = pSBData->posMin;
1617          *MaxPos = pSBData->posMax;
1618       }
1619       else
1620       {
1621          SetLastError(ERROR_NO_SCROLLBARS);
1622          *MinPos = 0;
1623          *MaxPos = 0;
1624       }
1625       return TRUE;
1626   }
1627   SetLastError(ERROR_INVALID_PARAMETER);
1628   return FALSE;
1629 }
1630 
1631 INT WINAPI
1632 RealSetScrollInfo(HWND Wnd, int SBType, LPCSCROLLINFO Info, BOOL bRedraw)
1633 {
1634   if (SB_CTL == SBType)
1635     {
1636       return SendMessageW(Wnd, SBM_SETSCROLLINFO, (WPARAM) bRedraw, (LPARAM) Info);
1637     }
1638   else
1639     {
1640       return NtUserSetScrollInfo(Wnd, SBType, Info, bRedraw);
1641     }
1642 }
1643 
1644 /*
1645  * @implemented
1646  */
1647 INT
1648 WINAPI
1649 DECLSPEC_HOTPATCH
1650 SetScrollInfo(HWND Wnd, int SBType, LPCSCROLLINFO Info, BOOL bRedraw)
1651 {
1652    BOOL Hook;
1653    INT Ret = 0;
1654 
1655    LoadUserApiHook();
1656 
1657    Hook = BeginIfHookedUserApiHook();
1658 
1659      /* Bypass SEH and go direct. */
1660    if (!Hook) return RealSetScrollInfo(Wnd, SBType, Info, bRedraw);
1661 
1662    _SEH2_TRY
1663    {
1664       Ret = guah.SetScrollInfo(Wnd, SBType, Info, bRedraw);
1665    }
1666    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1667    {
1668        ERR("Got exception in hooked SetScrollInfo!\n");
1669    }
1670    _SEH2_END;
1671 
1672    EndUserApiHook();
1673 
1674    return Ret;
1675 
1676 }
1677 
1678 /*
1679  * @implemented
1680  */
1681 INT
1682 WINAPI
1683 DECLSPEC_HOTPATCH
1684 SetScrollPos(HWND hWnd, INT nBar, INT nPos, BOOL bRedraw)
1685 {
1686   SCROLLINFO ScrollInfo;
1687 
1688   ScrollInfo.cbSize = sizeof(SCROLLINFO);
1689   ScrollInfo.fMask = SIF_POS|SIF_PREVIOUSPOS;
1690   ScrollInfo.nPos = nPos;
1691 
1692   return RealSetScrollInfo(hWnd, nBar, &ScrollInfo, bRedraw);
1693 }
1694 
1695 /*
1696  * @implemented
1697  */
1698 BOOL
1699 WINAPI
1700 DECLSPEC_HOTPATCH
1701 SetScrollRange(HWND hWnd, INT nBar, INT nMinPos, INT nMaxPos, BOOL bRedraw)
1702 {
1703   PWND pWnd;
1704   SCROLLINFO ScrollInfo;
1705 
1706   pWnd = ValidateHwnd(hWnd);
1707   if ( !pWnd ) return FALSE;
1708 
1709   if (((LONGLONG)nMaxPos - nMinPos) > MAXLONG)
1710   {
1711      SetLastError(ERROR_INVALID_SCROLLBAR_RANGE);
1712      return FALSE;
1713   }
1714 
1715   ScrollInfo.cbSize = sizeof(SCROLLINFO);
1716   ScrollInfo.fMask = SIF_RANGE;
1717   ScrollInfo.nMin = nMinPos;
1718   ScrollInfo.nMax = nMaxPos;
1719   SetScrollInfo(hWnd, nBar, &ScrollInfo, bRedraw); // do not bypass themes.
1720   return TRUE;
1721 }
1722