ca5186041a7d67ef082138459e3f571559d42cbb
[reactos.git] / reactos / win32ss / user / user32 / controls / scrollbar.c
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 ((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 }