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