scroll.c 65.2 KB
Newer Older
1
/*
Alexandre Julliard's avatar
Alexandre Julliard committed
2
 * Scrollbar control
Alexandre Julliard's avatar
Alexandre Julliard committed
3
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
4 5
 * Copyright 1993 Martin Ayotte
 * Copyright 1994, 1996 Alexandre Julliard
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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
20 21 22 23 24 25 26 27 28
 *
 * NOTES
 *
 * This code was audited for completeness against the documented features
 * of Comctl32.dll version 6.0 on Oct. 8, 2004, by Dimitrie O. Paun.
 * 
 * Unless otherwise noted, we believe this code to be complete, as per
 * the specification mentioned above.
 * If you discover missing features, or bugs, please note them below.
Alexandre Julliard's avatar
Alexandre Julliard committed
29 30
 */

31 32
#include <stdarg.h>

33
#include "windef.h"
34
#include "winbase.h"
35
#include "wingdi.h"
36
#include "controls.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
37
#include "win.h"
38
#include "wine/debug.h"
39
#include "user_private.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
40

41
WINE_DEFAULT_DEBUG_CHANNEL(scroll);
42

43
/* data for a single scroll bar */
44 45
typedef struct
{
46 47 48 49
    INT   curVal;   /* Current scroll-bar value */
    INT   minVal;   /* Minimum scroll-bar value */
    INT   maxVal;   /* Maximum scroll-bar value */
    INT   page;     /* Page size of scroll bar (Win32) */
50
    UINT  flags;    /* EnableScrollBar flags */
51
} SCROLLBAR_INFO, *LPSCROLLBAR_INFO;
52

53 54 55 56 57 58
/* data for window that has (one or two) scroll bars */
typedef struct
{
    SCROLLBAR_INFO horz;
    SCROLLBAR_INFO vert;
} WINSCROLLBAR_INFO, *LPWINSCROLLBAR_INFO;
59

Alexandre Julliard's avatar
Alexandre Julliard committed
60
  /* Minimum size of the rectangle between the arrows */
61
#define SCROLL_MIN_RECT  4
Alexandre Julliard's avatar
Alexandre Julliard committed
62

Alexandre Julliard's avatar
Alexandre Julliard committed
63
  /* Minimum size of the thumb in pixels */
Alexandre Julliard's avatar
Alexandre Julliard committed
64
#define SCROLL_MIN_THUMB 6
Alexandre Julliard's avatar
Alexandre Julliard committed
65

Alexandre Julliard's avatar
Alexandre Julliard committed
66
  /* Overlap between arrows and thumb */
67
#define SCROLL_ARROW_THUMB_OVERLAP 0
Alexandre Julliard's avatar
Alexandre Julliard committed
68

Alexandre Julliard's avatar
Alexandre Julliard committed
69 70 71 72
  /* Delay (in ms) before first repetition when holding the button down */
#define SCROLL_FIRST_DELAY   200

  /* Delay (in ms) between scroll repetitions */
Alexandre Julliard's avatar
Alexandre Julliard committed
73
#define SCROLL_REPEAT_DELAY  50
Alexandre Julliard's avatar
Alexandre Julliard committed
74 75 76 77 78 79 80 81 82 83 84 85 86 87

  /* Scroll timer id */
#define SCROLL_TIMER   0

  /* Scroll-bar hit testing */
enum SCROLL_HITTEST
{
    SCROLL_NOWHERE,      /* Outside the scroll bar */
    SCROLL_TOP_ARROW,    /* Top or left arrow */
    SCROLL_TOP_RECT,     /* Rectangle between the top arrow and the thumb */
    SCROLL_THUMB,        /* Thumb rectangle */
    SCROLL_BOTTOM_RECT,  /* Rectangle between the thumb and the bottom arrow */
    SCROLL_BOTTOM_ARROW  /* Bottom or right arrow */
};
Alexandre Julliard's avatar
Alexandre Julliard committed
88

89 90 91 92 93 94
 /* What to do after SCROLL_SetScrollInfo() */
#define SA_SSI_HIDE		0x0001
#define SA_SSI_SHOW		0x0002
#define SA_SSI_REFRESH		0x0004
#define SA_SSI_REPAINT_ARROWS	0x0008

Alexandre Julliard's avatar
Alexandre Julliard committed
95
 /* Thumb-tracking info */
96 97 98 99
static HWND SCROLL_TrackingWin = 0;
static INT  SCROLL_TrackingBar = 0;
static INT  SCROLL_TrackingPos = 0;
static INT  SCROLL_TrackingVal = 0;
100 101
 /* Hit test code of the last button-down event */
static enum SCROLL_HITTEST SCROLL_trackHitTest;
102
static BOOL SCROLL_trackVertical;
Alexandre Julliard's avatar
Alexandre Julliard committed
103

104
 /* Is the moving thumb being displayed? */
105
static BOOL SCROLL_MovingThumb = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
106

107
 /* Local functions */
108
static BOOL SCROLL_ShowScrollBar( HWND hwnd, INT nBar,
109
				    BOOL fShowH, BOOL fShowV );
110
static INT SCROLL_SetScrollInfo( HWND hwnd, INT nBar,
111
                                 const SCROLLINFO *info, BOOL bRedraw );
112
static void SCROLL_DrawInterior_9x( HWND hwnd, HDC hdc, INT nBar,
113 114 115 116
				    RECT *rect, INT arrowSize,
				    INT thumbSize, INT thumbPos,
				    UINT flags, BOOL vertical,
				    BOOL top_selected, BOOL bottom_selected );
117 118 119 120 121


/*********************************************************************
 * scrollbar class descriptor
 */
122
static const WCHAR scrollbarW[] = {'S','c','r','o','l','l','B','a','r',0};
123 124
const struct builtin_class_descr SCROLL_builtin_class =
{
125
    scrollbarW,             /* name */
126
    CS_DBLCLKS | CS_VREDRAW | CS_HREDRAW | CS_PARENTDC, /* style  */
127
    WINPROC_SCROLLBAR,      /* proc */
128
    sizeof(SCROLLBAR_INFO), /* extra */
129
    IDC_ARROW,              /* cursor */
130 131
    0                       /* brush */
};
132

133 134 135 136
/***********************************************************************
 *           SCROLL_ScrollInfoValid
 *
 *  Determine if the supplied SCROLLINFO struct is valid.
137
 *  info     [in] The SCROLLINFO struct to be tested
138
 */
139
static inline BOOL SCROLL_ScrollInfoValid( LPCSCROLLINFO info )
140 141 142 143 144
{
    return !(info->fMask & ~(SIF_ALL | SIF_DISABLENOSCROLL)
        || (info->cbSize != sizeof(*info)
            && info->cbSize != sizeof(*info) - sizeof(info->nTrackPos)));
}
145

146

Alexandre Julliard's avatar
Alexandre Julliard committed
147
/***********************************************************************
148 149 150 151 152
 *           SCROLL_GetInternalInfo

 * Returns pointer to internal SCROLLBAR_INFO structure for nBar
 * or NULL if failed (f.i. scroll bar does not exist yet)
 * If alloc is TRUE and the struct does not exist yet, create it.
Alexandre Julliard's avatar
Alexandre Julliard committed
153
 */
154
static SCROLLBAR_INFO *SCROLL_GetInternalInfo( HWND hwnd, INT nBar, BOOL alloc )
Alexandre Julliard's avatar
Alexandre Julliard committed
155
{
156
    SCROLLBAR_INFO *infoPtr = NULL;
157
    WND *wndPtr = WIN_GetPtr( hwnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
158

159
    if (!wndPtr || wndPtr == WND_OTHER_PROCESS || wndPtr == WND_DESKTOP) return NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
160 161
    switch(nBar)
    {
162 163 164 165 166 167 168 169 170 171 172 173
        case SB_HORZ:
            if (wndPtr->pScroll) infoPtr = &((LPWINSCROLLBAR_INFO)wndPtr->pScroll)->horz;
            break;
        case SB_VERT:
            if (wndPtr->pScroll) infoPtr = &((LPWINSCROLLBAR_INFO)wndPtr->pScroll)->vert;
            break;
        case SB_CTL:
            infoPtr = (SCROLLBAR_INFO *)wndPtr->wExtra;
            break;
        case SB_BOTH:
            WARN("with SB_BOTH\n");
            break;
Alexandre Julliard's avatar
Alexandre Julliard committed
174 175
    }

176
    if (!infoPtr && alloc)
Alexandre Julliard's avatar
Alexandre Julliard committed
177
    {
178 179
        WINSCROLLBAR_INFO *winInfoPtr;

180 181
        if (nBar != SB_HORZ && nBar != SB_VERT)
            WARN("Cannot initialize nBar=%d\n",nBar);
182
        else if ((winInfoPtr = HeapAlloc( GetProcessHeap(), 0, sizeof(WINSCROLLBAR_INFO) )))
Alexandre Julliard's avatar
Alexandre Julliard committed
183
        {
184
            /* Set default values */
185 186 187 188 189 190 191 192 193 194
            winInfoPtr->horz.minVal = 0;
            winInfoPtr->horz.curVal = 0;
            winInfoPtr->horz.page = 0;
            /* From MSDN and our own tests:
             * max for a standard scroll bar is 100 by default. */
            winInfoPtr->horz.maxVal = 100;
            winInfoPtr->horz.flags  = ESB_ENABLE_BOTH;
            winInfoPtr->vert = winInfoPtr->horz;
            wndPtr->pScroll = winInfoPtr;
            infoPtr = nBar == SB_HORZ ? &winInfoPtr->horz : &winInfoPtr->vert;
Alexandre Julliard's avatar
Alexandre Julliard committed
195 196
        }
    }
197
    WIN_ReleasePtr( wndPtr );
Alexandre Julliard's avatar
Alexandre Julliard committed
198
    return infoPtr;
Alexandre Julliard's avatar
Alexandre Julliard committed
199 200
}

Alexandre Julliard's avatar
Alexandre Julliard committed
201

Alexandre Julliard's avatar
Alexandre Julliard committed
202 203 204 205 206
/***********************************************************************
 *           SCROLL_GetScrollBarRect
 *
 * Compute the scroll bar rectangle, in drawing coordinates (i.e. client
 * coords for SB_CTL, window coords for SB_VERT and SB_HORZ).
Alexandre Julliard's avatar
Alexandre Julliard committed
207 208 209 210
 * 'arrowSize' returns the width or height of an arrow (depending on
 * the orientation of the scrollbar), 'thumbSize' returns the size of
 * the thumb, and 'thumbPos' returns the position of the thumb
 * relative to the left or to the top.
Alexandre Julliard's avatar
Alexandre Julliard committed
211 212
 * Return TRUE if the scrollbar is vertical, FALSE if horizontal.
 */
213
static BOOL SCROLL_GetScrollBarRect( HWND hwnd, INT nBar, RECT *lprect,
214 215
                                     INT *arrowSize, INT *thumbSize,
                                     INT *thumbPos )
Alexandre Julliard's avatar
Alexandre Julliard committed
216
{
217 218
    INT pixels;
    BOOL vertical;
219 220
    WND *wndPtr = WIN_GetPtr( hwnd );

221
    if (!wndPtr || wndPtr == WND_OTHER_PROCESS || wndPtr == WND_DESKTOP) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
222 223 224 225

    switch(nBar)
    {
      case SB_HORZ:
Alexandre Julliard's avatar
Alexandre Julliard committed
226
        lprect->left   = wndPtr->rectClient.left - wndPtr->rectWindow.left;
Alexandre Julliard's avatar
Alexandre Julliard committed
227
        lprect->top    = wndPtr->rectClient.bottom - wndPtr->rectWindow.top;
Alexandre Julliard's avatar
Alexandre Julliard committed
228
        lprect->right  = wndPtr->rectClient.right - wndPtr->rectWindow.left;
229
        lprect->bottom = lprect->top + GetSystemMetrics(SM_CYHSCROLL);
230
	if(wndPtr->dwStyle & WS_VSCROLL)
Alexandre Julliard's avatar
Alexandre Julliard committed
231
	  lprect->right++;
Alexandre Julliard's avatar
Alexandre Julliard committed
232 233 234 235
        vertical = FALSE;
	break;

      case SB_VERT:
236 237 238 239
        if((wndPtr->dwExStyle & WS_EX_LEFTSCROLLBAR) != 0)
            lprect->left   = wndPtr->rectClient.left - wndPtr->rectWindow.left - GetSystemMetrics(SM_CXVSCROLL);
        else
            lprect->left   = wndPtr->rectClient.right - wndPtr->rectWindow.left;
Alexandre Julliard's avatar
Alexandre Julliard committed
240
        lprect->top    = wndPtr->rectClient.top - wndPtr->rectWindow.top;
241
        lprect->right  = lprect->left + GetSystemMetrics(SM_CXVSCROLL);
Alexandre Julliard's avatar
Alexandre Julliard committed
242
        lprect->bottom = wndPtr->rectClient.bottom - wndPtr->rectWindow.top;
243
	if(wndPtr->dwStyle & WS_HSCROLL)
Alexandre Julliard's avatar
Alexandre Julliard committed
244
	  lprect->bottom++;
Alexandre Julliard's avatar
Alexandre Julliard committed
245 246 247 248
        vertical = TRUE;
	break;

      case SB_CTL:
249
	GetClientRect( hwnd, lprect );
Alexandre Julliard's avatar
Alexandre Julliard committed
250 251
        vertical = ((wndPtr->dwStyle & SBS_VERT) != 0);
	break;
Alexandre Julliard's avatar
Alexandre Julliard committed
252 253

    default:
254
        WIN_ReleasePtr( wndPtr );
Alexandre Julliard's avatar
Alexandre Julliard committed
255
        return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
256 257 258 259 260
    }

    if (vertical) pixels = lprect->bottom - lprect->top;
    else pixels = lprect->right - lprect->left;

261
    if (pixels <= 2*GetSystemMetrics(SM_CXVSCROLL) + SCROLL_MIN_RECT)
Alexandre Julliard's avatar
Alexandre Julliard committed
262
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
263 264 265 266 267
        if (pixels > SCROLL_MIN_RECT)
            *arrowSize = (pixels - SCROLL_MIN_RECT) / 2;
        else
            *arrowSize = 0;
        *thumbPos = *thumbSize = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
268
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
269
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
270
    {
271 272 273
        SCROLLBAR_INFO *info = SCROLL_GetInternalInfo( hwnd, nBar, FALSE );
        if (!info)
        {
274
            WARN("called for missing scroll bar\n");
275
            WIN_ReleasePtr( wndPtr );
276 277
            return FALSE;
        }
278 279
        *arrowSize = GetSystemMetrics(SM_CXVSCROLL);
        pixels -= (2 * (GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP));
Alexandre Julliard's avatar
Alexandre Julliard committed
280

281
        if (info->page)
Alexandre Julliard's avatar
Alexandre Julliard committed
282
        {
283
	    *thumbSize = MulDiv(pixels,info->page,(info->maxVal-info->minVal+1));
Alexandre Julliard's avatar
Alexandre Julliard committed
284 285
            if (*thumbSize < SCROLL_MIN_THUMB) *thumbSize = SCROLL_MIN_THUMB;
        }
286
        else *thumbSize = GetSystemMetrics(SM_CXVSCROLL);
Alexandre Julliard's avatar
Alexandre Julliard committed
287

Alexandre Julliard's avatar
Alexandre Julliard committed
288
        if (((pixels -= *thumbSize ) < 0) ||
Alexandre Julliard's avatar
Alexandre Julliard committed
289 290 291 292 293
            ((info->flags & ESB_DISABLE_BOTH) == ESB_DISABLE_BOTH))
        {
            /* Rectangle too small or scrollbar disabled -> no thumb */
            *thumbPos = *thumbSize = 0;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
294
        else
Alexandre Julliard's avatar
Alexandre Julliard committed
295
        {
296 297
            INT max = info->maxVal - max( info->page-1, 0 );
            if (info->minVal >= max)
Alexandre Julliard's avatar
Alexandre Julliard committed
298
                *thumbPos = *arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
Alexandre Julliard's avatar
Alexandre Julliard committed
299
            else
Alexandre Julliard's avatar
Alexandre Julliard committed
300
                *thumbPos = *arrowSize - SCROLL_ARROW_THUMB_OVERLAP
301
		  + MulDiv(pixels, (info->curVal-info->minVal),(max - info->minVal));
Alexandre Julliard's avatar
Alexandre Julliard committed
302
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
303
    }
304
    WIN_ReleasePtr( wndPtr );
Alexandre Julliard's avatar
Alexandre Julliard committed
305
    return vertical;
Alexandre Julliard's avatar
Alexandre Julliard committed
306 307 308
}


Alexandre Julliard's avatar
Alexandre Julliard committed
309 310 311 312 313 314
/***********************************************************************
 *           SCROLL_GetThumbVal
 *
 * Compute the current scroll position based on the thumb position in pixels
 * from the top of the scroll-bar.
 */
315 316
static UINT SCROLL_GetThumbVal( SCROLLBAR_INFO *infoPtr, RECT *rect,
                                  BOOL vertical, INT pos )
Alexandre Julliard's avatar
Alexandre Julliard committed
317
{
318 319
    INT thumbSize;
    INT pixels = vertical ? rect->bottom-rect->top : rect->right-rect->left;
Alexandre Julliard's avatar
Alexandre Julliard committed
320

321
    if ((pixels -= 2*(GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP)) <= 0)
322
        return infoPtr->minVal;
Alexandre Julliard's avatar
Alexandre Julliard committed
323

324
    if (infoPtr->page)
Alexandre Julliard's avatar
Alexandre Julliard committed
325
    {
326
        thumbSize = MulDiv(pixels,infoPtr->page,(infoPtr->maxVal-infoPtr->minVal+1));
Alexandre Julliard's avatar
Alexandre Julliard committed
327 328
        if (thumbSize < SCROLL_MIN_THUMB) thumbSize = SCROLL_MIN_THUMB;
    }
329
    else thumbSize = GetSystemMetrics(SM_CXVSCROLL);
Alexandre Julliard's avatar
Alexandre Julliard committed
330

331
    if ((pixels -= thumbSize) <= 0) return infoPtr->minVal;
Alexandre Julliard's avatar
Alexandre Julliard committed
332

333
    pos = max( 0, pos - (GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP) );
Alexandre Julliard's avatar
Alexandre Julliard committed
334
    if (pos > pixels) pos = pixels;
Alexandre Julliard's avatar
Alexandre Julliard committed
335

336 337 338
    if (!infoPtr->page) pos *= infoPtr->maxVal - infoPtr->minVal;
    else pos *= infoPtr->maxVal - infoPtr->minVal - infoPtr->page + 1;
    return infoPtr->minVal + ((pos + pixels / 2) / pixels);
Alexandre Julliard's avatar
Alexandre Julliard committed
339 340
}

341 342 343
/***********************************************************************
 *           SCROLL_PtInRectEx
 */
344
static BOOL SCROLL_PtInRectEx( LPRECT lpRect, POINT pt, BOOL vertical )
345
{
346
    RECT rect = *lpRect;
347
    int scrollbarWidth;
348

349 350
    /* Pad hit rect to allow mouse to be dragged outside of scrollbar and
     * still be considered in the scrollbar. */
351 352
    if (vertical)
    {
353 354 355 356 357
        scrollbarWidth = lpRect->right - lpRect->left;
        rect.left -= scrollbarWidth*8;
        rect.right += scrollbarWidth*8;
        rect.top -= scrollbarWidth*2;
        rect.bottom += scrollbarWidth*2;
358 359 360
    }
    else
    {
361 362 363 364 365
        scrollbarWidth = lpRect->bottom - lpRect->top;
        rect.left -= scrollbarWidth*2;
        rect.right += scrollbarWidth*2;
        rect.top -= scrollbarWidth*8;
        rect.bottom += scrollbarWidth*8;
366
    }
367
    return PtInRect( &rect, pt );
368 369 370 371 372
}

/***********************************************************************
 *           SCROLL_ClipPos
 */
373
static POINT SCROLL_ClipPos( LPRECT lpRect, POINT pt )
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
{
    if( pt.x < lpRect->left )
	pt.x = lpRect->left;
    else
    if( pt.x > lpRect->right )
	pt.x = lpRect->right;

    if( pt.y < lpRect->top )
	pt.y = lpRect->top;
    else
    if( pt.y > lpRect->bottom )
	pt.y = lpRect->bottom;

    return pt;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
390

Alexandre Julliard's avatar
Alexandre Julliard committed
391 392 393 394 395
/***********************************************************************
 *           SCROLL_HitTest
 *
 * Scroll-bar hit testing (don't confuse this with WM_NCHITTEST!).
 */
396 397
static enum SCROLL_HITTEST SCROLL_HitTest( HWND hwnd, INT nBar,
                                           POINT pt, BOOL bDragging )
Alexandre Julliard's avatar
Alexandre Julliard committed
398
{
399 400
    INT arrowSize, thumbSize, thumbPos;
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
401

402
    BOOL vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
Alexandre Julliard's avatar
Alexandre Julliard committed
403
                                           &arrowSize, &thumbSize, &thumbPos );
404 405

    if ( (bDragging && !SCROLL_PtInRectEx( &rect, pt, vertical )) ||
406
	 (!PtInRect( &rect, pt )) ) return SCROLL_NOWHERE;
Alexandre Julliard's avatar
Alexandre Julliard committed
407 408 409

    if (vertical)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
410
        if (pt.y < rect.top + arrowSize) return SCROLL_TOP_ARROW;
Alexandre Julliard's avatar
Alexandre Julliard committed
411 412 413
        if (pt.y >= rect.bottom - arrowSize) return SCROLL_BOTTOM_ARROW;
        if (!thumbPos) return SCROLL_TOP_RECT;
        pt.y -= rect.top;
Alexandre Julliard's avatar
Alexandre Julliard committed
414
        if (pt.y < thumbPos) return SCROLL_TOP_RECT;
Alexandre Julliard's avatar
Alexandre Julliard committed
415
        if (pt.y >= thumbPos + thumbSize) return SCROLL_BOTTOM_RECT;
Alexandre Julliard's avatar
Alexandre Julliard committed
416 417 418
    }
    else  /* horizontal */
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
419
        if (pt.x < rect.left + arrowSize) return SCROLL_TOP_ARROW;
Alexandre Julliard's avatar
Alexandre Julliard committed
420 421 422
        if (pt.x >= rect.right - arrowSize) return SCROLL_BOTTOM_ARROW;
        if (!thumbPos) return SCROLL_TOP_RECT;
        pt.x -= rect.left;
Alexandre Julliard's avatar
Alexandre Julliard committed
423
        if (pt.x < thumbPos) return SCROLL_TOP_RECT;
Alexandre Julliard's avatar
Alexandre Julliard committed
424
        if (pt.x >= thumbPos + thumbSize) return SCROLL_BOTTOM_RECT;
Alexandre Julliard's avatar
Alexandre Julliard committed
425
    }
426
    return SCROLL_THUMB;
Alexandre Julliard's avatar
Alexandre Julliard committed
427 428 429
}


Alexandre Julliard's avatar
Alexandre Julliard committed
430 431 432 433 434
/***********************************************************************
 *           SCROLL_DrawArrows
 *
 * Draw the scroll bar arrows.
 */
435 436 437
static void SCROLL_DrawArrows( HDC hdc, SCROLLBAR_INFO *infoPtr,
                               RECT *rect, INT arrowSize, BOOL vertical,
                               BOOL top_pressed, BOOL bottom_pressed )
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
{
  RECT r;

  r = *rect;
  if( vertical )
    r.bottom = r.top + arrowSize;
  else
    r.right = r.left + arrowSize;

  DrawFrameControl( hdc, &r, DFC_SCROLL,
		    (vertical ? DFCS_SCROLLUP : DFCS_SCROLLLEFT)
		    | (top_pressed ? (DFCS_PUSHED | DFCS_FLAT) : 0 )
		    | (infoPtr->flags&ESB_DISABLE_LTUP ? DFCS_INACTIVE : 0 ) );

  r = *rect;
  if( vertical )
    r.top = r.bottom-arrowSize;
  else
    r.left = r.right-arrowSize;

  DrawFrameControl( hdc, &r, DFC_SCROLL,
		    (vertical ? DFCS_SCROLLDOWN : DFCS_SCROLLRIGHT)
		    | (bottom_pressed ? (DFCS_PUSHED | DFCS_FLAT) : 0 )
		    | (infoPtr->flags&ESB_DISABLE_RTDN ? DFCS_INACTIVE : 0) );
}

464 465
static void SCROLL_DrawMovingThumb( HDC hdc, RECT *rect, BOOL vertical,
				    INT arrowSize, INT thumbSize )
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
{
  INT pos = SCROLL_TrackingPos;
  INT max_size;

  if( vertical )
    max_size = rect->bottom - rect->top;
  else
    max_size = rect->right - rect->left;

  max_size -= (arrowSize-SCROLL_ARROW_THUMB_OVERLAP) + thumbSize;

  if( pos < (arrowSize-SCROLL_ARROW_THUMB_OVERLAP) )
    pos = (arrowSize-SCROLL_ARROW_THUMB_OVERLAP);
  else if( pos > max_size )
    pos = max_size;

  SCROLL_DrawInterior_9x( SCROLL_TrackingWin, hdc, SCROLL_TrackingBar,
			  rect, arrowSize, thumbSize, pos,
			  0, vertical, FALSE, FALSE );
485

486 487 488
  SCROLL_MovingThumb = !SCROLL_MovingThumb;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
489 490 491 492 493
/***********************************************************************
 *           SCROLL_DrawInterior
 *
 * Draw the scroll bar interior (everything except the arrows).
 */
494
static void SCROLL_DrawInterior_9x( HWND hwnd, HDC hdc, INT nBar,
495 496 497 498 499 500 501 502 503 504 505 506 507 508
				    RECT *rect, INT arrowSize,
				    INT thumbSize, INT thumbPos,
				    UINT flags, BOOL vertical,
				    BOOL top_selected, BOOL bottom_selected )
{
    RECT r;
    HPEN hSavePen;
    HBRUSH hSaveBrush,hBrush;

    /* Only scrollbar controls send WM_CTLCOLORSCROLLBAR.
     * The window-owned scrollbars need to call DEFWND_ControlColor
     * to correctly setup default scrollbar colors
     */
    if (nBar == SB_CTL)
509
    {
510
      hBrush = (HBRUSH)SendMessageW( GetParent(hwnd), WM_CTLCOLORSCROLLBAR,
511 512 513 514 515 516 517
				     (WPARAM)hdc,(LPARAM)hwnd);
    }
    else
    {
      hBrush = DEFWND_ControlColor( hdc, CTLCOLOR_SCROLLBAR );
    }

518
    hSavePen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
    hSaveBrush = SelectObject( hdc, hBrush );

    /* Calculate the scroll rectangle */
    r = *rect;
    if (vertical)
    {
        r.top    += arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
        r.bottom -= (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
    }
    else
    {
        r.left  += arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
        r.right -= (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
    }

    /* Draw the scroll rectangles and thumb */
    if (!thumbPos)  /* No thumb to draw */
    {
        PatBlt( hdc, r.left, r.top,
                     r.right - r.left, r.bottom - r.top,
                     PATCOPY );

        /* cleanup and return */
        SelectObject( hdc, hSavePen );
        SelectObject( hdc, hSaveBrush );
        return;
    }

    if (vertical)
    {
        PatBlt( hdc, r.left, r.top,
                  r.right - r.left,
                  thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP),
                  top_selected ? 0x0f0000 : PATCOPY );
        r.top += thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
        PatBlt( hdc, r.left, r.top + thumbSize,
                  r.right - r.left,
                  r.bottom - r.top - thumbSize,
                  bottom_selected ? 0x0f0000 : PATCOPY );
        r.bottom = r.top + thumbSize;
    }
    else  /* horizontal */
    {
        PatBlt( hdc, r.left, r.top,
                  thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP),
                  r.bottom - r.top,
                  top_selected ? 0x0f0000 : PATCOPY );
        r.left += thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
        PatBlt( hdc, r.left + thumbSize, r.top,
                  r.right - r.left - thumbSize,
                  r.bottom - r.top,
                  bottom_selected ? 0x0f0000 : PATCOPY );
        r.right = r.left + thumbSize;
    }

    /* Draw the thumb */
    DrawEdge( hdc, &r, EDGE_RAISED, BF_RECT | BF_MIDDLE  );

    /* cleanup */
    SelectObject( hdc, hSavePen );
    SelectObject( hdc, hSaveBrush );
}


583
static void SCROLL_DrawInterior( HWND hwnd, HDC hdc, INT nBar,
584 585 586 587
                                 RECT *rect, INT arrowSize,
                                 INT thumbSize, INT thumbPos,
                                 UINT flags, BOOL vertical,
                                 BOOL top_selected, BOOL bottom_selected )
Alexandre Julliard's avatar
Alexandre Julliard committed
588
{
589
    RECT r;
590 591
    HPEN hSavePen;
    HBRUSH hSaveBrush,hBrush;
592 593 594 595 596 597
    BOOL Save_SCROLL_MovingThumb = SCROLL_MovingThumb;

    if (Save_SCROLL_MovingThumb &&
        (SCROLL_TrackingWin == hwnd) &&
        (SCROLL_TrackingBar == nBar))
        SCROLL_DrawMovingThumb( hdc, rect, vertical, arrowSize, thumbSize );
Alexandre Julliard's avatar
Alexandre Julliard committed
598 599 600

      /* Select the correct brush and pen */

601 602 603 604 605
    /* Only scrollbar controls send WM_CTLCOLORSCROLLBAR.
     * The window-owned scrollbars need to call DEFWND_ControlColor
     * to correctly setup default scrollbar colors
     */
    if (nBar == SB_CTL) {
606
        hBrush = (HBRUSH)SendMessageW( GetParent(hwnd), WM_CTLCOLORSCROLLBAR,
607 608 609
                                       (WPARAM)hdc,(LPARAM)hwnd);
    } else {
        hBrush = DEFWND_ControlColor( hdc, CTLCOLOR_SCROLLBAR );
Alexandre Julliard's avatar
Alexandre Julliard committed
610
    }
611
    hSavePen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
612
    hSaveBrush = SelectObject( hdc, hBrush );
Alexandre Julliard's avatar
Alexandre Julliard committed
613

Alexandre Julliard's avatar
Alexandre Julliard committed
614 615 616 617 618
      /* Calculate the scroll rectangle */

    r = *rect;
    if (vertical)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
619 620
        r.top    += arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
        r.bottom -= (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
Alexandre Julliard's avatar
Alexandre Julliard committed
621 622 623
    }
    else
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
624 625
        r.left  += arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
        r.right -= (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
Alexandre Julliard's avatar
Alexandre Julliard committed
626 627 628 629 630 631 632 633
    }

      /* Draw the scroll bar frame */

      /* Draw the scroll rectangles and thumb */

    if (!thumbPos)  /* No thumb to draw */
    {
634
        PatBlt( hdc, r.left, r.top, r.right - r.left, r.bottom - r.top, PATCOPY );
635 636 637 638

        /* cleanup and return */
        SelectObject( hdc, hSavePen );
        SelectObject( hdc, hSaveBrush );
Alexandre Julliard's avatar
Alexandre Julliard committed
639 640 641 642 643
        return;
    }

    if (vertical)
    {
644 645 646
        PatBlt( hdc, r.left, r.top, r.right - r.left,
                thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP),
                top_selected ? 0x0f0000 : PATCOPY );
Alexandre Julliard's avatar
Alexandre Julliard committed
647
        r.top += thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
648 649 650
        PatBlt( hdc, r.left, r.top + thumbSize, r.right - r.left,
                r.bottom - r.top - thumbSize,
                bottom_selected ? 0x0f0000 : PATCOPY );
Alexandre Julliard's avatar
Alexandre Julliard committed
651
        r.bottom = r.top + thumbSize;
Alexandre Julliard's avatar
Alexandre Julliard committed
652 653 654
    }
    else  /* horizontal */
    {
655 656 657
        PatBlt( hdc, r.left, r.top,
                thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP),
                r.bottom - r.top, top_selected ? 0x0f0000 : PATCOPY );
Alexandre Julliard's avatar
Alexandre Julliard committed
658
        r.left += thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
659 660
        PatBlt( hdc, r.left + thumbSize, r.top, r.right - r.left - thumbSize,
                r.bottom - r.top, bottom_selected ? 0x0f0000 : PATCOPY );
Alexandre Julliard's avatar
Alexandre Julliard committed
661
        r.right = r.left + thumbSize;
Alexandre Julliard's avatar
Alexandre Julliard committed
662 663 664 665
    }

      /* Draw the thumb */

666
    SelectObject( hdc, GetSysColorBrush(COLOR_BTNFACE) );
667
    Rectangle( hdc, r.left+1, r.top+1, r.right-1, r.bottom-1 );
668
    DrawEdge( hdc, &r, EDGE_RAISED, BF_RECT );
669 670

    if (Save_SCROLL_MovingThumb &&
Alexandre Julliard's avatar
Alexandre Julliard committed
671 672 673
        (SCROLL_TrackingWin == hwnd) &&
        (SCROLL_TrackingBar == nBar))
        SCROLL_DrawMovingThumb( hdc, rect, vertical, arrowSize, thumbSize );
674 675 676 677

    /* cleanup */
    SelectObject( hdc, hSavePen );
    SelectObject( hdc, hSaveBrush );
Alexandre Julliard's avatar
Alexandre Julliard committed
678
}
Alexandre Julliard's avatar
Alexandre Julliard committed
679

Alexandre Julliard's avatar
Alexandre Julliard committed
680

Alexandre Julliard's avatar
Alexandre Julliard committed
681 682 683 684 685
/***********************************************************************
 *           SCROLL_DrawScrollBar
 *
 * Redraw the whole scrollbar.
 */
686
void SCROLL_DrawScrollBar( HWND hwnd, HDC hdc, INT nBar,
687
			   BOOL arrows, BOOL interior )
Alexandre Julliard's avatar
Alexandre Julliard committed
688
{
689 690 691
    INT arrowSize, thumbSize, thumbPos;
    RECT rect;
    BOOL vertical;
692
    SCROLLBAR_INFO *infoPtr = SCROLL_GetInternalInfo( hwnd, nBar, TRUE );
693
    BOOL Save_SCROLL_MovingThumb = SCROLL_MovingThumb;
694 695 696
    DWORD style = GetWindowLongW( hwnd, GWL_STYLE );

    if (!(hwnd = WIN_GetFullHandle( hwnd ))) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
697

698 699 700 701
    if (!infoPtr ||
        ((nBar == SB_VERT) && !(style & WS_VSCROLL)) ||
        ((nBar == SB_HORZ) && !(style & WS_HSCROLL))) return;
    if (!WIN_IsWindowDrawable( hwnd, FALSE )) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
702

Alexandre Julliard's avatar
Alexandre Julliard committed
703
    vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
Alexandre Julliard's avatar
Alexandre Julliard committed
704
                                        &arrowSize, &thumbSize, &thumbPos );
Alexandre Julliard's avatar
Alexandre Julliard committed
705

706
    /* do not draw if the scrollbar rectangle is empty */
707
    if(IsRectEmpty(&rect)) return;
708

709 710 711 712 713
    if (Save_SCROLL_MovingThumb &&
        (SCROLL_TrackingWin == hwnd) &&
        (SCROLL_TrackingBar == nBar))
        SCROLL_DrawMovingThumb( hdc, &rect, vertical, arrowSize, thumbSize );

Alexandre Julliard's avatar
Alexandre Julliard committed
714 715
      /* Draw the arrows */

716 717
    if (arrows && arrowSize)
    {
718
	if( vertical == SCROLL_trackVertical && GetCapture() == hwnd )
719 720 721 722
	    SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
			       (SCROLL_trackHitTest == SCROLL_TOP_ARROW),
			       (SCROLL_trackHitTest == SCROLL_BOTTOM_ARROW) );
	else
723
	    SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
724 725 726 727
							       FALSE, FALSE );
    }
    if( interior )
	SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbSize,
Alexandre Julliard's avatar
Alexandre Julliard committed
728
                         thumbPos, infoPtr->flags, vertical, FALSE, FALSE );
729 730 731 732 733 734

    if (Save_SCROLL_MovingThumb &&
        (SCROLL_TrackingWin == hwnd) &&
        (SCROLL_TrackingBar == nBar))
        SCROLL_DrawMovingThumb( hdc, &rect, vertical, arrowSize, thumbSize );

Andreas Mohr's avatar
Andreas Mohr committed
735
    /* if scroll bar has focus, reposition the caret */
736
    if(hwnd==GetFocus() && (nBar==SB_CTL))
737 738 739 740 741 742 743 744 745 746
    {
        if (!vertical)
        {
            SetCaretPos(thumbPos+1, rect.top+1);
        }
        else
        {
            SetCaretPos(rect.top+1, thumbPos+1);
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
747 748
}

749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
/***********************************************************************
 *           SCROLL_DrawSizeGrip
 *
 *  Draw the size grip.
 */
static void SCROLL_DrawSizeGrip( HWND hwnd,  HDC hdc)
{
    RECT rc;

    GetClientRect( hwnd, &rc );
    FillRect( hdc, &rc, GetSysColorBrush(COLOR_SCROLLBAR) );
    rc.left = max( rc.left, rc.right - GetSystemMetrics(SM_CXVSCROLL) - 1 );
    rc.top  = max( rc.top, rc.bottom - GetSystemMetrics(SM_CYHSCROLL) - 1 );
    DrawFrameControl( hdc, &rc, DFC_SCROLL, DFCS_SCROLLSIZEGRIP );
}

Alexandre Julliard's avatar
Alexandre Julliard committed
765

Alexandre Julliard's avatar
Alexandre Julliard committed
766 767 768 769 770 771
/***********************************************************************
 *           SCROLL_RefreshScrollBar
 *
 * Repaint the scroll bar interior after a SetScrollRange() or
 * SetScrollPos() call.
 */
772
static void SCROLL_RefreshScrollBar( HWND hwnd, INT nBar,
773
				     BOOL arrows, BOOL interior )
Alexandre Julliard's avatar
Alexandre Julliard committed
774
{
775
    HDC hdc = GetDCEx( hwnd, 0,
Alexandre Julliard's avatar
Alexandre Julliard committed
776
                           DCX_CACHE | ((nBar == SB_CTL) ? 0 : DCX_WINDOW) );
Alexandre Julliard's avatar
Alexandre Julliard committed
777
    if (!hdc) return;
778 779

    SCROLL_DrawScrollBar( hwnd, hdc, nBar, arrows, interior );
780
    ReleaseDC( hwnd, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
781 782 783
}


Alexandre Julliard's avatar
Alexandre Julliard committed
784 785 786
/***********************************************************************
 *           SCROLL_HandleKbdEvent
 *
787
 * Handle a keyboard event (only for SB_CTL scrollbars with focus).
788 789 790 791 792
 *
 * PARAMS
 *    hwnd   [I] Handle of window with scrollbar(s)
 *    wParam [I] Variable input including enable state
 *    lParam [I] Variable input including input point
Alexandre Julliard's avatar
Alexandre Julliard committed
793
 */
794
static void SCROLL_HandleKbdEvent(HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
795
{
796
    TRACE("hwnd=%p wParam=%ld lParam=%ld\n", hwnd, wParam, lParam);
797 798 799 800

    /* hide caret on first KEYDOWN to prevent flicker */
    if ((lParam & PFD_DOUBLEBUFFER_DONTCARE) == 0)
        HideCaret(hwnd);
801

Alexandre Julliard's avatar
Alexandre Julliard committed
802 803
    switch(wParam)
    {
804 805 806 807 808 809
    case VK_PRIOR: wParam = SB_PAGEUP; break;
    case VK_NEXT:  wParam = SB_PAGEDOWN; break;
    case VK_HOME:  wParam = SB_TOP; break;
    case VK_END:   wParam = SB_BOTTOM; break;
    case VK_UP:    wParam = SB_LINEUP; break;
    case VK_DOWN:  wParam = SB_LINEDOWN; break;
810 811
    case VK_LEFT:  wParam = SB_LINEUP; break;
    case VK_RIGHT: wParam = SB_LINEDOWN; break;
812
    default: return;
Alexandre Julliard's avatar
Alexandre Julliard committed
813
    }
814
    SendMessageW(GetParent(hwnd),
815
        ((GetWindowLongW( hwnd, GWL_STYLE ) & SBS_VERT) ?
816
            WM_VSCROLL : WM_HSCROLL), wParam, (LPARAM)hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
817 818 819
}


Alexandre Julliard's avatar
Alexandre Julliard committed
820 821 822 823 824 825
/***********************************************************************
 *           SCROLL_HandleScrollEvent
 *
 * Handle a mouse or timer event for the scrollbar.
 * 'pt' is the location of the mouse event in client (for SB_CTL) or
 * windows coordinates.
Alexandre Julliard's avatar
Alexandre Julliard committed
826
 */
827
static void SCROLL_HandleScrollEvent( HWND hwnd, INT nBar, UINT msg, POINT pt)
Alexandre Julliard's avatar
Alexandre Julliard committed
828
{
Alexandre Julliard's avatar
Alexandre Julliard committed
829
      /* Previous mouse position for timer events */
830
    static POINT prevPt;
Alexandre Julliard's avatar
Alexandre Julliard committed
831
      /* Thumb position when tracking started. */
832
    static UINT trackThumbPos;
Alexandre Julliard's avatar
Alexandre Julliard committed
833
      /* Position in the scroll-bar of the last button-down event. */
834
    static INT lastClickPos;
Alexandre Julliard's avatar
Alexandre Julliard committed
835
      /* Position in the scroll-bar of the last mouse event. */
836
    static INT lastMousePos;
Alexandre Julliard's avatar
Alexandre Julliard committed
837 838

    enum SCROLL_HITTEST hittest;
839 840 841 842 843
    HWND hwndOwner, hwndCtl;
    BOOL vertical;
    INT arrowSize, thumbSize, thumbPos;
    RECT rect;
    HDC hdc;
Alexandre Julliard's avatar
Alexandre Julliard committed
844

845
    SCROLLBAR_INFO *infoPtr = SCROLL_GetInternalInfo( hwnd, nBar, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
846
    if (!infoPtr) return;
847
    if ((SCROLL_trackHitTest == SCROLL_NOWHERE) && (msg != WM_LBUTTONDOWN))
848
		  return;
Alexandre Julliard's avatar
Alexandre Julliard committed
849

850
    if (nBar == SB_CTL && (GetWindowLongW( hwnd, GWL_STYLE ) & (SBS_SIZEGRIP | SBS_SIZEBOX)))
851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
    {
        switch(msg)
        {
            case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
                HideCaret(hwnd);  /* hide caret while holding down LBUTTON */
                SetCapture( hwnd );
                prevPt = pt;
                SCROLL_trackHitTest  = hittest = SCROLL_THUMB;
                break;
            case WM_MOUSEMOVE:
                GetClientRect(GetParent(GetParent(hwnd)),&rect);
                prevPt = pt;
                break;
            case WM_LBUTTONUP:
                ReleaseCapture();
                SCROLL_trackHitTest  = hittest = SCROLL_NOWHERE;
                if (hwnd==GetFocus()) ShowCaret(hwnd);
                break;
            case WM_SYSTIMER:
                pt = prevPt;
                break;
        }
        return;
    }

876
    hdc = GetDCEx( hwnd, 0, DCX_CACHE | ((nBar == SB_CTL) ? 0 : DCX_WINDOW));
Alexandre Julliard's avatar
Alexandre Julliard committed
877
    vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
Alexandre Julliard's avatar
Alexandre Julliard committed
878
                                        &arrowSize, &thumbSize, &thumbPos );
879
    hwndOwner = (nBar == SB_CTL) ? GetParent(hwnd) : hwnd;
Alexandre Julliard's avatar
Alexandre Julliard committed
880 881 882 883 884
    hwndCtl   = (nBar == SB_CTL) ? hwnd : 0;

    switch(msg)
    {
      case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
Andreas Mohr's avatar
Andreas Mohr committed
885
          HideCaret(hwnd);  /* hide caret while holding down LBUTTON */
886
          SCROLL_trackVertical = vertical;
887
          SCROLL_trackHitTest  = hittest = SCROLL_HitTest( hwnd, nBar, pt, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
888 889 890 891
          lastClickPos  = vertical ? (pt.y - rect.top) : (pt.x - rect.left);
          lastMousePos  = lastClickPos;
          trackThumbPos = thumbPos;
          prevPt = pt;
892
          if (nBar == SB_CTL && (GetWindowLongW(hwnd, GWL_STYLE) & WS_TABSTOP)) SetFocus( hwnd );
893
          SetCapture( hwnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
894 895 896
          break;

      case WM_MOUSEMOVE:
897
          hittest = SCROLL_HitTest( hwnd, nBar, pt, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
898 899 900 901 902 903
          prevPt = pt;
          break;

      case WM_LBUTTONUP:
          hittest = SCROLL_NOWHERE;
          ReleaseCapture();
904 905
          /* if scrollbar has focus, show back caret */
          if (hwnd==GetFocus()) ShowCaret(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
906 907 908 909
          break;

      case WM_SYSTIMER:
          pt = prevPt;
910
          hittest = SCROLL_HitTest( hwnd, nBar, pt, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
911 912 913 914 915 916
          break;

      default:
          return;  /* Should never happen */
    }

917
    TRACE("Event: hwnd=%p bar=%d msg=%s pt=%d,%d hit=%d\n",
918
          hwnd, nBar, SPY_GetMsgName(msg,hwnd), pt.x, pt.y, hittest );
Alexandre Julliard's avatar
Alexandre Julliard committed
919

920
    switch(SCROLL_trackHitTest)
Alexandre Julliard's avatar
Alexandre Julliard committed
921 922 923 924 925 926
    {
    case SCROLL_NOWHERE:  /* No tracking in progress */
        break;

    case SCROLL_TOP_ARROW:
        SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
927 928
                           (hittest == SCROLL_trackHitTest), FALSE );
        if (hittest == SCROLL_trackHitTest)
Alexandre Julliard's avatar
Alexandre Julliard committed
929 930
        {
            if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
Alexandre Julliard's avatar
Alexandre Julliard committed
931
            {
932
                SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
933
                                SB_LINEUP, (LPARAM)hwndCtl );
934
	    }
935

936
	    SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
937
                            SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
938
        }
939
        else KillSystemTimer( hwnd, SCROLL_TIMER );
Alexandre Julliard's avatar
Alexandre Julliard committed
940 941 942
        break;

    case SCROLL_TOP_RECT:
Alexandre Julliard's avatar
Alexandre Julliard committed
943 944
        SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbSize,
                             thumbPos, infoPtr->flags, vertical,
945 946
                             (hittest == SCROLL_trackHitTest), FALSE );
        if (hittest == SCROLL_trackHitTest)
Alexandre Julliard's avatar
Alexandre Julliard committed
947 948
        {
            if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
Alexandre Julliard's avatar
Alexandre Julliard committed
949
            {
950
                SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
951
                                SB_PAGEUP, (LPARAM)hwndCtl );
Alexandre Julliard's avatar
Alexandre Julliard committed
952
            }
953
            SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
954
                              SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
955
        }
956
        else KillSystemTimer( hwnd, SCROLL_TIMER );
Alexandre Julliard's avatar
Alexandre Julliard committed
957 958 959 960
        break;

    case SCROLL_THUMB:
        if (msg == WM_LBUTTONDOWN)
Alexandre Julliard's avatar
Alexandre Julliard committed
961
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
962 963 964
            SCROLL_TrackingWin = hwnd;
            SCROLL_TrackingBar = nBar;
            SCROLL_TrackingPos = trackThumbPos + lastMousePos - lastClickPos;
965 966 967
            SCROLL_TrackingVal = SCROLL_GetThumbVal( infoPtr, &rect,
                                                        vertical,
                                                        SCROLL_TrackingPos );
968 969
	    if (!SCROLL_MovingThumb)
		SCROLL_DrawMovingThumb(hdc, &rect, vertical, arrowSize, thumbSize);
Alexandre Julliard's avatar
Alexandre Julliard committed
970
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
971
        else if (msg == WM_LBUTTONUP)
Alexandre Julliard's avatar
Alexandre Julliard committed
972
        {
973 974
	    if (SCROLL_MovingThumb)
		SCROLL_DrawMovingThumb(hdc, &rect, vertical, arrowSize, thumbSize);
975

Alexandre Julliard's avatar
Alexandre Julliard committed
976 977 978
            SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbSize,
                                 thumbPos, infoPtr->flags, vertical,
                                 FALSE, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
979
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
980 981
        else  /* WM_MOUSEMOVE */
        {
982
            INT pos;
Alexandre Julliard's avatar
Alexandre Julliard committed
983

984 985 986 987 988 989
            if (!SCROLL_PtInRectEx( &rect, pt, vertical )) pos = lastClickPos;
            else
	    {
		pt = SCROLL_ClipPos( &rect, pt );
		pos = vertical ? (pt.y - rect.top) : (pt.x - rect.left);
	    }
990
            if ( (pos != lastMousePos) || (!SCROLL_MovingThumb) )
Alexandre Julliard's avatar
Alexandre Julliard committed
991
            {
992 993
		if (SCROLL_MovingThumb)
		    SCROLL_DrawMovingThumb( hdc, &rect, vertical,
Alexandre Julliard's avatar
Alexandre Julliard committed
994
                                        arrowSize, thumbSize );
Alexandre Julliard's avatar
Alexandre Julliard committed
995
                lastMousePos = pos;
Alexandre Julliard's avatar
Alexandre Julliard committed
996 997 998 999
                SCROLL_TrackingPos = trackThumbPos + pos - lastClickPos;
                SCROLL_TrackingVal = SCROLL_GetThumbVal( infoPtr, &rect,
                                                         vertical,
                                                         SCROLL_TrackingPos );
1000
                SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
Alexandre Julliard's avatar
Alexandre Julliard committed
1001
                                MAKEWPARAM( SB_THUMBTRACK, SCROLL_TrackingVal),
1002
                                (LPARAM)hwndCtl );
1003 1004
		if (!SCROLL_MovingThumb)
		    SCROLL_DrawMovingThumb( hdc, &rect, vertical,
Alexandre Julliard's avatar
Alexandre Julliard committed
1005
                                        arrowSize, thumbSize );
Alexandre Julliard's avatar
Alexandre Julliard committed
1006
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
1007
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1008
        break;
1009

Alexandre Julliard's avatar
Alexandre Julliard committed
1010
    case SCROLL_BOTTOM_RECT:
Alexandre Julliard's avatar
Alexandre Julliard committed
1011 1012
        SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbSize,
                             thumbPos, infoPtr->flags, vertical,
1013 1014
                             FALSE, (hittest == SCROLL_trackHitTest) );
        if (hittest == SCROLL_trackHitTest)
Alexandre Julliard's avatar
Alexandre Julliard committed
1015 1016
        {
            if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
Alexandre Julliard's avatar
Alexandre Julliard committed
1017
            {
1018
                SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
1019
                                SB_PAGEDOWN, (LPARAM)hwndCtl );
Alexandre Julliard's avatar
Alexandre Julliard committed
1020
            }
1021
            SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
1022
                              SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
1023
        }
1024
        else KillSystemTimer( hwnd, SCROLL_TIMER );
Alexandre Julliard's avatar
Alexandre Julliard committed
1025
        break;
1026

Alexandre Julliard's avatar
Alexandre Julliard committed
1027 1028
    case SCROLL_BOTTOM_ARROW:
        SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
1029 1030
                           FALSE, (hittest == SCROLL_trackHitTest) );
        if (hittest == SCROLL_trackHitTest)
Alexandre Julliard's avatar
Alexandre Julliard committed
1031 1032
        {
            if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
Alexandre Julliard's avatar
Alexandre Julliard committed
1033
            {
1034
                SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
1035
                                SB_LINEDOWN, (LPARAM)hwndCtl );
1036 1037 1038
	    }

	    SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
1039
                            SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
1040
        }
1041
        else KillSystemTimer( hwnd, SCROLL_TIMER );
Alexandre Julliard's avatar
Alexandre Julliard committed
1042 1043 1044
        break;
    }

1045 1046 1047 1048 1049 1050 1051
    if (msg == WM_LBUTTONDOWN)
    {

        if (hittest == SCROLL_THUMB)
        {
            UINT val = SCROLL_GetThumbVal( infoPtr, &rect, vertical,
                                 trackThumbPos + lastMousePos - lastClickPos );
1052
            SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
1053 1054 1055 1056
                            MAKEWPARAM( SB_THUMBTRACK, val ), (LPARAM)hwndCtl );
        }
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
1057 1058
    if (msg == WM_LBUTTONUP)
    {
1059 1060 1061 1062
	hittest = SCROLL_trackHitTest;
	SCROLL_trackHitTest = SCROLL_NOWHERE;  /* Terminate tracking */

        if (hittest == SCROLL_THUMB)
Alexandre Julliard's avatar
Alexandre Julliard committed
1063
        {
1064
            UINT val = SCROLL_GetThumbVal( infoPtr, &rect, vertical,
Alexandre Julliard's avatar
Alexandre Julliard committed
1065
                                 trackThumbPos + lastMousePos - lastClickPos );
1066
            SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
1067
                            MAKEWPARAM( SB_THUMBPOSITION, val ), (LPARAM)hwndCtl );
Alexandre Julliard's avatar
Alexandre Julliard committed
1068
        }
1069
        /* SB_ENDSCROLL doesn't report thumb position */
1070
        SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
1071
                          SB_ENDSCROLL, (LPARAM)hwndCtl );
1072 1073 1074

        /* Terminate tracking */
        SCROLL_TrackingWin = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1075 1076
    }

1077
    ReleaseDC( hwnd, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
1078 1079 1080
}


1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
/***********************************************************************
 *           SCROLL_TrackScrollBar
 *
 * Track a mouse button press on a scroll-bar.
 * pt is in screen-coordinates for non-client scroll bars.
 */
void SCROLL_TrackScrollBar( HWND hwnd, INT scrollbar, POINT pt )
{
    MSG msg;
    INT xoffset = 0, yoffset = 0;

    if (scrollbar != SB_CTL)
    {
        WND *wndPtr = WIN_GetPtr( hwnd );
1095
        if (!wndPtr || wndPtr == WND_OTHER_PROCESS || wndPtr == WND_DESKTOP) return;
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109
        xoffset = wndPtr->rectClient.left - wndPtr->rectWindow.left;
        yoffset = wndPtr->rectClient.top - wndPtr->rectWindow.top;
        WIN_ReleasePtr( wndPtr );
        ScreenToClient( hwnd, &pt );
        pt.x += xoffset;
        pt.y += yoffset;
    }

    SCROLL_HandleScrollEvent( hwnd, scrollbar, WM_LBUTTONDOWN, pt );

    do
    {
        if (!GetMessageW( &msg, 0, 0, 0 )) break;
        if (CallMsgFilterW( &msg, MSGF_SCROLLBAR )) continue;
1110 1111 1112
        if (msg.message == WM_LBUTTONUP ||
            msg.message == WM_MOUSEMOVE ||
            (msg.message == WM_SYSTIMER && msg.wParam == SCROLL_TIMER))
1113
        {
1114 1115
            pt.x = (short)LOWORD(msg.lParam) + xoffset;
            pt.y = (short)HIWORD(msg.lParam) + yoffset;
1116
            SCROLL_HandleScrollEvent( hwnd, scrollbar, msg.message, pt );
1117 1118 1119
        }
        else
        {
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131
            TranslateMessage( &msg );
            DispatchMessageW( &msg );
        }
        if (!IsWindow( hwnd ))
        {
            ReleaseCapture();
            break;
        }
    } while (msg.message != WM_LBUTTONUP);
}


1132 1133 1134
/***********************************************************************
 *           SCROLL_CreateScrollBar
 *
1135 1136 1137 1138 1139
 * Create a scroll bar
 *
 * PARAMS
 *    hwnd     [I] Handle of window with scrollbar(s)
 *    lpCreate [I] The style and place of the scroll bar
1140
 */
1141
static void SCROLL_CreateScrollBar(HWND hwnd, LPCREATESTRUCTW lpCreate)
1142
{
1143
    LPSCROLLBAR_INFO info = SCROLL_GetInternalInfo(hwnd, SB_CTL, TRUE);
1144 1145 1146 1147 1148 1149 1150 1151 1152 1153
    if (!info) return;

    TRACE("hwnd=%p lpCreate=%p\n", hwnd, lpCreate);

    if (lpCreate->style & WS_DISABLED)
    {
        info->flags = ESB_DISABLE_BOTH;
        TRACE("Created WS_DISABLED scrollbar\n");
    }

1154

1155
    if (lpCreate->style & (SBS_SIZEGRIP | SBS_SIZEBOX))
1156
    {
1157 1158 1159 1160 1161 1162 1163 1164
        if (lpCreate->style & SBS_SIZEBOXTOPLEFTALIGN)
            MoveWindow( hwnd, lpCreate->x, lpCreate->y, GetSystemMetrics(SM_CXVSCROLL)+1,
                        GetSystemMetrics(SM_CYHSCROLL)+1, FALSE );
        else if(lpCreate->style & SBS_SIZEBOXBOTTOMRIGHTALIGN)
            MoveWindow( hwnd, lpCreate->x+lpCreate->cx-GetSystemMetrics(SM_CXVSCROLL)-1, 
                        lpCreate->y+lpCreate->cy-GetSystemMetrics(SM_CYHSCROLL)-1,
                        GetSystemMetrics(SM_CXVSCROLL)+1,
                        GetSystemMetrics(SM_CYHSCROLL)+1, FALSE );
1165 1166
    }
    else if (lpCreate->style & SBS_VERT)
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187
    {
        if (lpCreate->style & SBS_LEFTALIGN)
            MoveWindow( hwnd, lpCreate->x, lpCreate->y,
                        GetSystemMetrics(SM_CXVSCROLL)+1, lpCreate->cy, FALSE );
        else if (lpCreate->style & SBS_RIGHTALIGN)
            MoveWindow( hwnd,
                        lpCreate->x+lpCreate->cx-GetSystemMetrics(SM_CXVSCROLL)-1,
                        lpCreate->y,
                        GetSystemMetrics(SM_CXVSCROLL)+1, lpCreate->cy, FALSE );
    }
    else  /* SBS_HORZ */
    {
        if (lpCreate->style & SBS_TOPALIGN)
            MoveWindow( hwnd, lpCreate->x, lpCreate->y,
                        lpCreate->cx, GetSystemMetrics(SM_CYHSCROLL)+1, FALSE );
        else if (lpCreate->style & SBS_BOTTOMALIGN)
            MoveWindow( hwnd,
                        lpCreate->x,
                        lpCreate->y+lpCreate->cy-GetSystemMetrics(SM_CYHSCROLL)-1,
                        lpCreate->cx, GetSystemMetrics(SM_CYHSCROLL)+1, FALSE );
    }
1188 1189 1190
}


1191 1192 1193
/*************************************************************************
 *           SCROLL_GetScrollInfo
 *
1194 1195 1196 1197 1198 1199
 * Internal helper for the API function
 *
 * PARAMS
 *    hwnd [I]  Handle of window with scrollbar(s)
 *    nBar [I]  One of SB_HORZ, SB_VERT, or SB_CTL
 *    info [IO] fMask specifies which values to retrieve
1200 1201 1202
 *
 * RETURNS
 *    FALSE if requested field not filled (f.i. scroll bar does not exist)
1203
 */
1204
static BOOL SCROLL_GetScrollInfo(HWND hwnd, INT nBar, LPSCROLLINFO info)
1205 1206 1207 1208
{
    LPSCROLLBAR_INFO infoPtr;

    /* handle invalid data structure */
1209
    if (!SCROLL_ScrollInfoValid(info)
1210
        || !(infoPtr = SCROLL_GetInternalInfo(hwnd, nBar, FALSE)))
1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223
            return FALSE;

    /* fill in the desired scroll info structure */
    if (info->fMask & SIF_PAGE) info->nPage = infoPtr->page;
    if (info->fMask & SIF_POS) info->nPos = infoPtr->curVal;
    if ((info->fMask & SIF_TRACKPOS) && (info->cbSize == sizeof(*info)))
        info->nTrackPos = (SCROLL_TrackingWin == WIN_GetFullHandle(hwnd)) ? SCROLL_TrackingVal : infoPtr->curVal;
    if (info->fMask & SIF_RANGE)
    {
        info->nMin = infoPtr->minVal;
        info->nMax = infoPtr->maxVal;
    }

1224 1225 1226 1227
    TRACE("cbSize %02x fMask %04x nMin %d nMax %d nPage %u nPos %d nTrackPos %d\n",
           info->cbSize, info->fMask, info->nMin, info->nMax, info->nPage,
           info->nPos, info->nTrackPos);

1228 1229 1230 1231
    return (info->fMask & SIF_ALL) != 0;
}


1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251
/*************************************************************************
 *           SCROLL_GetScrollBarInfo
 *
 * Internal helper for the API function
 *
 * PARAMS
 *    hwnd     [I]  Handle of window with scrollbar(s)
 *    idObject [I]  One of OBJID_CLIENT, OBJID_HSCROLL, or OBJID_VSCROLL
 *    info     [IO] cbSize specifies the size of the structure
 *
 * RETURNS
 *    FALSE if failed
 */
static BOOL SCROLL_GetScrollBarInfo(HWND hwnd, LONG idObject, LPSCROLLBARINFO info)
{
    LPSCROLLBAR_INFO infoPtr;
    INT nBar;
    INT nDummy;
    DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
    BOOL pressed;
1252
    RECT rect;
1253 1254 1255 1256 1257 1258 1259 1260 1261 1262

    switch (idObject)
    {
        case OBJID_CLIENT: nBar = SB_CTL; break;
        case OBJID_HSCROLL: nBar = SB_HORZ; break;
        case OBJID_VSCROLL: nBar = SB_VERT; break;
        default: return FALSE;
    }

    /* handle invalid data structure */
1263
    if (info->cbSize != sizeof(*info))
1264 1265
        return FALSE;

1266 1267
    SCROLL_GetScrollBarRect(hwnd, nBar, &info->rcScrollBar, &nDummy,
                            &info->dxyLineButton, &info->xyThumbTop);
1268 1269 1270
    /* rcScrollBar needs to be in screen coordinates */
    GetWindowRect(hwnd, &rect);
    OffsetRect(&info->rcScrollBar, rect.left, rect.top);
1271 1272 1273 1274

    info->xyThumbBottom = info->xyThumbTop + info->dxyLineButton;

    infoPtr = SCROLL_GetInternalInfo(hwnd, nBar, TRUE);
1275 1276 1277
    if (!infoPtr)
        return FALSE;

1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331
    /* Scroll bar state */
    info->rgstate[0] = 0;
    if ((nBar == SB_HORZ && !(style & WS_HSCROLL))
        || (nBar == SB_VERT && !(style & WS_VSCROLL)))
        info->rgstate[0] |= STATE_SYSTEM_INVISIBLE;
    if (infoPtr->minVal >= infoPtr->maxVal - max(infoPtr->page - 1, 0))
    {
        if (!(info->rgstate[0] & STATE_SYSTEM_INVISIBLE))
            info->rgstate[0] |= STATE_SYSTEM_UNAVAILABLE;
        else
            info->rgstate[0] |= STATE_SYSTEM_OFFSCREEN;
    }
    if (nBar == SB_CTL && !IsWindowEnabled(hwnd))
        info->rgstate[0] |= STATE_SYSTEM_UNAVAILABLE;
    
    pressed = ((nBar == SB_VERT) == SCROLL_trackVertical && GetCapture() == hwnd);
    
    /* Top/left arrow button state. MSDN says top/right, but I don't believe it */
    info->rgstate[1] = 0;
    if (pressed && SCROLL_trackHitTest == SCROLL_TOP_ARROW)
        info->rgstate[1] |= STATE_SYSTEM_PRESSED;
    if (infoPtr->flags & ESB_DISABLE_LTUP)
        info->rgstate[1] |= STATE_SYSTEM_UNAVAILABLE;

    /* Page up/left region state. MSDN says up/right, but I don't believe it */
    info->rgstate[2] = 0;
    if (infoPtr->curVal == infoPtr->minVal)
        info->rgstate[2] |= STATE_SYSTEM_INVISIBLE;
    if (pressed && SCROLL_trackHitTest == SCROLL_TOP_RECT)
        info->rgstate[2] |= STATE_SYSTEM_PRESSED;

    /* Thumb state */
    info->rgstate[3] = 0;
    if (pressed && SCROLL_trackHitTest == SCROLL_THUMB)
        info->rgstate[3] |= STATE_SYSTEM_PRESSED;

    /* Page down/right region state. MSDN says down/left, but I don't believe it */
    info->rgstate[4] = 0;
    if (infoPtr->curVal >= infoPtr->maxVal - 1)
        info->rgstate[4] |= STATE_SYSTEM_INVISIBLE;
    if (pressed && SCROLL_trackHitTest == SCROLL_BOTTOM_RECT)
        info->rgstate[4] |= STATE_SYSTEM_PRESSED;
    
    /* Bottom/right arrow button state. MSDN says bottom/left, but I don't believe it */
    info->rgstate[5] = 0;
    if (pressed && SCROLL_trackHitTest == SCROLL_BOTTOM_ARROW)
        info->rgstate[5] |= STATE_SYSTEM_PRESSED;
    if (infoPtr->flags & ESB_DISABLE_RTDN)
        info->rgstate[5] |= STATE_SYSTEM_UNAVAILABLE;
        
    return TRUE;
}


1332 1333 1334 1335
/*************************************************************************
 *           SCROLL_GetScrollPos
 *
 *  Internal helper for the API function
1336 1337 1338 1339
 *
 * PARAMS
 *    hwnd [I]  Handle of window with scrollbar(s)
 *    nBar [I]  One of SB_HORZ, SB_VERT, or SB_CTL
1340
 */
1341
static INT SCROLL_GetScrollPos(HWND hwnd, INT nBar)
1342
{
1343
    LPSCROLLBAR_INFO infoPtr = SCROLL_GetInternalInfo(hwnd, nBar, FALSE);
1344 1345 1346 1347 1348 1349 1350 1351 1352
    return infoPtr ? infoPtr->curVal: 0;
}


/*************************************************************************
 *           SCROLL_GetScrollRange
 *
 *  Internal helper for the API function
 *
1353 1354 1355 1356 1357 1358
 * PARAMS
 *    hwnd  [I]  Handle of window with scrollbar(s)
 *    nBar  [I]  One of SB_HORZ, SB_VERT, or SB_CTL
 *    lpMin [O]  Where to store minimum value
 *    lpMax [O]  Where to store maximum value
 *
1359 1360 1361
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
1362
 */
1363
static BOOL SCROLL_GetScrollRange(HWND hwnd, INT nBar, LPINT lpMin, LPINT lpMax)
1364
{
1365
    LPSCROLLBAR_INFO infoPtr = SCROLL_GetInternalInfo(hwnd, nBar, FALSE);
1366 1367 1368 1369

    if (lpMin) *lpMin = infoPtr ? infoPtr->minVal : 0;
    if (lpMax) *lpMax = infoPtr ? infoPtr->maxVal : 0;

1370
    return TRUE;
1371 1372 1373
}


1374 1375 1376
/*************************************************************************
 *           SCROLL_SetScrollRange
 *
1377 1378 1379 1380 1381 1382
 * PARAMS
 *    hwnd  [I]  Handle of window with scrollbar(s)
 *    nBar  [I]  One of SB_HORZ, SB_VERT, or SB_CTL
 *    lpMin [I]  Minimum value
 *    lpMax [I]  Maximum value
 *
1383
 */
1384
static BOOL SCROLL_SetScrollRange(HWND hwnd, INT nBar, INT minVal, INT maxVal)
1385
{
1386
    LPSCROLLBAR_INFO infoPtr = SCROLL_GetInternalInfo(hwnd, nBar, FALSE);
1387 1388 1389 1390 1391 1392 1393 1394

    TRACE("hwnd=%p nBar=%d min=%d max=%d\n", hwnd, nBar, minVal, maxVal);

    if (infoPtr)
    {
        infoPtr->minVal = minVal;
        infoPtr->maxVal = maxVal;
    }
1395
    return TRUE;
1396 1397
}

1398

Alexandre Julliard's avatar
Alexandre Julliard committed
1399
/***********************************************************************
1400
 *           ScrollBarWndProc_common
Alexandre Julliard's avatar
Alexandre Julliard committed
1401
 */
1402
LRESULT ScrollBarWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
1403
{
1404 1405
    if (!IsWindow( hwnd )) return 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
1406 1407 1408
    switch(message)
    {
    case WM_CREATE:
1409 1410
        SCROLL_CreateScrollBar(hwnd, (LPCREATESTRUCTW)lParam);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1411 1412 1413 1414

    case WM_ENABLE:
        {
	    SCROLLBAR_INFO *infoPtr;
1415
	    if ((infoPtr = SCROLL_GetInternalInfo( hwnd, SB_CTL, FALSE )))
Alexandre Julliard's avatar
Alexandre Julliard committed
1416 1417 1418 1419 1420 1421 1422
	    {
		infoPtr->flags = wParam ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH;
		SCROLL_RefreshScrollBar(hwnd, SB_CTL, TRUE, TRUE);
	    }
	}
	return 0;

1423
    case WM_LBUTTONDBLCLK:
Alexandre Julliard's avatar
Alexandre Julliard committed
1424
    case WM_LBUTTONDOWN:
1425 1426
        {
	    POINT pt;
1427 1428
	    pt.x = (short)LOWORD(lParam);
	    pt.y = (short)HIWORD(lParam);
1429
            SCROLL_TrackScrollBar( hwnd, SB_CTL, pt );
1430 1431
	}
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1432 1433 1434
    case WM_LBUTTONUP:
    case WM_MOUSEMOVE:
    case WM_SYSTIMER:
Alexandre Julliard's avatar
Alexandre Julliard committed
1435
        {
1436
            POINT pt;
1437 1438
            pt.x = (short)LOWORD(lParam);
            pt.y = (short)HIWORD(lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1439 1440
            SCROLL_HandleScrollEvent( hwnd, SB_CTL, message, pt );
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1441 1442 1443
        break;

    case WM_KEYDOWN:
1444
        SCROLL_HandleKbdEvent(hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1445 1446
        break;

1447 1448 1449 1450 1451 1452
    case WM_KEYUP:
        ShowCaret(hwnd);
        break;

    case WM_SETFOCUS:
        {
Andreas Mohr's avatar
Andreas Mohr committed
1453
            /* Create a caret when a ScrollBar get focus */
1454 1455
            RECT rect;
            int arrowSize, thumbSize, thumbPos, vertical;
1456 1457 1458
            vertical = SCROLL_GetScrollBarRect( hwnd, SB_CTL, &rect,
                                                &arrowSize, &thumbSize, &thumbPos );
            if (!vertical)
1459
            {
1460
                CreateCaret(hwnd, (HBITMAP)1, thumbSize-2, rect.bottom-rect.top-2);
1461
                SetCaretPos(thumbPos+1, rect.top+1);
1462
            }
1463 1464
            else
            {
1465
                CreateCaret(hwnd, (HBITMAP)1, rect.right-rect.left-2,thumbSize-2);
1466 1467 1468
                SetCaretPos(rect.top+1, thumbPos+1);
            }
            ShowCaret(hwnd);
1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491
        }
        break;

    case WM_KILLFOCUS:
        {
            RECT rect;
            int arrowSize, thumbSize, thumbPos, vertical;
            vertical = SCROLL_GetScrollBarRect( hwnd, SB_CTL, &rect,&arrowSize, &thumbSize, &thumbPos );
            if (!vertical){
                rect.left=thumbPos+1;
                rect.right=rect.left+thumbSize;
            }
            else
            {
                rect.top=thumbPos+1;
                rect.bottom=rect.top+thumbSize;
            }
            HideCaret(hwnd);
            InvalidateRect(hwnd,&rect,0);
            DestroyCaret();
        }
        break;

Alexandre Julliard's avatar
Alexandre Julliard committed
1492
    case WM_ERASEBKGND:
Alexandre Julliard's avatar
Alexandre Julliard committed
1493 1494 1495 1496
         return 1;

    case WM_GETDLGCODE:
         return DLGC_WANTARROWS; /* Windows returns this value */
Alexandre Julliard's avatar
Alexandre Julliard committed
1497 1498 1499

    case WM_PAINT:
        {
1500
            PAINTSTRUCT ps;
1501
            HDC hdc = wParam ? (HDC)wParam : BeginPaint(hwnd, &ps);
1502 1503 1504 1505 1506
            if (GetWindowLongW( hwnd, GWL_STYLE ) & SBS_SIZEGRIP)
            {
                SCROLL_DrawSizeGrip( hwnd, hdc);
            }
            else if (GetWindowLongW( hwnd, GWL_STYLE ) & SBS_SIZEBOX)
1507 1508 1509 1510 1511
            {
                RECT rc;
                GetClientRect( hwnd, &rc );
                FillRect( hdc, &rc, GetSysColorBrush(COLOR_SCROLLBAR) );
            }
1512 1513
            else
                SCROLL_DrawScrollBar( hwnd, hdc, SB_CTL, TRUE, TRUE );
1514
            if (!wParam) EndPaint(hwnd, &ps);
Alexandre Julliard's avatar
Alexandre Julliard committed
1515 1516 1517
        }
        break;

1518 1519
    case SBM_SETPOS:
        return SetScrollPos( hwnd, SB_CTL, wParam, (BOOL)lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1520

1521
    case SBM_GETPOS:
1522
       return SCROLL_GetScrollPos(hwnd, SB_CTL);
Alexandre Julliard's avatar
Alexandre Julliard committed
1523

1524
    case SBM_SETRANGEREDRAW:
1525
    case SBM_SETRANGE:
1526
        {
1527
            INT oldPos = SCROLL_GetScrollPos( hwnd, SB_CTL );
1528 1529 1530
            SCROLL_SetScrollRange( hwnd, SB_CTL, wParam, lParam );
            if (message == SBM_SETRANGEREDRAW)
                SCROLL_RefreshScrollBar( hwnd, SB_CTL, TRUE, TRUE );
1531
            if (oldPos != SCROLL_GetScrollPos( hwnd, SB_CTL )) return oldPos;
1532 1533
        }
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1534

1535
    case SBM_GETRANGE:
1536
        return SCROLL_GetScrollRange(hwnd, SB_CTL, (LPINT)wParam, (LPINT)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1537

1538 1539
    case SBM_ENABLE_ARROWS:
        return EnableScrollBar( hwnd, SB_CTL, wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1540

1541
    case SBM_SETSCROLLINFO:
1542
        return SCROLL_SetScrollInfo( hwnd, SB_CTL, (SCROLLINFO *)lParam, wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1543

1544
    case SBM_GETSCROLLINFO:
1545
        return SCROLL_GetScrollInfo(hwnd, SB_CTL, (SCROLLINFO *)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1546

1547 1548 1549
    case SBM_GETSCROLLBARINFO:
        return SCROLL_GetScrollBarInfo(hwnd, OBJID_CLIENT, (SCROLLBARINFO *)lParam);

Alexandre Julliard's avatar
Alexandre Julliard committed
1550 1551 1552 1553 1554 1555 1556
    case 0x00e5:
    case 0x00e7:
    case 0x00e8:
    case 0x00ec:
    case 0x00ed:
    case 0x00ee:
    case 0x00ef:
1557
        ERR("unknown Win32 msg %04x wp=%08lx lp=%08lx\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
1558
		    message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1559
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1560

Alexandre Julliard's avatar
Alexandre Julliard committed
1561
    default:
Alexandre Julliard's avatar
Alexandre Julliard committed
1562
        if (message >= WM_USER)
1563
            WARN("unknown msg %04x wp=%04lx lp=%08lx\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
1564
			 message, wParam, lParam );
1565 1566 1567 1568
        if (unicode)
            return DefWindowProcW( hwnd, message, wParam, lParam );
        else
            return DefWindowProcA( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1569 1570 1571 1572
    }
    return 0;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1573

Alexandre Julliard's avatar
Alexandre Julliard committed
1574
/*************************************************************************
1575
 *           SetScrollInfo   (USER32.@)
1576
 *
1577
 * SetScrollInfo can be used to set the position, upper bound,
Alexandre Julliard's avatar
Alexandre Julliard committed
1578 1579
 * lower bound, and page size of a scrollbar control.
 *
1580 1581 1582 1583 1584 1585
 * PARAMS
 *    hwnd    [I]  Handle of window with scrollbar(s)
 *    nBar    [I]  One of SB_HORZ, SB_VERT, or SB_CTL
 *    info    [I]  Specifies what to change and new values
 *    bRedraw [I]  Should scrollbar be redrawn afterwards?
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1586 1587 1588 1589 1590 1591 1592 1593
 * RETURNS
 *    Scrollbar position
 *
 * NOTE
 *    For 100 lines of text to be displayed in a window of 25 lines,
 *  one would for instance use info->nMin=0, info->nMax=75
 *  (corresponding to the 76 different positions of the window on
 *  the text), and info->nPage=25.
Alexandre Julliard's avatar
Alexandre Julliard committed
1594
 */
1595
INT WINAPI SetScrollInfo(HWND hwnd, INT nBar, const SCROLLINFO *info, BOOL bRedraw)
Alexandre Julliard's avatar
Alexandre Julliard committed
1596
{
1597
    TRACE("hwnd=%p nBar=%d info=%p, bRedraw=%d\n", hwnd, nBar, info, bRedraw);
1598

1599 1600 1601
    /* Refer SB_CTL requests to the window */
    if (nBar == SB_CTL)
        return SendMessageW(hwnd, SBM_SETSCROLLINFO, bRedraw, (LPARAM)info);
1602
    else
1603
        return SCROLL_SetScrollInfo( hwnd, nBar, info, bRedraw );
1604 1605
}

1606
static INT SCROLL_SetScrollInfo( HWND hwnd, INT nBar, LPCSCROLLINFO info, BOOL bRedraw )
1607
{
1608
    /* Update the scrollbar state and set action flags according to
1609 1610
     * what has to be done graphics wise. */

Alexandre Julliard's avatar
Alexandre Julliard committed
1611
    SCROLLBAR_INFO *infoPtr;
1612
    UINT new_flags;
1613
    INT action = 0;
1614

1615 1616 1617 1618
    /* handle invalid data structure */
    if (!SCROLL_ScrollInfoValid(info)
        || !(infoPtr = SCROLL_GetInternalInfo(hwnd, nBar, TRUE)))
            return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1619

1620 1621
    if (TRACE_ON(scroll))
    {
1622
        TRACE("hwnd=%p bar=%d", hwnd, nBar);
1623 1624 1625 1626
        if (info->fMask & SIF_PAGE) TRACE( " page=%d", info->nPage );
        if (info->fMask & SIF_POS) TRACE( " pos=%d", info->nPos );
        if (info->fMask & SIF_RANGE) TRACE( " min=%d max=%d", info->nMin, info->nMax );
        TRACE("\n");
1627 1628
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
1629 1630 1631 1632
    /* Set the page size */

    if (info->fMask & SIF_PAGE)
    {
1633
	if( infoPtr->page != info->nPage )
1634
	{
1635
            infoPtr->page = info->nPage;
1636
            action |= SA_SSI_REFRESH;
1637
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1638 1639 1640 1641 1642 1643
    }

    /* Set the scroll pos */

    if (info->fMask & SIF_POS)
    {
1644
	if( infoPtr->curVal != info->nPos )
1645
	{
1646
	    infoPtr->curVal = info->nPos;
1647
            action |= SA_SSI_REFRESH;
1648
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1649 1650 1651 1652 1653 1654 1655 1656
    }

    /* Set the scroll range */

    if (info->fMask & SIF_RANGE)
    {
        /* Invalid range -> range is set to (0,0) */
        if ((info->nMin > info->nMax) ||
1657
            ((UINT)(info->nMax - info->nMin) >= 0x80000000))
Alexandre Julliard's avatar
Alexandre Julliard committed
1658
        {
1659
            action |= SA_SSI_REFRESH;
1660 1661
            infoPtr->minVal = 0;
            infoPtr->maxVal = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1662 1663 1664
        }
        else
        {
1665 1666
	    if( infoPtr->minVal != info->nMin ||
		infoPtr->maxVal != info->nMax )
1667
	    {
1668
                action |= SA_SSI_REFRESH;
1669 1670
                infoPtr->minVal = info->nMin;
                infoPtr->maxVal = info->nMax;
1671
	    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1672 1673 1674 1675
        }
    }

    /* Make sure the page size is valid */
1676 1677 1678
    if (infoPtr->page < 0) infoPtr->page = 0;
    else if (infoPtr->page > infoPtr->maxVal - infoPtr->minVal + 1 )
        infoPtr->page = infoPtr->maxVal - infoPtr->minVal + 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
1679 1680 1681

    /* Make sure the pos is inside the range */

1682 1683 1684 1685
    if (infoPtr->curVal < infoPtr->minVal)
        infoPtr->curVal = infoPtr->minVal;
    else if (infoPtr->curVal > infoPtr->maxVal - max( infoPtr->page-1, 0 ))
        infoPtr->curVal = infoPtr->maxVal - max( infoPtr->page-1, 0 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1686

1687
    TRACE("    new values: page=%d pos=%d min=%d max=%d\n",
1688 1689
		 infoPtr->page, infoPtr->curVal,
		 infoPtr->minVal, infoPtr->maxVal );
Alexandre Julliard's avatar
Alexandre Julliard committed
1690

1691
    /* don't change the scrollbar state if SetScrollInfo
1692 1693 1694 1695
     * is just called with SIF_DISABLENOSCROLL
     */
    if(!(info->fMask & SIF_ALL)) goto done;

Alexandre Julliard's avatar
Alexandre Julliard committed
1696 1697
    /* Check if the scrollbar should be hidden or disabled */

Alexandre Julliard's avatar
Alexandre Julliard committed
1698
    if (info->fMask & (SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL))
Alexandre Julliard's avatar
Alexandre Julliard committed
1699
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1700
        new_flags = infoPtr->flags;
1701
        if (infoPtr->minVal >= infoPtr->maxVal - max( infoPtr->page-1, 0 ))
Alexandre Julliard's avatar
Alexandre Julliard committed
1702
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
1703 1704
            /* Hide or disable scroll-bar */
            if (info->fMask & SIF_DISABLENOSCROLL)
1705
	    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1706
                new_flags = ESB_DISABLE_BOTH;
1707
                action |= SA_SSI_REFRESH;
1708
	    }
1709
            else if ((nBar != SB_CTL) && (action & SA_SSI_REFRESH))
1710
	    {
1711
                action = SA_SSI_HIDE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1712 1713
            }
        }
1714 1715
        else  /* Show and enable scroll-bar only if no page only changed. */
        if (info->fMask != SIF_PAGE)
Alexandre Julliard's avatar
Alexandre Julliard committed
1716
        {
1717 1718
	    new_flags = ESB_ENABLE_BOTH;
            if ((nBar != SB_CTL) && ( (action & SA_SSI_REFRESH) ))
1719
                action |= SA_SSI_SHOW;
Alexandre Julliard's avatar
Alexandre Julliard committed
1720 1721
        }

1722
        if (infoPtr->flags != new_flags) /* check arrow flags */
Alexandre Julliard's avatar
Alexandre Julliard committed
1723 1724
        {
            infoPtr->flags = new_flags;
1725
            action |= SA_SSI_REPAINT_ARROWS;
Alexandre Julliard's avatar
Alexandre Julliard committed
1726
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1727 1728
    }

1729
done:
1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742
    if( action & SA_SSI_HIDE )
        SCROLL_ShowScrollBar( hwnd, nBar, FALSE, FALSE );
    else
    {
        if( action & SA_SSI_SHOW )
            if( SCROLL_ShowScrollBar( hwnd, nBar, TRUE, TRUE ) )
                return infoPtr->curVal; /* SetWindowPos() already did the painting */

        if( bRedraw )
            SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, TRUE );
        else if( action & SA_SSI_REPAINT_ARROWS )
            SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, FALSE );
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1743

1744
    /* Return current position */
1745
    return infoPtr->curVal;
Alexandre Julliard's avatar
Alexandre Julliard committed
1746 1747 1748 1749
}


/*************************************************************************
1750
 *           GetScrollInfo   (USER32.@)
1751
 *
1752
 * GetScrollInfo can be used to retrieve the position, upper bound,
Alexandre Julliard's avatar
Alexandre Julliard committed
1753 1754
 * lower bound, and page size of a scrollbar control.
 *
1755 1756 1757 1758 1759
 * PARAMS
 *  hwnd [I]  Handle of window with scrollbar(s)
 *  nBar [I]  One of SB_HORZ, SB_VERT, or SB_CTL
 *  info [IO] fMask specifies which values to retrieve
 *
1760 1761
 * RETURNS
 *  TRUE if SCROLLINFO is filled
1762 1763
 *  ( if nBar is SB_CTL, GetScrollInfo returns TRUE even if nothing
 *  is filled)
Alexandre Julliard's avatar
Alexandre Julliard committed
1764
 */
1765
BOOL WINAPI GetScrollInfo(HWND hwnd, INT nBar, LPSCROLLINFO info)
Alexandre Julliard's avatar
Alexandre Julliard committed
1766
{
1767
    TRACE("hwnd=%p nBar=%d info=%p\n", hwnd, nBar, info);
Alexandre Julliard's avatar
Alexandre Julliard committed
1768

1769 1770
    /* Refer SB_CTL requests to the window */
    if (nBar == SB_CTL)
1771
    {
1772
        SendMessageW(hwnd, SBM_GETSCROLLINFO, 0, (LPARAM)info);
1773 1774 1775
        return TRUE;
    }
    return SCROLL_GetScrollInfo(hwnd, nBar, info);
Alexandre Julliard's avatar
Alexandre Julliard committed
1776 1777 1778
}


1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794
/*************************************************************************
 *           GetScrollBarInfo   (USER32.@)
 *
 * GetScrollBarInfo can be used to retrieve information about a scrollbar
 * control.
 *
 * PARAMS
 *  hwnd     [I]  Handle of window with scrollbar(s)
 *  idObject [I]  One of OBJID_CLIENT, OBJID_HSCROLL, or OBJID_VSCROLL
 *  info     [IO] cbSize specifies the size of SCROLLBARINFO
 *
 * RETURNS
 *  TRUE if success
 */
BOOL WINAPI GetScrollBarInfo(HWND hwnd, LONG idObject, LPSCROLLBARINFO info)
{
1795
    TRACE("hwnd=%p idObject=%d info=%p\n", hwnd, idObject, info);
1796 1797 1798

    /* Refer OBJID_CLIENT requests to the window */
    if (idObject == OBJID_CLIENT)
1799
        return SendMessageW(hwnd, SBM_GETSCROLLBARINFO, 0, (LPARAM)info);
1800 1801 1802 1803 1804
    else
        return SCROLL_GetScrollBarInfo(hwnd, idObject, info);
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1805
/*************************************************************************
1806
 *           SetScrollPos   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1807
 *
1808 1809
 * Sets the current position of the scroll thumb.
 *
1810 1811 1812 1813 1814 1815
 * PARAMS
 *    hwnd    [I]  Handle of window with scrollbar(s)
 *    nBar    [I]  One of SB_HORZ, SB_VERT, or SB_CTL
 *    nPos    [I]  New value
 *    bRedraw [I]  Should scrollbar be redrawn afterwards?
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1816 1817 1818 1819 1820 1821 1822
 * RETURNS
 *    Success: Scrollbar position
 *    Failure: 0
 *
 * REMARKS
 *    Note the ambiguity when 0 is returned.  Use GetLastError
 *    to make sure there was an error (and to know which one).
Alexandre Julliard's avatar
Alexandre Julliard committed
1823
 */
1824
INT WINAPI SetScrollPos( HWND hwnd, INT nBar, INT nPos, BOOL bRedraw)
Alexandre Julliard's avatar
Alexandre Julliard committed
1825
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1826
    SCROLLINFO info;
Alexandre Julliard's avatar
Alexandre Julliard committed
1827
    SCROLLBAR_INFO *infoPtr;
1828
    INT oldPos;
Alexandre Julliard's avatar
Alexandre Julliard committed
1829

1830
    if (!(infoPtr = SCROLL_GetInternalInfo( hwnd, nBar, FALSE ))) return 0;
1831
    oldPos      = infoPtr->curVal;
Alexandre Julliard's avatar
Alexandre Julliard committed
1832 1833 1834
    info.cbSize = sizeof(info);
    info.nPos   = nPos;
    info.fMask  = SIF_POS;
1835
    SetScrollInfo( hwnd, nBar, &info, bRedraw );
Alexandre Julliard's avatar
Alexandre Julliard committed
1836 1837 1838 1839
    return oldPos;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1840
/*************************************************************************
1841
 *           GetScrollPos   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1842
 *
1843 1844
 * Gets the current position of the scroll thumb.
 *
1845 1846 1847 1848
 * PARAMS
 *    hwnd    [I]  Handle of window with scrollbar(s)
 *    nBar    [I]  One of SB_HORZ, SB_VERT, or SB_CTL
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
1849 1850
 * RETURNS
 *    Success: Current position
1851
 *    Failure: 0
Alexandre Julliard's avatar
Alexandre Julliard committed
1852 1853
 *
 * REMARKS
1854
 *    There is ambiguity when 0 is returned.  Use GetLastError
Alexandre Julliard's avatar
Alexandre Julliard committed
1855
 *    to make sure there was an error (and to know which one).
Alexandre Julliard's avatar
Alexandre Julliard committed
1856
 */
1857
INT WINAPI GetScrollPos(HWND hwnd, INT nBar)
Alexandre Julliard's avatar
Alexandre Julliard committed
1858
{
1859
    TRACE("hwnd=%p nBar=%d\n", hwnd, nBar);
Alexandre Julliard's avatar
Alexandre Julliard committed
1860

1861 1862
    /* Refer SB_CTL requests to the window */
    if (nBar == SB_CTL)
1863
        return SendMessageW(hwnd, SBM_GETPOS, 0, 0);
1864 1865
    else
        return SCROLL_GetScrollPos(hwnd, nBar);
Alexandre Julliard's avatar
Alexandre Julliard committed
1866 1867 1868 1869
}


/*************************************************************************
1870
 *           SetScrollRange   (USER32.@)
1871 1872
 * The SetScrollRange function sets the minimum and maximum scroll box positions 
 * If nMinPos and nMaxPos is the same value, the scroll bar will hide
Alexandre Julliard's avatar
Alexandre Julliard committed
1873
 *
1874 1875
 * Sets the range of the scroll bar.
 *
1876 1877 1878 1879 1880 1881 1882
 * PARAMS
 *    hwnd    [I]  Handle of window with scrollbar(s)
 *    nBar    [I]  One of SB_HORZ, SB_VERT, or SB_CTL
 *    minVal  [I]  New minimum value
 *    maxVal  [I]  New Maximum value
 *    bRedraw [I]  Should scrollbar be redrawn afterwards?
 *
1883 1884 1885
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
Alexandre Julliard's avatar
Alexandre Julliard committed
1886
 */
1887
BOOL WINAPI SetScrollRange(HWND hwnd, INT nBar, INT minVal, INT maxVal, BOOL bRedraw)
Alexandre Julliard's avatar
Alexandre Julliard committed
1888
{
1889 1890 1891
    SCROLLINFO info;
 
    TRACE("hwnd=%p nBar=%d min=%d max=%d, bRedraw=%d\n", hwnd, nBar, minVal, maxVal, bRedraw);
1892

1893 1894 1895 1896 1897 1898
    info.cbSize = sizeof(info);
    info.fMask  = SIF_RANGE;
    info.nMin   = minVal;
    info.nMax   = maxVal;
    SetScrollInfo( hwnd, nBar, &info, bRedraw );
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1899
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1900

Alexandre Julliard's avatar
Alexandre Julliard committed
1901

Alexandre Julliard's avatar
Alexandre Julliard committed
1902 1903 1904
/*************************************************************************
 *	     SCROLL_SetNCSbState
 *
1905
 * Updates both scrollbars at the same time. Used by MDI CalcChildScroll().
Alexandre Julliard's avatar
Alexandre Julliard committed
1906
 */
1907 1908
INT SCROLL_SetNCSbState(HWND hwnd, int vMin, int vMax, int vPos,
                        int hMin, int hMax, int hPos)
Alexandre Julliard's avatar
Alexandre Julliard committed
1909
{
1910
    SCROLLINFO vInfo, hInfo;
1911

1912
    vInfo.cbSize = hInfo.cbSize = sizeof(SCROLLINFO);
1913 1914 1915 1916 1917 1918
    vInfo.nMin   = vMin;
    vInfo.nMax   = vMax;
    vInfo.nPos   = vPos;
    hInfo.nMin   = hMin;
    hInfo.nMax   = hMax;
    hInfo.nPos   = hPos;
1919 1920
    vInfo.fMask  = hInfo.fMask = SIF_RANGE | SIF_POS;

1921 1922
    SCROLL_SetScrollInfo( hwnd, SB_VERT, &vInfo, TRUE );
    SCROLL_SetScrollInfo( hwnd, SB_HORZ, &hInfo, TRUE );
1923 1924

    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1925
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1926

Alexandre Julliard's avatar
Alexandre Julliard committed
1927 1928

/*************************************************************************
1929
 *           GetScrollRange   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1930
 *
1931 1932
 * Gets the range of the scroll bar.
 *
1933 1934 1935 1936 1937 1938
 * PARAMS
 *    hwnd    [I]  Handle of window with scrollbar(s)
 *    nBar    [I]  One of SB_HORZ, SB_VERT, or SB_CTL
 *    lpMin   [O]  Where to store minimum value
 *    lpMax   [O]  Where to store maximum value
 *
1939 1940
 * RETURNS
 *    TRUE if values is filled
Alexandre Julliard's avatar
Alexandre Julliard committed
1941
 */
1942
BOOL WINAPI GetScrollRange(HWND hwnd, INT nBar, LPINT lpMin, LPINT lpMax)
Alexandre Julliard's avatar
Alexandre Julliard committed
1943
{
1944
    TRACE("hwnd=%p nBar=%d lpMin=%p lpMax=%p\n", hwnd, nBar, lpMin, lpMax);
Alexandre Julliard's avatar
Alexandre Julliard committed
1945

1946 1947
    /* Refer SB_CTL requests to the window */
    if (nBar == SB_CTL)
1948
        SendMessageW(hwnd, SBM_GETRANGE, (WPARAM)lpMin, (LPARAM)lpMax);
1949
    else
1950 1951 1952
        SCROLL_GetScrollRange(hwnd, nBar, lpMin, lpMax);

    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1953 1954 1955 1956
}


/*************************************************************************
1957
 *           SCROLL_ShowScrollBar()
Alexandre Julliard's avatar
Alexandre Julliard committed
1958
 *
1959
 * Back-end for ShowScrollBar(). Returns FALSE if no action was taken.
Alexandre Julliard's avatar
Alexandre Julliard committed
1960
 */
1961
static BOOL SCROLL_ShowScrollBar( HWND hwnd, INT nBar, BOOL fShowH, BOOL fShowV )
Alexandre Julliard's avatar
Alexandre Julliard committed
1962
{
1963
    ULONG old_style, set_bits = 0, clear_bits = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1964

1965
    TRACE("hwnd=%p bar=%d horz=%d, vert=%d\n", hwnd, nBar, fShowH, fShowV );
Alexandre Julliard's avatar
Alexandre Julliard committed
1966

Alexandre Julliard's avatar
Alexandre Julliard committed
1967
    switch(nBar)
Alexandre Julliard's avatar
Alexandre Julliard committed
1968 1969
    {
    case SB_CTL:
1970
        ShowWindow( hwnd, fShowH ? SW_SHOW : SW_HIDE );
1971
        return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1972

1973
    case SB_BOTH:
Alexandre Julliard's avatar
Alexandre Julliard committed
1974
    case SB_HORZ:
1975 1976 1977 1978
        if (fShowH) set_bits |= WS_HSCROLL;
        else clear_bits |= WS_HSCROLL;
        if( nBar == SB_HORZ ) break;
        /* fall through */
Alexandre Julliard's avatar
Alexandre Julliard committed
1979
    case SB_VERT:
1980 1981
        if (fShowV) set_bits |= WS_VSCROLL;
        else clear_bits |= WS_VSCROLL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1982 1983 1984
        break;

    default:
1985
        return FALSE;  /* Nothing to do! */
Alexandre Julliard's avatar
Alexandre Julliard committed
1986
    }
1987

1988 1989
    old_style = WIN_SetStyle( hwnd, set_bits, clear_bits );
    if ((old_style & clear_bits) != 0 || (old_style & set_bits) != set_bits)
1990
    {
1991 1992
        /* frame has been changed, let the window redraw itself */
        SetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE
Alexandre Julliard's avatar
Alexandre Julliard committed
1993
                    | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
1994
        return TRUE;
1995
    }
1996
    return FALSE; /* no frame changes */
1997 1998 1999 2000
}


/*************************************************************************
2001
 *           ShowScrollBar   (USER32.@)
2002
 *
2003 2004
 * Shows or hides the scroll bar.
 *
2005 2006 2007 2008 2009
 * PARAMS
 *    hwnd    [I]  Handle of window with scrollbar(s)
 *    nBar    [I]  One of SB_HORZ, SB_VERT, or SB_CTL
 *    fShow   [I]  TRUE = show, FALSE = hide
 *
2010 2011 2012
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
2013
 */
2014
BOOL WINAPI ShowScrollBar(HWND hwnd, INT nBar, BOOL fShow)
2015
{
2016 2017 2018
    if ( !hwnd )
        return FALSE;

2019 2020
    SCROLL_ShowScrollBar( hwnd, nBar, (nBar == SB_VERT) ? 0 : fShow,
                                      (nBar == SB_HORZ) ? 0 : fShow );
Alexandre Julliard's avatar
Alexandre Julliard committed
2021 2022
    return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2023 2024 2025


/*************************************************************************
2026
 *           EnableScrollBar   (USER32.@)
2027 2028
 *
 * Enables or disables the scroll bars.
Alexandre Julliard's avatar
Alexandre Julliard committed
2029
 */
2030
BOOL WINAPI EnableScrollBar( HWND hwnd, UINT nBar, UINT flags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2031
{
2032
    BOOL bFineWithMe;
Alexandre Julliard's avatar
Alexandre Julliard committed
2033 2034
    SCROLLBAR_INFO *infoPtr;

Alexandre Julliard's avatar
Alexandre Julliard committed
2035
    flags &= ESB_DISABLE_BOTH;
2036 2037 2038

    if (nBar == SB_BOTH)
    {
2039
	if (!(infoPtr = SCROLL_GetInternalInfo( hwnd, SB_VERT, TRUE ))) return FALSE;
2040 2041 2042 2043 2044 2045 2046 2047 2048
	if (!(bFineWithMe = (infoPtr->flags == flags)) )
	{
	    infoPtr->flags = flags;
	    SCROLL_RefreshScrollBar( hwnd, SB_VERT, TRUE, TRUE );
	}
	nBar = SB_HORZ;
    }
    else
	bFineWithMe = TRUE;
2049

2050
    if (!(infoPtr = SCROLL_GetInternalInfo( hwnd, nBar, TRUE ))) return FALSE;
2051
    if (bFineWithMe && infoPtr->flags == flags) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2052
    infoPtr->flags = flags;
Alexandre Julliard's avatar
Alexandre Julliard committed
2053

2054 2055 2056
    if (nBar == SB_CTL && (flags == ESB_DISABLE_BOTH || flags == ESB_ENABLE_BOTH))
        EnableWindow(hwnd, flags == ESB_ENABLE_BOTH);

2057
    SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2058 2059
    return TRUE;
}