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