4 * Copyright 1993 Martin Ayotte
5 * Copyright 1994, 1996 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "ntgdi_private.h"
27 #include "ntuser_private.h"
28 #include "wine/debug.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(scroll
);
33 #define SCROLLBAR_MAGIC 0x5c6011ba
35 /* Minimum size of the rectangle between the arrows */
36 #define SCROLL_MIN_RECT 4
38 /* Minimum size of the thumb in pixels */
39 #define SCROLL_MIN_THUMB 8
41 /* Overlap between arrows and thumb */
42 #define SCROLL_ARROW_THUMB_OVERLAP 0
44 /* Delay (in ms) before first repetition when holding the button down */
45 #define SCROLL_FIRST_DELAY 200
47 /* Delay (in ms) between scroll repetitions */
48 #define SCROLL_REPEAT_DELAY 50
51 #define SCROLL_TIMER 0
53 /* What to do after set_scroll_info() */
54 #define SA_SSI_HIDE 0x0001
55 #define SA_SSI_SHOW 0x0002
56 #define SA_SSI_REFRESH 0x0004
57 #define SA_SSI_REPAINT_ARROWS 0x0008
59 /* Scroll Bar tracking information */
60 static struct SCROLL_TRACKING_INFO g_tracking_info
;
62 /* Is the moving thumb being displayed? */
63 static BOOL scroll_moving_thumb
= FALSE
;
65 /* data for window that has (one or two) scroll bars */
66 struct win_scroll_bar_info
68 struct scroll_info horz
;
69 struct scroll_info vert
;
72 #define SCROLLBAR_MAGIC 0x5c6011ba
75 static struct scroll_info
*get_scroll_info_ptr( HWND hwnd
, int bar
, BOOL alloc
)
77 struct scroll_info
*info
= NULL
;
78 WND
*win
= get_win_ptr( hwnd
);
80 if (!win
|| win
== WND_OTHER_PROCESS
|| win
== WND_DESKTOP
) return NULL
;
85 if (win
->pScroll
) info
= &win
->pScroll
->horz
;
88 if (win
->pScroll
) info
= &win
->pScroll
->vert
;
91 if (win
->cbWndExtra
>= sizeof(struct scroll_bar_win_data
))
93 struct scroll_bar_win_data
*data
= (struct scroll_bar_win_data
*)win
->wExtra
;
94 if (data
->magic
== SCROLLBAR_MAGIC
) info
= &data
->info
;
96 if (!info
) WARN( "window is not a scrollbar control\n" );
99 WARN( "with SB_BOTH\n" );
105 struct win_scroll_bar_info
*win_info
;
107 if (bar
!= SB_HORZ
&& bar
!= SB_VERT
)
108 WARN("Cannot initialize bar=%d\n",bar
);
109 else if ((win_info
= malloc( sizeof(struct win_scroll_bar_info
) )))
111 /* Set default values */
112 win_info
->horz
.minVal
= 0;
113 win_info
->horz
.curVal
= 0;
114 win_info
->horz
.page
= 0;
115 /* From MSDN and our own tests:
116 * max for a standard scroll bar is 100 by default. */
117 win_info
->horz
.maxVal
= 100;
118 win_info
->horz
.flags
= ESB_ENABLE_BOTH
;
119 win_info
->vert
= win_info
->horz
;
120 win
->pScroll
= win_info
;
121 info
= bar
== SB_HORZ
? &win_info
->horz
: &win_info
->vert
;
125 if (info
) user_lock();
126 release_win_ptr( win
);
130 static void release_scroll_info_ptr( struct scroll_info
*info
)
135 static BOOL
show_scroll_bar( HWND hwnd
, int bar
, BOOL show_horz
, BOOL show_vert
)
137 ULONG old_style
, set_bits
= 0, clear_bits
= 0;
139 TRACE( "hwnd=%p bar=%d horz=%d, vert=%d\n", hwnd
, bar
, show_horz
, show_vert
);
144 NtUserShowWindow( hwnd
, show_horz
? SW_SHOW
: SW_HIDE
);
149 if (show_horz
) set_bits
|= WS_HSCROLL
;
150 else clear_bits
|= WS_HSCROLL
;
151 if (bar
== SB_HORZ
) break;
154 if (show_vert
) set_bits
|= WS_VSCROLL
;
155 else clear_bits
|= WS_VSCROLL
;
159 return FALSE
; /* Nothing to do! */
162 old_style
= set_window_style( hwnd
, set_bits
, clear_bits
);
163 if ((old_style
& clear_bits
) != 0 || (old_style
& set_bits
) != set_bits
)
165 /* frame has been changed, let the window redraw itself */
166 NtUserSetWindowPos( hwnd
, 0, 0, 0, 0, 0,
167 SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_FRAMECHANGED
);
170 return FALSE
; /* no frame changes */
173 /***********************************************************************
174 * get_scroll_bar_rect
176 * Compute the scroll bar rectangle, in drawing coordinates (i.e. client
177 * coords for SB_CTL, window coords for SB_VERT and SB_HORZ).
178 * 'arrow_size' returns the width or height of an arrow (depending on
179 * the orientation of the scrollbar), 'thumb_size' returns the size of
180 * the thumb, and 'thumb_pos' returns the position of the thumb
181 * relative to the left or to the top.
182 * Return TRUE if the scrollbar is vertical, FALSE if horizontal.
184 static BOOL
get_scroll_bar_rect( HWND hwnd
, int bar
, RECT
*rect
, int *arrow_size
,
185 int *thumb_size
, int *thumb_pos
)
187 int pixels
, min_thumb_size
;
189 WND
*win
= get_win_ptr( hwnd
);
191 if (!win
|| win
== WND_OTHER_PROCESS
|| win
== WND_DESKTOP
) return FALSE
;
196 get_window_rects( hwnd
, COORDS_WINDOW
, NULL
, rect
, get_thread_dpi() );
197 rect
->top
= rect
->bottom
;
198 rect
->bottom
+= get_system_metrics( SM_CYHSCROLL
);
199 if (win
->dwStyle
& WS_VSCROLL
) rect
->right
++;
204 get_window_rects( hwnd
, COORDS_WINDOW
, NULL
, rect
, get_thread_dpi() );
205 if (win
->dwExStyle
& WS_EX_LEFTSCROLLBAR
)
207 rect
->right
= rect
->left
;
208 rect
->left
-= get_system_metrics( SM_CXVSCROLL
);
212 rect
->left
= rect
->right
;
213 rect
->right
+= get_system_metrics( SM_CXVSCROLL
);
215 if (win
->dwStyle
& WS_HSCROLL
) rect
->bottom
++;
220 get_client_rect( hwnd
, rect
);
221 vertical
= (win
->dwStyle
& SBS_VERT
) != 0;
225 release_win_ptr( win
);
229 if (vertical
) pixels
= rect
->bottom
- rect
->top
;
230 else pixels
= rect
->right
- rect
->left
;
232 if (pixels
<= 2 * get_system_metrics( SM_CXVSCROLL
) + SCROLL_MIN_RECT
)
234 if (pixels
> SCROLL_MIN_RECT
)
235 *arrow_size
= (pixels
- SCROLL_MIN_RECT
) / 2;
238 *thumb_pos
= *thumb_size
= 0;
242 struct scroll_info
*info
= get_scroll_info_ptr( hwnd
, bar
, TRUE
);
245 WARN( "called for missing scroll bar\n" );
246 release_win_ptr( win
);
249 *arrow_size
= get_system_metrics( SM_CXVSCROLL
);
250 pixels
-= 2 * (get_system_metrics( SM_CXVSCROLL
) - SCROLL_ARROW_THUMB_OVERLAP
);
254 *thumb_size
= muldiv( pixels
,info
->page
, info
->maxVal
- info
->minVal
+ 1 );
255 min_thumb_size
= muldiv( SCROLL_MIN_THUMB
, get_dpi_for_window( hwnd
), 96 );
256 if (*thumb_size
< min_thumb_size
) *thumb_size
= min_thumb_size
;
258 else *thumb_size
= get_system_metrics( SM_CXVSCROLL
);
260 if ((pixels
-= *thumb_size
) < 0 || (info
->flags
& ESB_DISABLE_BOTH
) == ESB_DISABLE_BOTH
)
262 /* Rectangle too small or scrollbar disabled -> no thumb */
263 *thumb_pos
= *thumb_size
= 0;
267 int max
= info
->maxVal
- max( info
->page
-1, 0 );
268 if (info
->minVal
>= max
)
269 *thumb_pos
= *arrow_size
- SCROLL_ARROW_THUMB_OVERLAP
;
271 *thumb_pos
= *arrow_size
- SCROLL_ARROW_THUMB_OVERLAP
272 + muldiv( pixels
, info
->curVal
- info
->minVal
, max
- info
->minVal
);
274 release_scroll_info_ptr( info
);
276 release_win_ptr( win
);
280 /***********************************************************************
283 * Redraw the whole scrollbar.
285 static void draw_scroll_bar( HWND hwnd
, HDC hdc
, int bar
, enum SCROLL_HITTEST hit_test
,
286 const struct SCROLL_TRACKING_INFO
*tracking_info
, BOOL draw_arrows
,
289 struct draw_scroll_bar_params params
;
290 struct scroll_info
*info
;
291 RECT clip_box
, intersect
;
296 if (!(hwnd
= get_full_window_handle( hwnd
)))
299 style
= get_window_long( hwnd
, GWL_STYLE
);
300 if ((bar
== SB_VERT
&& !(style
& WS_VSCROLL
)) || (bar
== SB_HORZ
&& !(style
& WS_HSCROLL
)))
303 if (!is_window_drawable( hwnd
, FALSE
))
306 if (!(info
= get_scroll_info_ptr( hwnd
, bar
, TRUE
))) return;
307 params
.enable_flags
= info
->flags
;
308 release_scroll_info_ptr( info
);
310 if (bar
== SB_CTL
&& get_window_long( hwnd
, GWL_STYLE
) & (SBS_SIZEGRIP
| SBS_SIZEBOX
))
312 get_client_rect( hwnd
, ¶ms
.rect
);
313 params
.arrow_size
= 0;
314 params
.thumb_pos
= 0;
315 params
.thumb_size
= 0;
316 params
.vertical
= FALSE
;
322 params
.vertical
= get_scroll_bar_rect( hwnd
, bar
, ¶ms
.rect
, ¶ms
.arrow_size
,
323 ¶ms
.thumb_size
, ¶ms
.thumb_pos
);
325 if (scroll_moving_thumb
&& tracking_info
->win
== hwnd
&& tracking_info
->bar
== bar
)
327 max_size
= params
.vertical
?
328 params
.rect
.bottom
- params
.rect
.top
: params
.rect
.right
- params
.rect
.left
;
329 max_size
-= params
.arrow_size
- SCROLL_ARROW_THUMB_OVERLAP
+ params
.thumb_size
;
331 pos
= tracking_info
->thumb_pos
;
332 if (pos
< params
.arrow_size
- SCROLL_ARROW_THUMB_OVERLAP
)
333 pos
= params
.arrow_size
- SCROLL_ARROW_THUMB_OVERLAP
;
334 else if (pos
> max_size
)
337 params
.thumb_pos
= pos
;
341 /* do not draw if the scrollbar rectangle is empty */
342 if (IsRectEmpty( ¶ms
.rect
))
345 TRACE( "hwnd %p, hdc %p, bar %d, hit_test %d, tracking_info(win %p, bar %d, thumb_pos %d, "
346 "track_pos %d, vertical %d, hit_test %d), draw_arrows %d, draw_interior %d, rect %s, "
347 "arrow_size %d, thumb_pos %d, thumb_val %d, vertical %d, captured window %p\n", hwnd
, hdc
,
348 bar
, hit_test
, tracking_info
->win
, tracking_info
->bar
, tracking_info
->thumb_pos
,
349 tracking_info
->thumb_val
, tracking_info
->vertical
, tracking_info
->hit_test
, draw_arrows
,
350 draw_interior
, wine_dbgstr_rect(¶ms
.rect
), params
.arrow_size
, params
.thumb_pos
,
351 params
.thumb_size
, params
.vertical
, get_capture() );
356 params
.hit_test
= hit_test
;
357 params
.tracking_info
= *tracking_info
;
358 params
.arrows
= draw_arrows
;
359 params
.interior
= draw_interior
;
360 KeUserModeCallback( NtUserDrawScrollBar
, ¶ms
, sizeof(params
), &ret_ptr
, &ret_len
);
362 if (bar
== SB_HORZ
|| bar
== SB_VERT
)
364 NtGdiGetAppClipBox( hdc
, &clip_box
);
365 if (intersect_rect( &intersect
, ¶ms
.rect
, &clip_box
))
366 set_standard_scroll_painted( hwnd
, bar
, TRUE
);
370 void draw_nc_scrollbar( HWND hwnd
, HDC hdc
, BOOL draw_horizontal
, BOOL draw_vertical
)
373 draw_scroll_bar( hwnd
, hdc
, SB_HORZ
, g_tracking_info
.hit_test
, &g_tracking_info
, TRUE
, TRUE
);
375 draw_scroll_bar( hwnd
, hdc
, SB_VERT
, g_tracking_info
.hit_test
, &g_tracking_info
, TRUE
, TRUE
);
378 static void refresh_scroll_bar( HWND hwnd
, int nBar
, BOOL arrows
, BOOL interior
)
380 HDC hdc
= NtUserGetDCEx( hwnd
, 0, DCX_CACHE
| (nBar
== SB_CTL
? 0 : DCX_WINDOW
) );
383 draw_scroll_bar( hwnd
, hdc
, nBar
, g_tracking_info
.hit_test
, &g_tracking_info
, arrows
, interior
);
384 NtUserReleaseDC( hwnd
, hdc
);
387 static BOOL
point_in_scroll_rect( RECT
*r
, POINT pt
, BOOL vertical
)
392 /* Pad hit rect to allow mouse to be dragged outside of scrollbar and
393 * still be considered in the scrollbar. */
396 width
= r
->right
- r
->left
;
397 InflateRect( &rect
, width
* 8, width
* 2 );
401 width
= r
->bottom
- r
->top
;
402 InflateRect( &rect
, width
* 2, width
* 8 );
404 return PtInRect( &rect
, pt
);
407 /***********************************************************************
410 * Scroll-bar hit testing (don't confuse this with WM_NCHITTEST!).
412 static enum SCROLL_HITTEST
scroll_hit_test( HWND hwnd
, int bar
, POINT pt
, BOOL dragging
)
414 int arrow_size
, thumb_size
, thumb_pos
;
418 vertical
= get_scroll_bar_rect( hwnd
, bar
, &rect
, &arrow_size
, &thumb_size
, &thumb_pos
);
420 if ((dragging
&& !point_in_scroll_rect( &rect
, pt
, vertical
)) || !PtInRect( &rect
, pt
))
421 return SCROLL_NOWHERE
;
425 if (pt
.y
< rect
.top
+ arrow_size
) return SCROLL_TOP_ARROW
;
426 if (pt
.y
>= rect
.bottom
- arrow_size
) return SCROLL_BOTTOM_ARROW
;
427 if (!thumb_pos
) return SCROLL_TOP_RECT
;
429 if (pt
.y
< thumb_pos
) return SCROLL_TOP_RECT
;
430 if (pt
.y
>= thumb_pos
+ thumb_size
) return SCROLL_BOTTOM_RECT
;
432 else /* horizontal */
434 if (pt
.x
< rect
.left
+ arrow_size
) return SCROLL_TOP_ARROW
;
435 if (pt
.x
>= rect
.right
- arrow_size
) return SCROLL_BOTTOM_ARROW
;
436 if (!thumb_pos
) return SCROLL_TOP_RECT
;
438 if (pt
.x
< thumb_pos
) return SCROLL_TOP_RECT
;
439 if (pt
.x
>= thumb_pos
+ thumb_size
) return SCROLL_BOTTOM_RECT
;
444 static BOOL
is_standard_scroll_painted( HWND hwnd
, int bar
)
446 struct scroll_info
*info
;
449 if (bar
!= SB_HORZ
&& bar
!= SB_VERT
) return FALSE
;
450 if (!(info
= get_scroll_info_ptr( hwnd
, bar
, FALSE
))) return FALSE
;
452 release_scroll_info_ptr( info
);
456 /***********************************************************************
459 * Compute the current scroll position based on the thumb position in pixels
460 * from the top of the scroll-bar.
462 static UINT
get_thumb_val( HWND hwnd
, int bar
, RECT
*rect
, BOOL vertical
, int pos
)
464 int thumb_size
, min_thumb_size
, pixels
, range
;
465 struct scroll_info
*info
;
468 pixels
= vertical
? rect
->bottom
-rect
->top
: rect
->right
-rect
->left
;
469 pixels
-= 2 * (get_system_metrics( SM_CXVSCROLL
) - SCROLL_ARROW_THUMB_OVERLAP
);
470 if (!(info
= get_scroll_info_ptr( hwnd
, bar
, FALSE
))) return 0;
476 thumb_size
= muldiv( pixels
, info
->page
, info
->maxVal
- info
->minVal
+ 1 );
477 min_thumb_size
= muldiv( SCROLL_MIN_THUMB
, get_dpi_for_window( hwnd
), 96 );
478 if (thumb_size
< min_thumb_size
) thumb_size
= min_thumb_size
;
480 else thumb_size
= get_system_metrics( SM_CXVSCROLL
);
482 if ((pixels
-= thumb_size
) > 0)
484 pos
= max( 0, pos
- (get_system_metrics( SM_CXVSCROLL
) - SCROLL_ARROW_THUMB_OVERLAP
));
485 if (pos
> pixels
) pos
= pixels
;
488 range
= info
->maxVal
- info
->minVal
;
490 range
= info
->maxVal
- info
->minVal
- info
->page
+ 1;
492 ret
= info
->minVal
+ muldiv( pos
, range
, pixels
);
496 release_scroll_info_ptr( info
);
500 static POINT
clip_scroll_pos( RECT
*rect
, POINT pt
)
502 if (pt
.x
< rect
->left
)
504 else if (pt
.x
> rect
->right
)
507 if (pt
.y
< rect
->top
)
509 else if (pt
.y
> rect
->bottom
)
515 /***********************************************************************
516 * handle_scroll_event
518 * Handle a mouse or timer event for the scrollbar.
519 * 'pt' is the location of the mouse event in client (for SB_CTL) or
520 * windows coordinates.
522 void handle_scroll_event( HWND hwnd
, int bar
, UINT msg
, POINT pt
)
524 /* Previous mouse position for timer events. */
525 static POINT prev_pt
;
526 /* Thumb position when tracking started. */
527 static UINT track_thumb_pos
;
528 /* Position in the scroll-bar of the last button-down event. */
529 static int last_click_pos
;
530 /* Position in the scroll-bar of the last mouse event. */
531 static int last_mouse_pos
;
533 int arrow_size
, thumb_size
, thumb_pos
;
534 enum SCROLL_HITTEST hittest
;
535 HWND owner_hwnd
, ctl_hwnd
;
536 struct scroll_info
*info
;
542 if (!(info
= get_scroll_info_ptr( hwnd
, bar
, FALSE
))) return;
543 release_scroll_info_ptr( info
);
545 if (g_tracking_info
.hit_test
== SCROLL_NOWHERE
&&
546 (msg
!= WM_LBUTTONDOWN
&& msg
!= WM_MOUSEMOVE
&& msg
!= WM_MOUSELEAVE
&&
547 msg
!= WM_NCMOUSEMOVE
&& msg
!= WM_NCMOUSELEAVE
))
550 if (bar
== SB_CTL
&& (get_window_long( hwnd
, GWL_STYLE
) & (SBS_SIZEGRIP
| SBS_SIZEBOX
)))
554 case WM_LBUTTONDOWN
: /* Initialise mouse tracking */
555 NtUserHideCaret( hwnd
); /* hide caret while holding down LBUTTON */
556 NtUserSetCapture( hwnd
);
558 g_tracking_info
.hit_test
= hittest
= SCROLL_THUMB
;
561 get_client_rect( get_parent( get_parent( hwnd
)), &rect
);
566 g_tracking_info
.hit_test
= hittest
= SCROLL_NOWHERE
;
567 if (hwnd
== get_focus()) NtUserShowCaret( hwnd
);
576 hdc
= NtUserGetDCEx( hwnd
, 0, DCX_CACHE
| (bar
== SB_CTL
? 0 : DCX_WINDOW
));
577 vertical
= get_scroll_bar_rect( hwnd
, bar
, &rect
, &arrow_size
, &thumb_size
, &thumb_pos
);
578 owner_hwnd
= bar
== SB_CTL
? get_parent( hwnd
) : hwnd
;
579 ctl_hwnd
= bar
== SB_CTL
? hwnd
: 0;
583 case WM_LBUTTONDOWN
: /* Initialise mouse tracking */
584 NtUserHideCaret( hwnd
); /* hide caret while holding down LBUTTON */
585 g_tracking_info
.vertical
= vertical
;
586 g_tracking_info
.hit_test
= hittest
= scroll_hit_test( hwnd
, bar
, pt
, FALSE
);
587 last_click_pos
= vertical
? pt
.y
- rect
.top
: pt
.x
- rect
.left
;
588 last_mouse_pos
= last_click_pos
;
589 track_thumb_pos
= thumb_pos
;
591 if (bar
== SB_CTL
&& (get_window_long( hwnd
, GWL_STYLE
) & WS_TABSTOP
))
592 NtUserSetFocus( hwnd
);
593 NtUserSetCapture( hwnd
);
597 hittest
= scroll_hit_test( hwnd
, bar
, pt
,
598 vertical
== g_tracking_info
.vertical
&& get_capture() == hwnd
);
604 tme
.cbSize
= sizeof(tme
);
605 tme
.dwFlags
= TME_QUERY
;
606 NtUserTrackMouseEvent( &tme
);
607 if (!(tme
.dwFlags
& TME_LEAVE
) || tme
.hwndTrack
!= hwnd
)
609 tme
.dwFlags
= TME_LEAVE
;
610 tme
.hwndTrack
= hwnd
;
611 NtUserTrackMouseEvent( &tme
);
616 hittest
= scroll_hit_test( hwnd
, bar
, pt
,
617 vertical
== g_tracking_info
.vertical
&& get_capture() == hwnd
);
623 tme
.cbSize
= sizeof(tme
);
624 tme
.dwFlags
= TME_QUERY
;
625 NtUserTrackMouseEvent( &tme
);
626 if (((tme
.dwFlags
& (TME_NONCLIENT
| TME_LEAVE
)) != (TME_NONCLIENT
| TME_LEAVE
)) ||
627 tme
.hwndTrack
!= hwnd
)
629 tme
.dwFlags
= TME_NONCLIENT
| TME_LEAVE
;
630 tme
.hwndTrack
= hwnd
;
631 NtUserTrackMouseEvent( &tme
);
636 case WM_NCMOUSELEAVE
:
640 hittest
= SCROLL_NOWHERE
;
647 hittest
= SCROLL_NOWHERE
;
651 hittest
= SCROLL_NOWHERE
;
653 /* if scrollbar has focus, show back caret */
654 if (hwnd
== get_focus()) NtUserShowCaret( hwnd
);
659 hittest
= scroll_hit_test( hwnd
, bar
, pt
, FALSE
);
663 return; /* Should never happen */
666 TRACE( "Event: hwnd=%p bar=%d msg=%s pt=%d,%d hit=%d\n",
667 hwnd
, bar
, debugstr_msg_name( msg
, hwnd
), (int)pt
.x
, (int)pt
.y
, hittest
);
669 switch (g_tracking_info
.hit_test
)
671 case SCROLL_NOWHERE
: /* No tracking in progress */
672 /* For standard scroll bars, hovered state gets painted only when the scroll bar was
673 * previously painted by DefWinProc(). If an application handles WM_NCPAINT by itself, then
674 * the scrollbar shouldn't be repainted here to avoid overwriting the application painted
676 if (msg
== WM_MOUSEMOVE
|| msg
== WM_MOUSELEAVE
||
677 ((msg
== WM_NCMOUSEMOVE
|| msg
== WM_NCMOUSELEAVE
) &&
678 is_standard_scroll_painted( hwnd
, bar
)))
679 draw_scroll_bar( hwnd
, hdc
, bar
, hittest
, &g_tracking_info
, TRUE
, TRUE
);
682 case SCROLL_TOP_ARROW
:
683 draw_scroll_bar( hwnd
, hdc
, bar
, hittest
, &g_tracking_info
, TRUE
, FALSE
);
684 if (hittest
== g_tracking_info
.hit_test
)
686 if ((msg
== WM_LBUTTONDOWN
) || (msg
== WM_SYSTIMER
))
688 send_message( owner_hwnd
, vertical
? WM_VSCROLL
: WM_HSCROLL
,
689 SB_LINEUP
, (LPARAM
)ctl_hwnd
);
692 NtUserSetSystemTimer( hwnd
, SCROLL_TIMER
,
693 msg
== WM_LBUTTONDOWN
? SCROLL_FIRST_DELAY
: SCROLL_REPEAT_DELAY
);
695 else NtUserKillSystemTimer( hwnd
, SCROLL_TIMER
);
698 case SCROLL_TOP_RECT
:
699 draw_scroll_bar( hwnd
, hdc
, bar
, hittest
, &g_tracking_info
, FALSE
, TRUE
);
700 if (hittest
== g_tracking_info
.hit_test
)
702 if (msg
== WM_LBUTTONDOWN
|| msg
== WM_SYSTIMER
)
704 send_message( owner_hwnd
, vertical
? WM_VSCROLL
: WM_HSCROLL
,
705 SB_PAGEUP
, (LPARAM
)ctl_hwnd
);
707 NtUserSetSystemTimer( hwnd
, SCROLL_TIMER
,
708 msg
== WM_LBUTTONDOWN
? SCROLL_FIRST_DELAY
: SCROLL_REPEAT_DELAY
);
710 else NtUserKillSystemTimer( hwnd
, SCROLL_TIMER
);
714 if (msg
== WM_LBUTTONDOWN
)
716 g_tracking_info
.win
= hwnd
;
717 g_tracking_info
.bar
= bar
;
718 g_tracking_info
.thumb_pos
= track_thumb_pos
+ last_mouse_pos
- last_click_pos
;
719 g_tracking_info
.thumb_val
= get_thumb_val( hwnd
, bar
, &rect
, vertical
,
720 g_tracking_info
.thumb_pos
);
721 if (!scroll_moving_thumb
)
723 scroll_moving_thumb
= TRUE
;
724 draw_scroll_bar( hwnd
, hdc
, bar
, hittest
, &g_tracking_info
, FALSE
, TRUE
);
727 else if (msg
== WM_LBUTTONUP
)
729 draw_scroll_bar( hwnd
, hdc
, bar
, SCROLL_NOWHERE
, &g_tracking_info
, FALSE
, TRUE
);
731 else if (msg
== WM_MOUSEMOVE
)
735 if (!point_in_scroll_rect( &rect
, pt
, vertical
)) pos
= last_click_pos
;
738 pt
= clip_scroll_pos( &rect
, pt
);
739 pos
= vertical
? pt
.y
- rect
.top
: pt
.x
- rect
.left
;
741 if (pos
!= last_mouse_pos
|| !scroll_moving_thumb
)
743 last_mouse_pos
= pos
;
744 g_tracking_info
.thumb_pos
= track_thumb_pos
+ pos
- last_click_pos
;
745 g_tracking_info
.thumb_val
= get_thumb_val( hwnd
, bar
, &rect
, vertical
,
746 g_tracking_info
.thumb_pos
);
747 send_message( owner_hwnd
, vertical
? WM_VSCROLL
: WM_HSCROLL
,
748 MAKEWPARAM( SB_THUMBTRACK
, g_tracking_info
.thumb_val
),
750 scroll_moving_thumb
= TRUE
;
751 draw_scroll_bar( hwnd
, hdc
, bar
, hittest
, &g_tracking_info
, FALSE
, TRUE
);
756 case SCROLL_BOTTOM_RECT
:
757 draw_scroll_bar( hwnd
, hdc
, bar
, hittest
, &g_tracking_info
, FALSE
, TRUE
);
758 if (hittest
== g_tracking_info
.hit_test
)
760 if (msg
== WM_LBUTTONDOWN
|| msg
== WM_SYSTIMER
)
762 send_message( owner_hwnd
, vertical
? WM_VSCROLL
: WM_HSCROLL
,
763 SB_PAGEDOWN
, (LPARAM
)ctl_hwnd
);
765 NtUserSetSystemTimer( hwnd
, SCROLL_TIMER
,
766 msg
== WM_LBUTTONDOWN
? SCROLL_FIRST_DELAY
: SCROLL_REPEAT_DELAY
);
768 else NtUserKillSystemTimer( hwnd
, SCROLL_TIMER
);
771 case SCROLL_BOTTOM_ARROW
:
772 draw_scroll_bar( hwnd
, hdc
, bar
, hittest
, &g_tracking_info
, TRUE
, FALSE
);
773 if (hittest
== g_tracking_info
.hit_test
)
775 if (msg
== WM_LBUTTONDOWN
|| msg
== WM_SYSTIMER
)
777 send_message( owner_hwnd
, vertical
? WM_VSCROLL
: WM_HSCROLL
,
778 SB_LINEDOWN
, (LPARAM
)ctl_hwnd
);
781 NtUserSetSystemTimer( hwnd
, SCROLL_TIMER
,
782 msg
== WM_LBUTTONDOWN
? SCROLL_FIRST_DELAY
: SCROLL_REPEAT_DELAY
);
784 else NtUserKillSystemTimer( hwnd
, SCROLL_TIMER
);
788 if (msg
== WM_LBUTTONDOWN
)
791 if (hittest
== SCROLL_THUMB
)
793 UINT val
= get_thumb_val( hwnd
, bar
, &rect
, vertical
,
794 track_thumb_pos
+ last_mouse_pos
- last_click_pos
);
795 send_message( owner_hwnd
, vertical
? WM_VSCROLL
: WM_HSCROLL
,
796 MAKEWPARAM( SB_THUMBTRACK
, val
), (LPARAM
)ctl_hwnd
);
800 if (msg
== WM_LBUTTONUP
)
802 hittest
= g_tracking_info
.hit_test
;
803 g_tracking_info
.hit_test
= SCROLL_NOWHERE
; /* Terminate tracking */
805 if (hittest
== SCROLL_THUMB
)
807 UINT val
= get_thumb_val( hwnd
, bar
, &rect
, vertical
,
808 track_thumb_pos
+ last_mouse_pos
- last_click_pos
);
809 send_message( owner_hwnd
, vertical
? WM_VSCROLL
: WM_HSCROLL
,
810 MAKEWPARAM( SB_THUMBPOSITION
, val
), (LPARAM
)ctl_hwnd
);
812 /* SB_ENDSCROLL doesn't report thumb position */
813 send_message( owner_hwnd
, vertical
? WM_VSCROLL
: WM_HSCROLL
,
814 SB_ENDSCROLL
, (LPARAM
)ctl_hwnd
);
816 /* Terminate tracking */
817 g_tracking_info
.win
= 0;
818 scroll_moving_thumb
= FALSE
;
819 hittest
= SCROLL_NOWHERE
;
820 draw_scroll_bar( hwnd
, hdc
, bar
, hittest
, &g_tracking_info
, TRUE
, TRUE
);
823 NtUserReleaseDC( hwnd
, hdc
);
826 /***********************************************************************
829 * Track a mouse button press on a scroll-bar.
830 * pt is in screen-coordinates for non-client scroll bars.
832 void track_scroll_bar( HWND hwnd
, int scrollbar
, POINT pt
)
837 if (scrollbar
!= SB_CTL
)
839 get_window_rects( hwnd
, COORDS_CLIENT
, &rect
, NULL
, get_thread_dpi() );
840 screen_to_client( hwnd
, &pt
);
845 rect
.left
= rect
.top
= 0;
847 handle_scroll_event( hwnd
, scrollbar
, WM_LBUTTONDOWN
, pt
);
851 if (!NtUserGetMessage( &msg
, 0, 0, 0 )) break;
852 if (NtUserCallMsgFilter( &msg
, MSGF_SCROLLBAR
)) continue;
853 if (msg
.message
== WM_LBUTTONUP
||
854 msg
.message
== WM_MOUSEMOVE
||
855 msg
.message
== WM_MOUSELEAVE
||
856 msg
.message
== WM_NCMOUSEMOVE
||
857 msg
.message
== WM_NCMOUSELEAVE
||
858 (msg
.message
== WM_SYSTIMER
&& msg
.wParam
== SCROLL_TIMER
))
860 pt
.x
= (short)LOWORD( msg
.lParam
) - rect
.left
;
861 pt
.y
= (short)HIWORD( msg
.lParam
) - rect
.top
;
862 handle_scroll_event( hwnd
, scrollbar
, msg
.message
, pt
);
866 NtUserTranslateMessage( &msg
, 0 );
867 NtUserDispatchMessage( &msg
);
869 if (!is_window( hwnd
))
874 } while (msg
.message
!= WM_LBUTTONUP
&& get_capture() == hwnd
);
877 /***********************************************************************
878 * validate_scroll_info
880 * Determine if the supplied SCROLLINFO struct is valid.
882 static inline BOOL
validate_scroll_info( const SCROLLINFO
*info
)
884 return !(info
->fMask
& ~(SIF_ALL
| SIF_DISABLENOSCROLL
| SIF_RETURNPREV
) ||
885 (info
->cbSize
!= sizeof(*info
) &&
886 info
->cbSize
!= sizeof(*info
) - sizeof(info
->nTrackPos
)));
890 BOOL
get_scroll_info( HWND hwnd
, int bar
, SCROLLINFO
*info
)
892 struct scroll_info
*scroll
;
894 /* handle invalid data structure */
895 if (!validate_scroll_info( info
) || !(scroll
= get_scroll_info_ptr( hwnd
, bar
, FALSE
)))
898 /* fill in the desired scroll info structure */
899 if (info
->fMask
& SIF_PAGE
) info
->nPage
= scroll
->page
;
900 if (info
->fMask
& SIF_POS
) info
->nPos
= scroll
->curVal
;
901 if ((info
->fMask
& SIF_TRACKPOS
) && (info
->cbSize
== sizeof(*info
)))
902 info
->nTrackPos
= g_tracking_info
.win
== get_full_window_handle( hwnd
) ?
903 g_tracking_info
.thumb_val
: scroll
->curVal
;
904 if (info
->fMask
& SIF_RANGE
)
906 info
->nMin
= scroll
->minVal
;
907 info
->nMax
= scroll
->maxVal
;
909 release_scroll_info_ptr( scroll
);
911 TRACE( "cbSize %02x fMask %04x nMin %d nMax %d nPage %u nPos %d nTrackPos %d\n",
912 info
->cbSize
, info
->fMask
, info
->nMin
, info
->nMax
, info
->nPage
,
913 info
->nPos
, info
->nTrackPos
);
915 return (info
->fMask
& SIF_ALL
) != 0;
918 static int set_scroll_info( HWND hwnd
, int bar
, const SCROLLINFO
*info
, BOOL redraw
)
920 struct scroll_info
*scroll
;
922 int action
= 0, ret
= 0;
924 /* handle invalid data structure */
925 if (!validate_scroll_info( info
) ||
926 !(scroll
= get_scroll_info_ptr( hwnd
, bar
, TRUE
)))
929 if (TRACE_ON(scroll
))
931 TRACE( "hwnd=%p bar=%d", hwnd
, bar
);
932 if (info
->fMask
& SIF_PAGE
) TRACE( " page=%d", info
->nPage
);
933 if (info
->fMask
& SIF_POS
) TRACE( " pos=%d", info
->nPos
);
934 if (info
->fMask
& SIF_RANGE
) TRACE( " min=%d max=%d", info
->nMin
, info
->nMax
);
938 /* undocumented flag, return previous position instead of modified */
939 if (info
->fMask
& SIF_RETURNPREV
) ret
= scroll
->curVal
;
941 /* Set the page size */
942 if ((info
->fMask
& SIF_PAGE
) && scroll
->page
!= info
->nPage
)
944 scroll
->page
= info
->nPage
;
945 action
|= SA_SSI_REFRESH
;
948 /* Set the scroll pos */
949 if ((info
->fMask
& SIF_POS
) && scroll
->curVal
!= info
->nPos
)
951 scroll
->curVal
= info
->nPos
;
952 action
|= SA_SSI_REFRESH
;
955 /* Set the scroll range */
956 if (info
->fMask
& SIF_RANGE
)
958 /* Invalid range -> range is set to (0,0) */
959 if (info
->nMin
> info
->nMax
|| (UINT
)(info
->nMax
- info
->nMin
) >= 0x80000000)
961 action
|= SA_SSI_REFRESH
;
967 if (scroll
->minVal
!= info
->nMin
|| scroll
->maxVal
!= info
->nMax
)
969 action
|= SA_SSI_REFRESH
;
970 scroll
->minVal
= info
->nMin
;
971 scroll
->maxVal
= info
->nMax
;
976 /* Make sure the page size is valid */
977 if (scroll
->page
< 0) scroll
->page
= 0;
978 else if (scroll
->page
> scroll
->maxVal
- scroll
->minVal
+ 1)
979 scroll
->page
= scroll
->maxVal
- scroll
->minVal
+ 1;
981 /* Make sure the pos is inside the range */
982 if (scroll
->curVal
< scroll
->minVal
)
983 scroll
->curVal
= scroll
->minVal
;
984 else if (scroll
->curVal
> scroll
->maxVal
- max( scroll
->page
- 1, 0 ))
985 scroll
->curVal
= scroll
->maxVal
- max( scroll
->page
- 1, 0 );
987 TRACE( " new values: page=%d pos=%d min=%d max=%d\n", scroll
->page
, scroll
->curVal
,
988 scroll
->minVal
, scroll
->maxVal
);
990 /* don't change the scrollbar state if SetScrollInfo is just called with SIF_DISABLENOSCROLL */
991 if(!(info
->fMask
& SIF_ALL
)) goto done
;
993 /* Check if the scrollbar should be hidden or disabled */
994 if (info
->fMask
& (SIF_RANGE
| SIF_PAGE
| SIF_DISABLENOSCROLL
))
996 new_flags
= scroll
->flags
;
997 if (scroll
->minVal
>= scroll
->maxVal
- max( scroll
->page
- 1, 0 ))
999 /* Hide or disable scroll-bar */
1000 if (info
->fMask
& SIF_DISABLENOSCROLL
)
1002 new_flags
= ESB_DISABLE_BOTH
;
1003 action
|= SA_SSI_REFRESH
;
1005 else if (bar
!= SB_CTL
&& (action
& SA_SSI_REFRESH
))
1007 action
= SA_SSI_HIDE
;
1010 else if (info
->fMask
!= SIF_PAGE
)
1012 /* Show and enable scroll-bar only if no page only changed. */
1013 new_flags
= ESB_ENABLE_BOTH
;
1014 if (bar
!= SB_CTL
&& (action
& SA_SSI_REFRESH
)) action
|= SA_SSI_SHOW
;
1017 if (bar
== SB_CTL
&& redraw
&& is_window_visible( hwnd
) &&
1018 (new_flags
== ESB_ENABLE_BOTH
|| new_flags
== ESB_DISABLE_BOTH
))
1020 release_scroll_info_ptr( scroll
);
1021 enable_window( hwnd
, new_flags
== ESB_ENABLE_BOTH
);
1022 if (!(scroll
= get_scroll_info_ptr( hwnd
, bar
, FALSE
))) return 0;
1025 if (scroll
->flags
!= new_flags
) /* check arrow flags */
1027 scroll
->flags
= new_flags
;
1028 action
|= SA_SSI_REPAINT_ARROWS
;
1033 if (!(info
->fMask
& SIF_RETURNPREV
)) ret
= scroll
->curVal
;
1034 release_scroll_info_ptr( scroll
);
1035 if (action
& SA_SSI_HIDE
)
1036 show_scroll_bar( hwnd
, bar
, FALSE
, FALSE
);
1039 if (action
& SA_SSI_SHOW
&& show_scroll_bar( hwnd
, bar
, TRUE
, TRUE
))
1040 return ret
; /* NtUserSetWindowPos() already did the painting */
1043 refresh_scroll_bar( hwnd
, bar
, TRUE
, TRUE
);
1044 else if (action
& SA_SSI_REPAINT_ARROWS
)
1045 refresh_scroll_bar( hwnd
, bar
, TRUE
, FALSE
);
1048 return ret
; /* Return current position */
1051 static BOOL
get_scroll_bar_info( HWND hwnd
, LONG id
, SCROLLBARINFO
*info
)
1053 struct scroll_info
*scroll
;
1055 DWORD style
= get_window_long( hwnd
, GWL_STYLE
);
1061 case OBJID_CLIENT
: bar
= SB_CTL
; break;
1062 case OBJID_HSCROLL
: bar
= SB_HORZ
; break;
1063 case OBJID_VSCROLL
: bar
= SB_VERT
; break;
1064 default: return FALSE
;
1067 /* handle invalid data structure */
1068 if (info
->cbSize
!= sizeof(*info
)) return FALSE
;
1070 get_scroll_bar_rect( hwnd
, bar
, &info
->rcScrollBar
, &dummy
,
1071 &info
->dxyLineButton
, &info
->xyThumbTop
);
1072 /* rcScrollBar needs to be in screen coordinates */
1073 get_window_rect( hwnd
, &rect
, get_thread_dpi() );
1074 OffsetRect( &info
->rcScrollBar
, rect
.left
, rect
.top
);
1076 info
->xyThumbBottom
= info
->xyThumbTop
+ info
->dxyLineButton
;
1078 if (!(scroll
= get_scroll_info_ptr( hwnd
, bar
, TRUE
))) return FALSE
;
1080 /* Scroll bar state */
1081 info
->rgstate
[0] = 0;
1082 if ((bar
== SB_HORZ
&& !(style
& WS_HSCROLL
)) ||
1083 (bar
== SB_VERT
&& !(style
& WS_VSCROLL
)))
1084 info
->rgstate
[0] |= STATE_SYSTEM_INVISIBLE
;
1085 if (scroll
->minVal
>= scroll
->maxVal
- max( scroll
->page
- 1, 0 ))
1087 if (!(info
->rgstate
[0] & STATE_SYSTEM_INVISIBLE
))
1088 info
->rgstate
[0] |= STATE_SYSTEM_UNAVAILABLE
;
1090 info
->rgstate
[0] |= STATE_SYSTEM_OFFSCREEN
;
1092 if (bar
== SB_CTL
&& !is_window_enabled( hwnd
))
1093 info
->rgstate
[0] |= STATE_SYSTEM_UNAVAILABLE
;
1095 pressed
= (bar
== SB_VERT
) == g_tracking_info
.vertical
&& get_capture() == hwnd
;
1097 /* Top/left arrow button state. MSDN says top/right, but I don't believe it */
1098 info
->rgstate
[1] = 0;
1099 if (pressed
&& g_tracking_info
.hit_test
== SCROLL_TOP_ARROW
)
1100 info
->rgstate
[1] |= STATE_SYSTEM_PRESSED
;
1101 if (scroll
->flags
& ESB_DISABLE_LTUP
)
1102 info
->rgstate
[1] |= STATE_SYSTEM_UNAVAILABLE
;
1104 /* Page up/left region state. MSDN says up/right, but I don't believe it */
1105 info
->rgstate
[2] = 0;
1106 if (scroll
->curVal
== scroll
->minVal
)
1107 info
->rgstate
[2] |= STATE_SYSTEM_INVISIBLE
;
1108 if (pressed
&& g_tracking_info
.hit_test
== SCROLL_TOP_RECT
)
1109 info
->rgstate
[2] |= STATE_SYSTEM_PRESSED
;
1112 info
->rgstate
[3] = 0;
1113 if (pressed
&& g_tracking_info
.hit_test
== SCROLL_THUMB
)
1114 info
->rgstate
[3] |= STATE_SYSTEM_PRESSED
;
1116 /* Page down/right region state. MSDN says down/left, but I don't believe it */
1117 info
->rgstate
[4] = 0;
1118 if (scroll
->curVal
>= scroll
->maxVal
- 1)
1119 info
->rgstate
[4] |= STATE_SYSTEM_INVISIBLE
;
1120 if (pressed
&& g_tracking_info
.hit_test
== SCROLL_BOTTOM_RECT
)
1121 info
->rgstate
[4] |= STATE_SYSTEM_PRESSED
;
1123 /* Bottom/right arrow button state. MSDN says bottom/left, but I don't believe it */
1124 info
->rgstate
[5] = 0;
1125 if (pressed
&& g_tracking_info
.hit_test
== SCROLL_BOTTOM_ARROW
)
1126 info
->rgstate
[5] |= STATE_SYSTEM_PRESSED
;
1127 if (scroll
->flags
& ESB_DISABLE_RTDN
)
1128 info
->rgstate
[5] |= STATE_SYSTEM_UNAVAILABLE
;
1130 release_scroll_info_ptr( scroll
);
1134 static void create_scroll_bar( HWND hwnd
, CREATESTRUCTW
*create
)
1136 struct scroll_info
*info
= NULL
;
1139 TRACE( "hwnd=%p create=%p\n", hwnd
, create
);
1141 win
= get_win_ptr( hwnd
);
1142 if (win
->cbWndExtra
>= sizeof(struct scroll_bar_win_data
))
1144 struct scroll_bar_win_data
*data
= (struct scroll_bar_win_data
*)win
->wExtra
;
1145 data
->magic
= SCROLLBAR_MAGIC
;
1148 else WARN( "Not enough extra data\n" );
1149 release_win_ptr( win
);
1152 if (create
->style
& WS_DISABLED
)
1154 info
->flags
= ESB_DISABLE_BOTH
;
1155 TRACE( "Created WS_DISABLED scrollbar\n" );
1158 if (create
->style
& (SBS_SIZEGRIP
| SBS_SIZEBOX
))
1160 if (create
->style
& SBS_SIZEBOXTOPLEFTALIGN
)
1161 NtUserMoveWindow( hwnd
, create
->x
, create
->y
, get_system_metrics( SM_CXVSCROLL
) + 1,
1162 get_system_metrics( SM_CYHSCROLL
) + 1, FALSE
);
1163 else if(create
->style
& SBS_SIZEBOXBOTTOMRIGHTALIGN
)
1164 NtUserMoveWindow( hwnd
, create
->x
+ create
->cx
- get_system_metrics( SM_CXVSCROLL
) - 1,
1165 create
->y
+ create
->cy
-get_system_metrics( SM_CYHSCROLL
) - 1,
1166 get_system_metrics( SM_CXVSCROLL
) + 1,
1167 get_system_metrics( SM_CYHSCROLL
) + 1, FALSE
);
1169 else if (create
->style
& SBS_VERT
)
1171 if (create
->style
& SBS_LEFTALIGN
)
1172 NtUserMoveWindow( hwnd
, create
->x
, create
->y
, get_system_metrics( SM_CXVSCROLL
) + 1,
1173 create
->cy
, FALSE
);
1174 else if (create
->style
& SBS_RIGHTALIGN
)
1175 NtUserMoveWindow( hwnd
, create
->x
+ create
->cx
- get_system_metrics( SM_CXVSCROLL
) - 1,
1176 create
->y
, get_system_metrics( SM_CXVSCROLL
) + 1, create
->cy
, FALSE
);
1180 if (create
->style
& SBS_TOPALIGN
)
1181 NtUserMoveWindow( hwnd
, create
->x
, create
->y
, create
->cx
,
1182 get_system_metrics( SM_CYHSCROLL
) + 1, FALSE
);
1183 else if (create
->style
& SBS_BOTTOMALIGN
)
1184 NtUserMoveWindow( hwnd
, create
->x
,
1185 create
->y
+ create
->cy
- get_system_metrics( SM_CYHSCROLL
) - 1,
1186 create
->cx
, get_system_metrics( SM_CYHSCROLL
) + 1, FALSE
);
1190 static void handle_kbd_event( HWND hwnd
, WPARAM wparam
, LPARAM lparam
)
1192 TRACE( "hwnd=%p wparam=%ld lparam=%ld\n", hwnd
, (long)wparam
, lparam
);
1194 /* hide caret on first KEYDOWN to prevent flicker */
1195 if ((lparam
& PFD_DOUBLEBUFFER_DONTCARE
) == 0)
1196 NtUserHideCaret( hwnd
);
1200 case VK_PRIOR
: wparam
= SB_PAGEUP
; break;
1201 case VK_NEXT
: wparam
= SB_PAGEDOWN
; break;
1202 case VK_HOME
: wparam
= SB_TOP
; break;
1203 case VK_END
: wparam
= SB_BOTTOM
; break;
1204 case VK_UP
: wparam
= SB_LINEUP
; break;
1205 case VK_DOWN
: wparam
= SB_LINEDOWN
; break;
1206 case VK_LEFT
: wparam
= SB_LINEUP
; break;
1207 case VK_RIGHT
: wparam
= SB_LINEDOWN
; break;
1211 send_message( get_parent(hwnd
),
1212 (get_window_long( hwnd
, GWL_STYLE
) & SBS_VERT
) ? WM_VSCROLL
: WM_HSCROLL
,
1213 wparam
, (LPARAM
)hwnd
);
1216 static int get_scroll_pos(HWND hwnd
, int bar
)
1218 struct scroll_info
*scroll
= get_scroll_info_ptr( hwnd
, bar
, FALSE
);
1220 if (!scroll
) return 0;
1221 ret
= scroll
->curVal
;
1222 release_scroll_info_ptr( scroll
);
1226 static BOOL
set_scroll_range( HWND hwnd
, int bar
, int min_val
, int max_val
)
1228 struct scroll_info
*scroll
= get_scroll_info_ptr( hwnd
, bar
, FALSE
);
1230 TRACE( "hwnd=%p bar=%d min=%d max=%d\n", hwnd
, bar
, min_val
, max_val
);
1234 scroll
->minVal
= min_val
;
1235 scroll
->maxVal
= max_val
;
1236 release_scroll_info_ptr( scroll
);
1241 static BOOL
get_scroll_range( HWND hwnd
, int nBar
, int *min
, int *max
)
1243 struct scroll_info
*info
;
1245 if (!(info
= get_scroll_info_ptr( hwnd
, nBar
, FALSE
))) return FALSE
;
1246 if (min
) *min
= info
->minVal
;
1247 if (max
) *max
= info
->maxVal
;
1248 release_scroll_info_ptr( info
);
1252 LRESULT
scroll_bar_window_proc( HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
, BOOL ansi
)
1254 if (!is_window( hwnd
)) return 0;
1259 create_scroll_bar( hwnd
, (CREATESTRUCTW
*)lparam
);
1263 handle_kbd_event( hwnd
, wparam
, lparam
);
1267 NtUserShowCaret( hwnd
);
1270 case WM_LBUTTONDBLCLK
:
1271 case WM_LBUTTONDOWN
:
1272 if (get_window_long( hwnd
, GWL_STYLE
) & SBS_SIZEGRIP
)
1274 send_message( get_parent(hwnd
), WM_SYSCOMMAND
,
1275 SC_SIZE
+ ((get_window_long( hwnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) ?
1276 WMSZ_BOTTOMLEFT
: WMSZ_BOTTOMRIGHT
), lparam
);
1281 pt
.x
= (short)LOWORD( lparam
);
1282 pt
.y
= (short)HIWORD( lparam
);
1283 track_scroll_bar( hwnd
, SB_CTL
, pt
);
1288 case WM_NCMOUSEMOVE
:
1289 case WM_NCMOUSELEAVE
:
1295 pt
.x
= (short)LOWORD( lparam
);
1296 pt
.y
= (short)HIWORD( lparam
);
1297 handle_scroll_event( hwnd
, SB_CTL
, msg
, pt
);
1303 struct scroll_info
*scroll
;
1304 if ((scroll
= get_scroll_info_ptr( hwnd
, SB_CTL
, FALSE
)))
1306 scroll
->flags
= wparam
? ESB_ENABLE_BOTH
: ESB_DISABLE_BOTH
;
1307 release_scroll_info_ptr( scroll
);
1308 refresh_scroll_bar( hwnd
, SB_CTL
, TRUE
, TRUE
);
1315 /* Create a caret when a ScrollBar get focus */
1317 int arrow_size
, thumb_size
, thumb_pos
, vertical
;
1318 vertical
= get_scroll_bar_rect( hwnd
, SB_CTL
, &rect
, &arrow_size
,
1319 &thumb_size
, &thumb_pos
);
1322 NtUserCreateCaret( hwnd
, (HBITMAP
)1, thumb_size
- 2, rect
.bottom
- rect
.top
- 2 );
1323 set_caret_pos( thumb_pos
+ 1, rect
.top
+ 1 );
1327 NtUserCreateCaret( hwnd
, (HBITMAP
)1, rect
.right
- rect
.left
- 2, thumb_size
- 2 );
1328 set_caret_pos( rect
.top
+ 1, thumb_pos
+ 1 );
1330 NtUserShowCaret( hwnd
);
1336 int arrow_size
, thumb_size
, thumb_pos
, vertical
;
1338 vertical
= get_scroll_bar_rect( hwnd
, SB_CTL
, &rect
,&arrow_size
, &thumb_size
, &thumb_pos
);
1341 rect
.left
= thumb_pos
+ 1;
1342 rect
.right
= rect
.left
+ thumb_size
;
1346 rect
.top
= thumb_pos
+ 1;
1347 rect
.bottom
= rect
.top
+ thumb_size
;
1349 NtUserHideCaret( hwnd
);
1350 NtUserInvalidateRect( hwnd
, &rect
, 0 );
1359 return DLGC_WANTARROWS
; /* Windows returns this value */
1364 HDC hdc
= wparam
? (HDC
)wparam
: NtUserBeginPaint( hwnd
, &ps
);
1365 draw_scroll_bar( hwnd
, hdc
, SB_CTL
, g_tracking_info
.hit_test
, &g_tracking_info
, TRUE
, TRUE
);
1366 if (!wparam
) NtUserEndPaint( hwnd
, &ps
);
1370 case SBM_SETRANGEREDRAW
:
1373 int old_pos
= get_scroll_pos( hwnd
, SB_CTL
);
1374 set_scroll_range( hwnd
, SB_CTL
, wparam
, lparam
);
1375 if (msg
== SBM_SETRANGEREDRAW
) refresh_scroll_bar( hwnd
, SB_CTL
, TRUE
, TRUE
);
1376 if (old_pos
!= get_scroll_pos( hwnd
, SB_CTL
)) return old_pos
;
1380 case SBM_GETSCROLLINFO
:
1381 return get_scroll_info( hwnd
, SB_CTL
, (SCROLLINFO
*)lparam
);
1383 case SBM_GETSCROLLBARINFO
:
1384 return get_scroll_bar_info( hwnd
, OBJID_CLIENT
, (SCROLLBARINFO
*)lparam
);
1386 case SBM_SETSCROLLINFO
:
1387 return set_scroll_info( hwnd
, SB_CTL
, (SCROLLINFO
*)lparam
, wparam
);
1390 if (get_window_long( hwnd
, GWL_STYLE
) & SBS_SIZEGRIP
)
1392 ULONG_PTR cursor
= (get_window_long( hwnd
, GWL_EXSTYLE
) & WS_EX_LAYOUTRTL
) ?
1393 IDC_SIZENESW
: IDC_SIZENWSE
;
1394 return (LRESULT
)NtUserSetCursor( LoadImageW( 0, (const WCHAR
*)cursor
, IMAGE_CURSOR
,
1395 0, 0, LR_SHARED
| LR_DEFAULTSIZE
));
1397 return default_window_proc( hwnd
, msg
, wparam
, lparam
, ansi
);
1402 info
.cbSize
= sizeof(info
);
1404 info
.fMask
= SIF_POS
| SIF_RETURNPREV
;
1405 return NtUserSetScrollInfo( hwnd
, SB_CTL
, &info
, lparam
);
1409 return get_scroll_pos( hwnd
, SB_CTL
);
1412 return get_scroll_range( hwnd
, SB_CTL
, (int *)wparam
, (int *)lparam
);
1414 case SBM_ENABLE_ARROWS
:
1415 return NtUserEnableScrollBar( hwnd
, SB_CTL
, wparam
);
1424 ERR( "unknown Win32 msg %04x wp=%08lx lp=%08lx\n", msg
, (long)wparam
, lparam
);
1429 WARN( "unknown msg %04x wp=%08lx lp=%08lx\n", msg
, (long)wparam
, lparam
);
1430 return default_window_proc( hwnd
, msg
, wparam
, lparam
, ansi
);
1434 void set_standard_scroll_painted( HWND hwnd
, int bar
, BOOL painted
)
1436 struct scroll_info
*info
;
1438 if (bar
!= SB_HORZ
&& bar
!= SB_VERT
)
1441 if ((info
= get_scroll_info_ptr( hwnd
, bar
, FALSE
)))
1443 info
->painted
= painted
;
1444 release_scroll_info_ptr( info
);
1448 /*************************************************************************
1449 * NtUserShowScrollBar (win32u.@)
1451 BOOL WINAPI
NtUserShowScrollBar( HWND hwnd
, INT bar
, BOOL show
)
1453 if (!hwnd
) return FALSE
;
1455 show_scroll_bar( hwnd
, bar
, bar
== SB_VERT
? 0 : show
, bar
== SB_HORZ
? 0 : show
);
1459 /*************************************************************************
1460 * NtUserGetScrollBarInfo (win32u.@)
1462 BOOL WINAPI
NtUserGetScrollBarInfo( HWND hwnd
, LONG id
, SCROLLBARINFO
*info
)
1464 TRACE( "hwnd=%p id=%d info=%p\n", hwnd
, (int)id
, info
);
1466 /* Refer OBJID_CLIENT requests to the window */
1467 if (id
== OBJID_CLIENT
)
1468 return send_message( hwnd
, SBM_GETSCROLLBARINFO
, 0, (LPARAM
)info
);
1469 return get_scroll_bar_info( hwnd
, id
, info
);
1472 /*************************************************************************
1473 * NtUserEnableScrollBar (win32u.@)
1475 BOOL WINAPI
NtUserEnableScrollBar( HWND hwnd
, UINT bar
, UINT flags
)
1477 struct scroll_info
*scroll
;
1480 flags
&= ESB_DISABLE_BOTH
;
1484 if (!(scroll
= get_scroll_info_ptr( hwnd
, SB_VERT
, TRUE
))) return FALSE
;
1485 check_flags
= scroll
->flags
== flags
;
1486 scroll
->flags
= flags
;
1487 release_scroll_info_ptr( scroll
);
1488 if (!check_flags
) refresh_scroll_bar( hwnd
, SB_VERT
, TRUE
, TRUE
);
1492 check_flags
= bar
!= SB_CTL
;
1494 if (!(scroll
= get_scroll_info_ptr( hwnd
, bar
, TRUE
))) return FALSE
;
1495 if (check_flags
) check_flags
= scroll
->flags
== flags
;
1496 scroll
->flags
= flags
;
1497 release_scroll_info_ptr( scroll
);
1498 if (check_flags
) return FALSE
;
1500 if (bar
== SB_CTL
&& (flags
== ESB_DISABLE_BOTH
|| flags
== ESB_ENABLE_BOTH
))
1501 NtUserEnableWindow( hwnd
, flags
== ESB_ENABLE_BOTH
);
1503 refresh_scroll_bar( hwnd
, bar
, TRUE
, TRUE
);
1508 /*************************************************************************
1509 * NtUserSetScrollInfo (win32u.@)
1511 INT WINAPI
NtUserSetScrollInfo( HWND hwnd
, int bar
, const SCROLLINFO
*info
, BOOL redraw
)
1513 TRACE( "hwnd=%p bar=%d info=%p, redraw=%d\n", hwnd
, bar
, info
, redraw
);
1515 /* Refer SB_CTL requests to the window */
1517 return send_message( hwnd
, SBM_SETSCROLLINFO
, redraw
, (LPARAM
)info
);
1519 return set_scroll_info( hwnd
, bar
, info
, redraw
);