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