cmd: DIR command outputs free space for the path.
[wine.git] / dlls / win32u / scroll.c
blob19a9a1379f407ce835cec77a60fd39eb7c09ceeb
1 /*
2 * Scrollbar control
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
22 #if 0
23 #pragma makedep unix
24 #endif
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
50 /* Scroll timer id */
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;
82 switch (bar)
84 case SB_HORZ:
85 if (win->pScroll) info = &win->pScroll->horz;
86 break;
87 case SB_VERT:
88 if (win->pScroll) info = &win->pScroll->vert;
89 break;
90 case SB_CTL:
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" );
97 break;
98 case SB_BOTH:
99 WARN( "with SB_BOTH\n" );
100 break;
103 if (!info && alloc)
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 );
127 return info;
130 static void release_scroll_info_ptr( struct scroll_info *info )
132 user_unlock();
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 );
141 switch (bar)
143 case SB_CTL:
144 NtUserShowWindow( hwnd, show_horz ? SW_SHOW : SW_HIDE );
145 return TRUE;
147 case SB_BOTH:
148 case SB_HORZ:
149 if (show_horz) set_bits |= WS_HSCROLL;
150 else clear_bits |= WS_HSCROLL;
151 if (bar == SB_HORZ) break;
152 /* fall through */
153 case SB_VERT:
154 if (show_vert) set_bits |= WS_VSCROLL;
155 else clear_bits |= WS_VSCROLL;
156 break;
158 default:
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 );
168 return TRUE;
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;
188 BOOL vertical;
189 WND *win = get_win_ptr( hwnd );
191 if (!win || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
193 switch(bar)
195 case SB_HORZ:
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++;
200 vertical = FALSE;
201 break;
203 case SB_VERT:
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 );
210 else
212 rect->left = rect->right;
213 rect->right += get_system_metrics( SM_CXVSCROLL );
215 if (win->dwStyle & WS_HSCROLL) rect->bottom++;
216 vertical = TRUE;
217 break;
219 case SB_CTL:
220 get_client_rect( hwnd, rect );
221 vertical = (win->dwStyle & SBS_VERT) != 0;
222 break;
224 default:
225 release_win_ptr( win );
226 return FALSE;
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;
236 else
237 *arrow_size = 0;
238 *thumb_pos = *thumb_size = 0;
240 else
242 struct scroll_info *info = get_scroll_info_ptr( hwnd, bar, TRUE );
243 if (!info)
245 WARN( "called for missing scroll bar\n" );
246 release_win_ptr( win );
247 return FALSE;
249 *arrow_size = get_system_metrics( SM_CXVSCROLL );
250 pixels -= 2 * (get_system_metrics( SM_CXVSCROLL ) - SCROLL_ARROW_THUMB_OVERLAP);
252 if (info->page)
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;
265 else
267 int max = info->maxVal - max( info->page-1, 0 );
268 if (info->minVal >= max)
269 *thumb_pos = *arrow_size - SCROLL_ARROW_THUMB_OVERLAP;
270 else
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 );
277 return vertical;
280 /***********************************************************************
281 * draw_scroll_bar
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,
287 BOOL draw_interior )
289 struct draw_scroll_bar_params params;
290 struct scroll_info *info;
291 RECT clip_box, intersect;
292 DWORD style;
293 void *ret_ptr;
294 ULONG ret_len;
296 if (!(hwnd = get_full_window_handle( hwnd )))
297 return;
299 style = get_window_long( hwnd, GWL_STYLE );
300 if ((bar == SB_VERT && !(style & WS_VSCROLL)) || (bar == SB_HORZ && !(style & WS_HSCROLL)))
301 return;
303 if (!is_window_drawable( hwnd, FALSE ))
304 return;
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, &params.rect );
313 params.arrow_size = 0;
314 params.thumb_pos = 0;
315 params.thumb_size = 0;
316 params.vertical = FALSE;
318 else
320 int pos, max_size;
322 params.vertical = get_scroll_bar_rect( hwnd, bar, &params.rect, &params.arrow_size,
323 &params.thumb_size, &params.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)
335 pos = max_size;
337 params.thumb_pos = pos;
341 /* do not draw if the scrollbar rectangle is empty */
342 if (IsRectEmpty( &params.rect ))
343 return;
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(&params.rect), params.arrow_size, params.thumb_pos,
351 params.thumb_size, params.vertical, get_capture() );
353 params.hwnd = hwnd;
354 params.hdc = hdc;
355 params.bar = bar;
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, &params, sizeof(params), &ret_ptr, &ret_len );
362 if (bar == SB_HORZ || bar == SB_VERT)
364 NtGdiGetAppClipBox( hdc, &clip_box );
365 if (intersect_rect( &intersect, &params.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 )
372 if (draw_horizontal)
373 draw_scroll_bar( hwnd, hdc, SB_HORZ, g_tracking_info.hit_test, &g_tracking_info, TRUE, TRUE );
374 if (draw_vertical)
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) );
381 if (!hdc) return;
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 )
389 RECT rect = *r;
390 int width;
392 /* Pad hit rect to allow mouse to be dragged outside of scrollbar and
393 * still be considered in the scrollbar. */
394 if (vertical)
396 width = r->right - r->left;
397 InflateRect( &rect, width * 8, width * 2 );
399 else
401 width = r->bottom - r->top;
402 InflateRect( &rect, width * 2, width * 8 );
404 return PtInRect( &rect, pt );
407 /***********************************************************************
408 * scroll_hit_test
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;
415 BOOL vertical;
416 RECT rect;
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;
423 if (vertical)
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;
428 pt.y -= rect.top;
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;
437 pt.x -= rect.left;
438 if (pt.x < thumb_pos) return SCROLL_TOP_RECT;
439 if (pt.x >= thumb_pos + thumb_size) return SCROLL_BOTTOM_RECT;
441 return SCROLL_THUMB;
444 static BOOL is_standard_scroll_painted( HWND hwnd, int bar )
446 struct scroll_info *info;
447 BOOL ret;
449 if (bar != SB_HORZ && bar != SB_VERT) return FALSE;
450 if (!(info = get_scroll_info_ptr( hwnd, bar, FALSE ))) return FALSE;
451 ret = info->painted;
452 release_scroll_info_ptr( info );
453 return ret;
456 /***********************************************************************
457 * get_thumb_val
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;
466 UINT ret;
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;
471 ret = info->minVal;
472 if (pixels > 0)
474 if (info->page)
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;
487 if (!info->page)
488 range = info->maxVal - info->minVal;
489 else
490 range = info->maxVal - info->minVal - info->page + 1;
492 ret = info->minVal + muldiv( pos, range, pixels );
496 release_scroll_info_ptr( info );
497 return ret;
500 static POINT clip_scroll_pos( RECT *rect, POINT pt )
502 if (pt.x < rect->left)
503 pt.x = rect->left;
504 else if (pt.x > rect->right)
505 pt.x = rect->right;
507 if (pt.y < rect->top)
508 pt.y = rect->top;
509 else if (pt.y > rect->bottom)
510 pt.y = rect->bottom;
512 return pt;
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;
537 TRACKMOUSEEVENT tme;
538 BOOL vertical;
539 RECT rect;
540 HDC hdc;
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))
548 return;
550 if (bar == SB_CTL && (get_window_long( hwnd, GWL_STYLE ) & (SBS_SIZEGRIP | SBS_SIZEBOX)))
552 switch (msg)
554 case WM_LBUTTONDOWN: /* Initialise mouse tracking */
555 NtUserHideCaret( hwnd ); /* hide caret while holding down LBUTTON */
556 NtUserSetCapture( hwnd );
557 prev_pt = pt;
558 g_tracking_info.hit_test = hittest = SCROLL_THUMB;
559 break;
560 case WM_MOUSEMOVE:
561 get_client_rect( get_parent( get_parent( hwnd )), &rect );
562 prev_pt = pt;
563 break;
564 case WM_LBUTTONUP:
565 release_capture();
566 g_tracking_info.hit_test = hittest = SCROLL_NOWHERE;
567 if (hwnd == get_focus()) NtUserShowCaret( hwnd );
568 break;
569 case WM_SYSTIMER:
570 pt = prev_pt;
571 break;
573 return;
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;
581 switch (msg)
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;
590 prev_pt = pt;
591 if (bar == SB_CTL && (get_window_long( hwnd, GWL_STYLE ) & WS_TABSTOP))
592 NtUserSetFocus( hwnd );
593 NtUserSetCapture( hwnd );
594 break;
596 case WM_MOUSEMOVE:
597 hittest = scroll_hit_test( hwnd, bar, pt,
598 vertical == g_tracking_info.vertical && get_capture() == hwnd );
599 prev_pt = pt;
601 if (bar != SB_CTL)
602 break;
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 );
613 break;
615 case WM_NCMOUSEMOVE:
616 hittest = scroll_hit_test( hwnd, bar, pt,
617 vertical == g_tracking_info.vertical && get_capture() == hwnd );
618 prev_pt = pt;
620 if (bar == SB_CTL)
621 break;
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 );
634 break;
636 case WM_NCMOUSELEAVE:
637 if (bar == SB_CTL)
638 return;
640 hittest = SCROLL_NOWHERE;
641 break;
643 case WM_MOUSELEAVE:
644 if (bar != SB_CTL)
645 return;
647 hittest = SCROLL_NOWHERE;
648 break;
650 case WM_LBUTTONUP:
651 hittest = SCROLL_NOWHERE;
652 release_capture();
653 /* if scrollbar has focus, show back caret */
654 if (hwnd == get_focus()) NtUserShowCaret( hwnd );
655 break;
657 case WM_SYSTIMER:
658 pt = prev_pt;
659 hittest = scroll_hit_test( hwnd, bar, pt, FALSE );
660 break;
662 default:
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
675 * content */
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 );
680 break;
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 );
696 break;
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 );
711 break;
713 case SCROLL_THUMB:
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)
733 int pos;
735 if (!point_in_scroll_rect( &rect, pt, vertical )) pos = last_click_pos;
736 else
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 ),
749 (LPARAM)ctl_hwnd );
750 scroll_moving_thumb = TRUE;
751 draw_scroll_bar( hwnd, hdc, bar, hittest, &g_tracking_info, FALSE, TRUE );
754 break;
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 );
769 break;
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 );
785 break;
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 /***********************************************************************
827 * track_scroll_bar
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 )
834 MSG msg;
835 RECT rect;
837 if (scrollbar != SB_CTL)
839 get_window_rects( hwnd, COORDS_CLIENT, &rect, NULL, get_thread_dpi() );
840 screen_to_client( hwnd, &pt );
841 pt.x -= rect.left;
842 pt.y -= rect.top;
844 else
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 );
864 else
866 NtUserTranslateMessage( &msg, 0 );
867 NtUserDispatchMessage( &msg );
869 if (!is_window( hwnd ))
871 release_capture();
872 break;
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 )))
896 return 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;
921 UINT new_flags;
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 )))
927 return 0;
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 );
935 TRACE( "\n" );
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;
962 scroll->minVal = 0;
963 scroll->maxVal = 0;
965 else
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;
1032 done:
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 );
1037 else
1039 if (action & SA_SSI_SHOW && show_scroll_bar( hwnd, bar, TRUE, TRUE ))
1040 return ret; /* NtUserSetWindowPos() already did the painting */
1042 if (redraw)
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;
1054 int bar, dummy;
1055 DWORD style = get_window_long( hwnd, GWL_STYLE );
1056 BOOL pressed;
1057 RECT rect;
1059 switch (id)
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;
1089 else
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;
1111 /* Thumb state */
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 );
1131 return TRUE;
1134 static void create_scroll_bar( HWND hwnd, CREATESTRUCTW *create )
1136 struct scroll_info *info = NULL;
1137 WND *win;
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;
1146 info = &data->info;
1148 else WARN( "Not enough extra data\n" );
1149 release_win_ptr( win );
1150 if (!info) return;
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 );
1178 else /* SBS_HORZ */
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 );
1198 switch (wparam)
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;
1208 default: return;
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 );
1219 int ret;
1220 if (!scroll) return 0;
1221 ret = scroll->curVal;
1222 release_scroll_info_ptr( scroll );
1223 return ret;
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 );
1232 if (scroll)
1234 scroll->minVal = min_val;
1235 scroll->maxVal = max_val;
1236 release_scroll_info_ptr( scroll );
1238 return TRUE;
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 );
1249 return TRUE;
1252 LRESULT scroll_bar_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, BOOL ansi )
1254 if (!is_window( hwnd )) return 0;
1256 switch (msg)
1258 case WM_CREATE:
1259 create_scroll_bar( hwnd, (CREATESTRUCTW *)lparam );
1260 return 0;
1262 case WM_KEYDOWN:
1263 handle_kbd_event( hwnd, wparam, lparam );
1264 return 0;
1266 case WM_KEYUP:
1267 NtUserShowCaret( hwnd );
1268 return 0;
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 );
1278 else
1280 POINT pt;
1281 pt.x = (short)LOWORD( lparam );
1282 pt.y = (short)HIWORD( lparam );
1283 track_scroll_bar( hwnd, SB_CTL, pt );
1285 return 0;
1287 case WM_LBUTTONUP:
1288 case WM_NCMOUSEMOVE:
1289 case WM_NCMOUSELEAVE:
1290 case WM_MOUSEMOVE:
1291 case WM_MOUSELEAVE:
1292 case WM_SYSTIMER:
1294 POINT pt;
1295 pt.x = (short)LOWORD( lparam );
1296 pt.y = (short)HIWORD( lparam );
1297 handle_scroll_event( hwnd, SB_CTL, msg, pt );
1299 return 0;
1301 case WM_ENABLE:
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 );
1311 return 0;
1313 case WM_SETFOCUS:
1315 /* Create a caret when a ScrollBar get focus */
1316 RECT rect;
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 );
1320 if (!vertical)
1322 NtUserCreateCaret( hwnd, (HBITMAP)1, thumb_size - 2, rect.bottom - rect.top - 2 );
1323 set_caret_pos( thumb_pos + 1, rect.top + 1 );
1325 else
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 );
1332 return 0;
1334 case WM_KILLFOCUS:
1336 int arrow_size, thumb_size, thumb_pos, vertical;
1337 RECT rect;
1338 vertical = get_scroll_bar_rect( hwnd, SB_CTL, &rect,&arrow_size, &thumb_size, &thumb_pos );
1339 if (!vertical)
1341 rect.left = thumb_pos + 1;
1342 rect.right = rect.left + thumb_size;
1344 else
1346 rect.top = thumb_pos + 1;
1347 rect.bottom = rect.top + thumb_size;
1349 NtUserHideCaret( hwnd );
1350 NtUserInvalidateRect( hwnd, &rect, 0 );
1351 destroy_caret();
1353 return 0;
1355 case WM_ERASEBKGND:
1356 return 1;
1358 case WM_GETDLGCODE:
1359 return DLGC_WANTARROWS; /* Windows returns this value */
1361 case WM_PAINT:
1363 PAINTSTRUCT ps;
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 );
1368 return 0;
1370 case SBM_SETRANGEREDRAW:
1371 case SBM_SETRANGE:
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;
1378 return 0;
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 );
1389 case WM_SETCURSOR:
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 );
1399 case SBM_SETPOS:
1401 SCROLLINFO info;
1402 info.cbSize = sizeof(info);
1403 info.nPos = wparam;
1404 info.fMask = SIF_POS | SIF_RETURNPREV;
1405 return NtUserSetScrollInfo( hwnd, SB_CTL, &info, lparam );
1408 case SBM_GETPOS:
1409 return get_scroll_pos( hwnd, SB_CTL );
1411 case SBM_GETRANGE:
1412 return get_scroll_range( hwnd, SB_CTL, (int *)wparam, (int *)lparam );
1414 case SBM_ENABLE_ARROWS:
1415 return NtUserEnableScrollBar( hwnd, SB_CTL, wparam );
1417 case 0x00e5:
1418 case 0x00e7:
1419 case 0x00e8:
1420 case 0x00ec:
1421 case 0x00ed:
1422 case 0x00ee:
1423 case 0x00ef:
1424 ERR( "unknown Win32 msg %04x wp=%08lx lp=%08lx\n", msg, (long)wparam, lparam );
1425 return 0;
1427 default:
1428 if (msg >= WM_USER)
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)
1439 return;
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 );
1456 return TRUE;
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;
1478 BOOL check_flags;
1480 flags &= ESB_DISABLE_BOTH;
1482 if (bar == SB_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 );
1489 bar = SB_HORZ;
1491 else
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 );
1504 return 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 */
1516 if (bar == SB_CTL)
1517 return send_message( hwnd, SBM_SETSCROLLINFO, redraw, (LPARAM)info );
1519 return set_scroll_info( hwnd, bar, info, redraw );