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