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