scroll.c 64.9 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 "config.h"

33 34
#include <stdarg.h>

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

43
WINE_DEFAULT_DEBUG_CHANNEL(scroll);
44

45
/* data for a single scroll bar */
46 47
typedef struct
{
48 49 50 51
    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) */
52
    UINT  flags;    /* EnableScrollBar flags */
53
} SCROLLBAR_INFO, *LPSCROLLBAR_INFO;
54

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

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

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
71 72 73 74
  /* 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
75
#define SCROLL_REPEAT_DELAY  50
Alexandre Julliard's avatar
Alexandre Julliard committed
76 77 78 79 80 81 82 83 84 85 86 87 88 89

  /* 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
90

91 92 93 94 95 96
 /* 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
97
 /* Thumb-tracking info */
98 99 100 101
static HWND SCROLL_TrackingWin = 0;
static INT  SCROLL_TrackingBar = 0;
static INT  SCROLL_TrackingPos = 0;
static INT  SCROLL_TrackingVal = 0;
102 103
 /* Hit test code of the last button-down event */
static enum SCROLL_HITTEST SCROLL_trackHitTest;
104
static BOOL SCROLL_trackVertical;
Alexandre Julliard's avatar
Alexandre Julliard committed
105

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

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


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

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

148

Alexandre Julliard's avatar
Alexandre Julliard committed
149
/***********************************************************************
150 151 152 153 154
 *           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
155
 */
156
static SCROLLBAR_INFO *SCROLL_GetInternalInfo( HWND hwnd, INT nBar, BOOL alloc )
Alexandre Julliard's avatar
Alexandre Julliard committed
157
{
158
    SCROLLBAR_INFO *infoPtr = NULL;
159
    WND *wndPtr = WIN_GetPtr( hwnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
160

161
    if (!wndPtr || wndPtr == WND_OTHER_PROCESS || wndPtr == WND_DESKTOP) return NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
162 163
    switch(nBar)
    {
164 165 166 167 168 169 170 171 172 173 174 175
        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
176 177
    }

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

182 183
        if (nBar != SB_HORZ && nBar != SB_VERT)
            WARN("Cannot initialize nBar=%d\n",nBar);
184
        else if ((winInfoPtr = HeapAlloc( GetProcessHeap(), 0, sizeof(WINSCROLLBAR_INFO) )))
Alexandre Julliard's avatar
Alexandre Julliard committed
185
        {
186
            /* Set default values */
187 188 189 190 191 192 193 194 195 196
            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
197 198
        }
    }
199
    WIN_ReleasePtr( wndPtr );
Alexandre Julliard's avatar
Alexandre Julliard committed
200
    return infoPtr;
Alexandre Julliard's avatar
Alexandre Julliard committed
201 202
}

Alexandre Julliard's avatar
Alexandre Julliard committed
203

Alexandre Julliard's avatar
Alexandre Julliard committed
204 205 206 207 208
/***********************************************************************
 *           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
209 210 211 212
 * '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
213 214
 * Return TRUE if the scrollbar is vertical, FALSE if horizontal.
 */
215
static BOOL SCROLL_GetScrollBarRect( HWND hwnd, INT nBar, RECT *lprect,
216 217
                                     INT *arrowSize, INT *thumbSize,
                                     INT *thumbPos )
Alexandre Julliard's avatar
Alexandre Julliard committed
218
{
219 220
    INT pixels;
    BOOL vertical;
221 222
    WND *wndPtr = WIN_GetPtr( hwnd );

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

    switch(nBar)
    {
      case SB_HORZ:
228 229 230
        WIN_GetRectangles( hwnd, COORDS_WINDOW, NULL, lprect );
        lprect->top = lprect->bottom;
        lprect->bottom += GetSystemMetrics(SM_CYHSCROLL);
231
	if(wndPtr->dwStyle & WS_VSCROLL)
Alexandre Julliard's avatar
Alexandre Julliard committed
232
	  lprect->right++;
Alexandre Julliard's avatar
Alexandre Julliard committed
233 234 235 236
        vertical = FALSE;
	break;

      case SB_VERT:
237
        WIN_GetRectangles( hwnd, COORDS_WINDOW, NULL, lprect );
238
        if((wndPtr->dwExStyle & WS_EX_LEFTSCROLLBAR) != 0)
239 240 241 242
        {
            lprect->right = lprect->left;
            lprect->left -= GetSystemMetrics(SM_CXVSCROLL);
        }
243
        else
244 245 246 247
        {
            lprect->left = lprect->right;
            lprect->right += GetSystemMetrics(SM_CXVSCROLL);
        }
248
	if(wndPtr->dwStyle & WS_HSCROLL)
Alexandre Julliard's avatar
Alexandre Julliard committed
249
	  lprect->bottom++;
Alexandre Julliard's avatar
Alexandre Julliard committed
250 251 252 253
        vertical = TRUE;
	break;

      case SB_CTL:
254
	GetClientRect( hwnd, lprect );
Alexandre Julliard's avatar
Alexandre Julliard committed
255 256
        vertical = ((wndPtr->dwStyle & SBS_VERT) != 0);
	break;
Alexandre Julliard's avatar
Alexandre Julliard committed
257 258

    default:
259
        WIN_ReleasePtr( wndPtr );
Alexandre Julliard's avatar
Alexandre Julliard committed
260
        return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
261 262 263 264 265
    }

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

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

286
        if (info->page)
Alexandre Julliard's avatar
Alexandre Julliard committed
287
        {
288
	    *thumbSize = MulDiv(pixels,info->page,(info->maxVal-info->minVal+1));
Alexandre Julliard's avatar
Alexandre Julliard committed
289 290
            if (*thumbSize < SCROLL_MIN_THUMB) *thumbSize = SCROLL_MIN_THUMB;
        }
291
        else *thumbSize = GetSystemMetrics(SM_CXVSCROLL);
Alexandre Julliard's avatar
Alexandre Julliard committed
292

Alexandre Julliard's avatar
Alexandre Julliard committed
293
        if (((pixels -= *thumbSize ) < 0) ||
Alexandre Julliard's avatar
Alexandre Julliard committed
294 295 296 297 298
            ((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
299
        else
Alexandre Julliard's avatar
Alexandre Julliard committed
300
        {
301 302
            INT max = info->maxVal - max( info->page-1, 0 );
            if (info->minVal >= max)
Alexandre Julliard's avatar
Alexandre Julliard committed
303
                *thumbPos = *arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
Alexandre Julliard's avatar
Alexandre Julliard committed
304
            else
Alexandre Julliard's avatar
Alexandre Julliard committed
305
                *thumbPos = *arrowSize - SCROLL_ARROW_THUMB_OVERLAP
306
		  + MulDiv(pixels, (info->curVal-info->minVal),(max - info->minVal));
Alexandre Julliard's avatar
Alexandre Julliard committed
307
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
308
    }
309
    WIN_ReleasePtr( wndPtr );
Alexandre Julliard's avatar
Alexandre Julliard committed
310
    return vertical;
Alexandre Julliard's avatar
Alexandre Julliard committed
311 312 313
}


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

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

330
    if (infoPtr->page)
Alexandre Julliard's avatar
Alexandre Julliard committed
331
    {
332
        thumbSize = MulDiv(pixels,infoPtr->page,(infoPtr->maxVal-infoPtr->minVal+1));
Alexandre Julliard's avatar
Alexandre Julliard committed
333 334
        if (thumbSize < SCROLL_MIN_THUMB) thumbSize = SCROLL_MIN_THUMB;
    }
335
    else thumbSize = GetSystemMetrics(SM_CXVSCROLL);
Alexandre Julliard's avatar
Alexandre Julliard committed
336

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

339
    pos = max( 0, pos - (GetSystemMetrics(SM_CXVSCROLL) - SCROLL_ARROW_THUMB_OVERLAP) );
Alexandre Julliard's avatar
Alexandre Julliard committed
340
    if (pos > pixels) pos = pixels;
Alexandre Julliard's avatar
Alexandre Julliard committed
341

342 343 344 345 346 347
    if (!infoPtr->page)
        range = infoPtr->maxVal - infoPtr->minVal;
    else
        range = infoPtr->maxVal - infoPtr->minVal - infoPtr->page + 1;

    return infoPtr->minVal + MulDiv(pos, range, pixels);
Alexandre Julliard's avatar
Alexandre Julliard committed
348 349
}

350 351 352
/***********************************************************************
 *           SCROLL_PtInRectEx
 */
353
static BOOL SCROLL_PtInRectEx( LPRECT lpRect, POINT pt, BOOL vertical )
354
{
355
    RECT rect = *lpRect;
356
    int scrollbarWidth;
357

358 359
    /* Pad hit rect to allow mouse to be dragged outside of scrollbar and
     * still be considered in the scrollbar. */
360 361
    if (vertical)
    {
362 363 364 365 366
        scrollbarWidth = lpRect->right - lpRect->left;
        rect.left -= scrollbarWidth*8;
        rect.right += scrollbarWidth*8;
        rect.top -= scrollbarWidth*2;
        rect.bottom += scrollbarWidth*2;
367 368 369
    }
    else
    {
370 371 372 373 374
        scrollbarWidth = lpRect->bottom - lpRect->top;
        rect.left -= scrollbarWidth*2;
        rect.right += scrollbarWidth*2;
        rect.top -= scrollbarWidth*8;
        rect.bottom += scrollbarWidth*8;
375
    }
376
    return PtInRect( &rect, pt );
377 378 379 380 381
}

/***********************************************************************
 *           SCROLL_ClipPos
 */
382
static POINT SCROLL_ClipPos( LPRECT lpRect, POINT pt )
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
{
    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
399

Alexandre Julliard's avatar
Alexandre Julliard committed
400 401 402 403 404
/***********************************************************************
 *           SCROLL_HitTest
 *
 * Scroll-bar hit testing (don't confuse this with WM_NCHITTEST!).
 */
405 406
static enum SCROLL_HITTEST SCROLL_HitTest( HWND hwnd, INT nBar,
                                           POINT pt, BOOL bDragging )
Alexandre Julliard's avatar
Alexandre Julliard committed
407
{
408 409
    INT arrowSize, thumbSize, thumbPos;
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
410

411
    BOOL vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
Alexandre Julliard's avatar
Alexandre Julliard committed
412
                                           &arrowSize, &thumbSize, &thumbPos );
413 414

    if ( (bDragging && !SCROLL_PtInRectEx( &rect, pt, vertical )) ||
415
	 (!PtInRect( &rect, pt )) ) return SCROLL_NOWHERE;
Alexandre Julliard's avatar
Alexandre Julliard committed
416 417 418

    if (vertical)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
419
        if (pt.y < rect.top + arrowSize) return SCROLL_TOP_ARROW;
Alexandre Julliard's avatar
Alexandre Julliard committed
420 421 422
        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
423
        if (pt.y < thumbPos) return SCROLL_TOP_RECT;
Alexandre Julliard's avatar
Alexandre Julliard committed
424
        if (pt.y >= thumbPos + thumbSize) return SCROLL_BOTTOM_RECT;
Alexandre Julliard's avatar
Alexandre Julliard committed
425 426 427
    }
    else  /* horizontal */
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
428
        if (pt.x < rect.left + arrowSize) return SCROLL_TOP_ARROW;
Alexandre Julliard's avatar
Alexandre Julliard committed
429 430 431
        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
432
        if (pt.x < thumbPos) return SCROLL_TOP_RECT;
Alexandre Julliard's avatar
Alexandre Julliard committed
433
        if (pt.x >= thumbPos + thumbSize) return SCROLL_BOTTOM_RECT;
Alexandre Julliard's avatar
Alexandre Julliard committed
434
    }
435
    return SCROLL_THUMB;
Alexandre Julliard's avatar
Alexandre Julliard committed
436 437 438
}


Alexandre Julliard's avatar
Alexandre Julliard committed
439 440 441 442 443
/***********************************************************************
 *           SCROLL_DrawArrows
 *
 * Draw the scroll bar arrows.
 */
444 445 446
static void SCROLL_DrawArrows( HDC hdc, SCROLLBAR_INFO *infoPtr,
                               RECT *rect, INT arrowSize, BOOL vertical,
                               BOOL top_pressed, BOOL bottom_pressed )
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
{
  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) );
}

473 474
static void SCROLL_DrawMovingThumb( HDC hdc, RECT *rect, BOOL vertical,
				    INT arrowSize, INT thumbSize )
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
{
  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 );
494

495 496 497
  SCROLL_MovingThumb = !SCROLL_MovingThumb;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
498 499 500 501 502
/***********************************************************************
 *           SCROLL_DrawInterior
 *
 * Draw the scroll bar interior (everything except the arrows).
 */
503
static void SCROLL_DrawInterior_9x( HWND hwnd, HDC hdc, INT nBar,
504 505 506 507 508 509 510 511 512 513 514 515 516 517
				    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)
518
    {
519
      hBrush = (HBRUSH)SendMessageW( GetParent(hwnd), WM_CTLCOLORSCROLLBAR,
520 521 522 523 524 525 526
				     (WPARAM)hdc,(LPARAM)hwnd);
    }
    else
    {
      hBrush = DEFWND_ControlColor( hdc, CTLCOLOR_SCROLLBAR );
    }

527
    hSavePen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
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 583 584 585 586 587 588 589 590 591
    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 );
}


592
static void SCROLL_DrawInterior( HWND hwnd, HDC hdc, INT nBar,
593 594 595 596
                                 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
597
{
598
    RECT r;
599 600
    HPEN hSavePen;
    HBRUSH hSaveBrush,hBrush;
601 602 603 604 605 606
    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
607 608 609

      /* Select the correct brush and pen */

610 611 612 613 614
    /* 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) {
615
        hBrush = (HBRUSH)SendMessageW( GetParent(hwnd), WM_CTLCOLORSCROLLBAR,
616 617 618
                                       (WPARAM)hdc,(LPARAM)hwnd);
    } else {
        hBrush = DEFWND_ControlColor( hdc, CTLCOLOR_SCROLLBAR );
Alexandre Julliard's avatar
Alexandre Julliard committed
619
    }
620
    hSavePen = SelectObject( hdc, SYSCOLOR_GetPen(COLOR_WINDOWFRAME) );
621
    hSaveBrush = SelectObject( hdc, hBrush );
Alexandre Julliard's avatar
Alexandre Julliard committed
622

Alexandre Julliard's avatar
Alexandre Julliard committed
623 624 625 626 627
      /* Calculate the scroll rectangle */

    r = *rect;
    if (vertical)
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
628 629
        r.top    += arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
        r.bottom -= (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
Alexandre Julliard's avatar
Alexandre Julliard committed
630 631 632
    }
    else
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
633 634
        r.left  += arrowSize - SCROLL_ARROW_THUMB_OVERLAP;
        r.right -= (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
Alexandre Julliard's avatar
Alexandre Julliard committed
635 636 637 638 639 640 641 642
    }

      /* Draw the scroll bar frame */

      /* Draw the scroll rectangles and thumb */

    if (!thumbPos)  /* No thumb to draw */
    {
643
        PatBlt( hdc, r.left, r.top, r.right - r.left, r.bottom - r.top, PATCOPY );
644 645 646 647

        /* cleanup and return */
        SelectObject( hdc, hSavePen );
        SelectObject( hdc, hSaveBrush );
Alexandre Julliard's avatar
Alexandre Julliard committed
648 649 650 651 652
        return;
    }

    if (vertical)
    {
653 654 655
        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
656
        r.top += thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
657 658 659
        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
660
        r.bottom = r.top + thumbSize;
Alexandre Julliard's avatar
Alexandre Julliard committed
661 662 663
    }
    else  /* horizontal */
    {
664 665 666
        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
667
        r.left += thumbPos - (arrowSize - SCROLL_ARROW_THUMB_OVERLAP);
668 669
        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
670
        r.right = r.left + thumbSize;
Alexandre Julliard's avatar
Alexandre Julliard committed
671 672 673 674
    }

      /* Draw the thumb */

675
    SelectObject( hdc, GetSysColorBrush(COLOR_BTNFACE) );
676
    Rectangle( hdc, r.left+1, r.top+1, r.right-1, r.bottom-1 );
677
    DrawEdge( hdc, &r, EDGE_RAISED, BF_RECT );
678 679

    if (Save_SCROLL_MovingThumb &&
Alexandre Julliard's avatar
Alexandre Julliard committed
680 681 682
        (SCROLL_TrackingWin == hwnd) &&
        (SCROLL_TrackingBar == nBar))
        SCROLL_DrawMovingThumb( hdc, rect, vertical, arrowSize, thumbSize );
683 684 685 686

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

Alexandre Julliard's avatar
Alexandre Julliard committed
689

Alexandre Julliard's avatar
Alexandre Julliard committed
690 691 692 693 694
/***********************************************************************
 *           SCROLL_DrawScrollBar
 *
 * Redraw the whole scrollbar.
 */
695
void SCROLL_DrawScrollBar( HWND hwnd, HDC hdc, INT nBar,
696
			   BOOL arrows, BOOL interior )
Alexandre Julliard's avatar
Alexandre Julliard committed
697
{
698 699 700
    INT arrowSize, thumbSize, thumbPos;
    RECT rect;
    BOOL vertical;
701
    SCROLLBAR_INFO *infoPtr = SCROLL_GetInternalInfo( hwnd, nBar, TRUE );
702
    BOOL Save_SCROLL_MovingThumb = SCROLL_MovingThumb;
703 704 705
    DWORD style = GetWindowLongW( hwnd, GWL_STYLE );

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

707 708 709 710
    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
711

Alexandre Julliard's avatar
Alexandre Julliard committed
712
    vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
Alexandre Julliard's avatar
Alexandre Julliard committed
713
                                        &arrowSize, &thumbSize, &thumbPos );
Alexandre Julliard's avatar
Alexandre Julliard committed
714

715
    /* do not draw if the scrollbar rectangle is empty */
716
    if(IsRectEmpty(&rect)) return;
717

718 719 720 721 722
    if (Save_SCROLL_MovingThumb &&
        (SCROLL_TrackingWin == hwnd) &&
        (SCROLL_TrackingBar == nBar))
        SCROLL_DrawMovingThumb( hdc, &rect, vertical, arrowSize, thumbSize );

Alexandre Julliard's avatar
Alexandre Julliard committed
723 724
      /* Draw the arrows */

725 726
    if (arrows && arrowSize)
    {
727
	if( vertical == SCROLL_trackVertical && GetCapture() == hwnd )
728 729 730 731
	    SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
			       (SCROLL_trackHitTest == SCROLL_TOP_ARROW),
			       (SCROLL_trackHitTest == SCROLL_BOTTOM_ARROW) );
	else
732
	    SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
733 734 735 736
							       FALSE, FALSE );
    }
    if( interior )
	SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbSize,
Alexandre Julliard's avatar
Alexandre Julliard committed
737
                         thumbPos, infoPtr->flags, vertical, FALSE, FALSE );
738 739 740 741 742 743

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

Andreas Mohr's avatar
Andreas Mohr committed
744
    /* if scroll bar has focus, reposition the caret */
745
    if(hwnd==GetFocus() && (nBar==SB_CTL))
746 747 748 749 750 751 752 753 754 755
    {
        if (!vertical)
        {
            SetCaretPos(thumbPos+1, rect.top+1);
        }
        else
        {
            SetCaretPos(rect.top+1, thumbPos+1);
        }
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
756 757
}

758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
/***********************************************************************
 *           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
774

Alexandre Julliard's avatar
Alexandre Julliard committed
775 776 777 778 779 780
/***********************************************************************
 *           SCROLL_RefreshScrollBar
 *
 * Repaint the scroll bar interior after a SetScrollRange() or
 * SetScrollPos() call.
 */
781
static void SCROLL_RefreshScrollBar( HWND hwnd, INT nBar,
782
				     BOOL arrows, BOOL interior )
Alexandre Julliard's avatar
Alexandre Julliard committed
783
{
784
    HDC hdc = GetDCEx( hwnd, 0,
Alexandre Julliard's avatar
Alexandre Julliard committed
785
                           DCX_CACHE | ((nBar == SB_CTL) ? 0 : DCX_WINDOW) );
Alexandre Julliard's avatar
Alexandre Julliard committed
786
    if (!hdc) return;
787 788

    SCROLL_DrawScrollBar( hwnd, hdc, nBar, arrows, interior );
789
    ReleaseDC( hwnd, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
790 791 792
}


Alexandre Julliard's avatar
Alexandre Julliard committed
793 794 795
/***********************************************************************
 *           SCROLL_HandleKbdEvent
 *
796
 * Handle a keyboard event (only for SB_CTL scrollbars with focus).
797 798 799 800 801
 *
 * 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
802
 */
803
static void SCROLL_HandleKbdEvent(HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
804
{
805
    TRACE("hwnd=%p wParam=%ld lParam=%ld\n", hwnd, wParam, lParam);
806 807 808 809

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

Alexandre Julliard's avatar
Alexandre Julliard committed
811 812
    switch(wParam)
    {
813 814 815 816 817 818
    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;
819 820
    case VK_LEFT:  wParam = SB_LINEUP; break;
    case VK_RIGHT: wParam = SB_LINEDOWN; break;
821
    default: return;
Alexandre Julliard's avatar
Alexandre Julliard committed
822
    }
823
    SendMessageW(GetParent(hwnd),
824
        ((GetWindowLongW( hwnd, GWL_STYLE ) & SBS_VERT) ?
825
            WM_VSCROLL : WM_HSCROLL), wParam, (LPARAM)hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
826 827 828
}


Alexandre Julliard's avatar
Alexandre Julliard committed
829 830 831 832 833 834
/***********************************************************************
 *           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
835
 */
836
static void SCROLL_HandleScrollEvent( HWND hwnd, INT nBar, UINT msg, POINT pt)
Alexandre Julliard's avatar
Alexandre Julliard committed
837
{
Alexandre Julliard's avatar
Alexandre Julliard committed
838
      /* Previous mouse position for timer events */
839
    static POINT prevPt;
Alexandre Julliard's avatar
Alexandre Julliard committed
840
      /* Thumb position when tracking started. */
841
    static UINT trackThumbPos;
Alexandre Julliard's avatar
Alexandre Julliard committed
842
      /* Position in the scroll-bar of the last button-down event. */
843
    static INT lastClickPos;
Alexandre Julliard's avatar
Alexandre Julliard committed
844
      /* Position in the scroll-bar of the last mouse event. */
845
    static INT lastMousePos;
Alexandre Julliard's avatar
Alexandre Julliard committed
846 847

    enum SCROLL_HITTEST hittest;
848 849 850 851 852
    HWND hwndOwner, hwndCtl;
    BOOL vertical;
    INT arrowSize, thumbSize, thumbPos;
    RECT rect;
    HDC hdc;
Alexandre Julliard's avatar
Alexandre Julliard committed
853

854
    SCROLLBAR_INFO *infoPtr = SCROLL_GetInternalInfo( hwnd, nBar, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
855
    if (!infoPtr) return;
856
    if ((SCROLL_trackHitTest == SCROLL_NOWHERE) && (msg != WM_LBUTTONDOWN))
857
		  return;
Alexandre Julliard's avatar
Alexandre Julliard committed
858

859
    if (nBar == SB_CTL && (GetWindowLongW( hwnd, GWL_STYLE ) & (SBS_SIZEGRIP | SBS_SIZEBOX)))
860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
    {
        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;
    }

885
    hdc = GetDCEx( hwnd, 0, DCX_CACHE | ((nBar == SB_CTL) ? 0 : DCX_WINDOW));
Alexandre Julliard's avatar
Alexandre Julliard committed
886
    vertical = SCROLL_GetScrollBarRect( hwnd, nBar, &rect,
Alexandre Julliard's avatar
Alexandre Julliard committed
887
                                        &arrowSize, &thumbSize, &thumbPos );
888
    hwndOwner = (nBar == SB_CTL) ? GetParent(hwnd) : hwnd;
Alexandre Julliard's avatar
Alexandre Julliard committed
889 890 891 892 893
    hwndCtl   = (nBar == SB_CTL) ? hwnd : 0;

    switch(msg)
    {
      case WM_LBUTTONDOWN:  /* Initialise mouse tracking */
Andreas Mohr's avatar
Andreas Mohr committed
894
          HideCaret(hwnd);  /* hide caret while holding down LBUTTON */
895
          SCROLL_trackVertical = vertical;
896
          SCROLL_trackHitTest  = hittest = SCROLL_HitTest( hwnd, nBar, pt, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
897 898 899 900
          lastClickPos  = vertical ? (pt.y - rect.top) : (pt.x - rect.left);
          lastMousePos  = lastClickPos;
          trackThumbPos = thumbPos;
          prevPt = pt;
901
          if (nBar == SB_CTL && (GetWindowLongW(hwnd, GWL_STYLE) & WS_TABSTOP)) SetFocus( hwnd );
902
          SetCapture( hwnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
903 904 905
          break;

      case WM_MOUSEMOVE:
906
          hittest = SCROLL_HitTest( hwnd, nBar, pt, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
907 908 909 910 911 912
          prevPt = pt;
          break;

      case WM_LBUTTONUP:
          hittest = SCROLL_NOWHERE;
          ReleaseCapture();
913 914
          /* if scrollbar has focus, show back caret */
          if (hwnd==GetFocus()) ShowCaret(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
915 916 917 918
          break;

      case WM_SYSTIMER:
          pt = prevPt;
919
          hittest = SCROLL_HitTest( hwnd, nBar, pt, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
920 921 922 923 924 925
          break;

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

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

929
    switch(SCROLL_trackHitTest)
Alexandre Julliard's avatar
Alexandre Julliard committed
930 931 932 933 934 935
    {
    case SCROLL_NOWHERE:  /* No tracking in progress */
        break;

    case SCROLL_TOP_ARROW:
        SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
936 937
                           (hittest == SCROLL_trackHitTest), FALSE );
        if (hittest == SCROLL_trackHitTest)
Alexandre Julliard's avatar
Alexandre Julliard committed
938 939
        {
            if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
Alexandre Julliard's avatar
Alexandre Julliard committed
940
            {
941
                SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
942
                                SB_LINEUP, (LPARAM)hwndCtl );
943
	    }
944

945
	    SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
946
                            SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
947
        }
948
        else KillSystemTimer( hwnd, SCROLL_TIMER );
Alexandre Julliard's avatar
Alexandre Julliard committed
949 950 951
        break;

    case SCROLL_TOP_RECT:
Alexandre Julliard's avatar
Alexandre Julliard committed
952 953
        SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbSize,
                             thumbPos, infoPtr->flags, vertical,
954 955
                             (hittest == SCROLL_trackHitTest), FALSE );
        if (hittest == SCROLL_trackHitTest)
Alexandre Julliard's avatar
Alexandre Julliard committed
956 957
        {
            if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
Alexandre Julliard's avatar
Alexandre Julliard committed
958
            {
959
                SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
960
                                SB_PAGEUP, (LPARAM)hwndCtl );
Alexandre Julliard's avatar
Alexandre Julliard committed
961
            }
962
            SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
963
                              SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
964
        }
965
        else KillSystemTimer( hwnd, SCROLL_TIMER );
Alexandre Julliard's avatar
Alexandre Julliard committed
966 967 968 969
        break;

    case SCROLL_THUMB:
        if (msg == WM_LBUTTONDOWN)
Alexandre Julliard's avatar
Alexandre Julliard committed
970
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
971 972 973
            SCROLL_TrackingWin = hwnd;
            SCROLL_TrackingBar = nBar;
            SCROLL_TrackingPos = trackThumbPos + lastMousePos - lastClickPos;
974 975 976
            SCROLL_TrackingVal = SCROLL_GetThumbVal( infoPtr, &rect,
                                                        vertical,
                                                        SCROLL_TrackingPos );
977 978
	    if (!SCROLL_MovingThumb)
		SCROLL_DrawMovingThumb(hdc, &rect, vertical, arrowSize, thumbSize);
Alexandre Julliard's avatar
Alexandre Julliard committed
979
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
980
        else if (msg == WM_LBUTTONUP)
Alexandre Julliard's avatar
Alexandre Julliard committed
981
        {
982 983
	    if (SCROLL_MovingThumb)
		SCROLL_DrawMovingThumb(hdc, &rect, vertical, arrowSize, thumbSize);
984

Alexandre Julliard's avatar
Alexandre Julliard committed
985 986 987
            SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbSize,
                                 thumbPos, infoPtr->flags, vertical,
                                 FALSE, FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
988
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
989 990
        else  /* WM_MOUSEMOVE */
        {
991
            INT pos;
Alexandre Julliard's avatar
Alexandre Julliard committed
992

993 994 995 996 997 998
            if (!SCROLL_PtInRectEx( &rect, pt, vertical )) pos = lastClickPos;
            else
	    {
		pt = SCROLL_ClipPos( &rect, pt );
		pos = vertical ? (pt.y - rect.top) : (pt.x - rect.left);
	    }
999
            if ( (pos != lastMousePos) || (!SCROLL_MovingThumb) )
Alexandre Julliard's avatar
Alexandre Julliard committed
1000
            {
1001 1002
		if (SCROLL_MovingThumb)
		    SCROLL_DrawMovingThumb( hdc, &rect, vertical,
Alexandre Julliard's avatar
Alexandre Julliard committed
1003
                                        arrowSize, thumbSize );
Alexandre Julliard's avatar
Alexandre Julliard committed
1004
                lastMousePos = pos;
Alexandre Julliard's avatar
Alexandre Julliard committed
1005 1006 1007 1008
                SCROLL_TrackingPos = trackThumbPos + pos - lastClickPos;
                SCROLL_TrackingVal = SCROLL_GetThumbVal( infoPtr, &rect,
                                                         vertical,
                                                         SCROLL_TrackingPos );
1009
                SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
Alexandre Julliard's avatar
Alexandre Julliard committed
1010
                                MAKEWPARAM( SB_THUMBTRACK, SCROLL_TrackingVal),
1011
                                (LPARAM)hwndCtl );
1012 1013
		if (!SCROLL_MovingThumb)
		    SCROLL_DrawMovingThumb( hdc, &rect, vertical,
Alexandre Julliard's avatar
Alexandre Julliard committed
1014
                                        arrowSize, thumbSize );
Alexandre Julliard's avatar
Alexandre Julliard committed
1015
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
1016
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1017
        break;
1018

Alexandre Julliard's avatar
Alexandre Julliard committed
1019
    case SCROLL_BOTTOM_RECT:
Alexandre Julliard's avatar
Alexandre Julliard committed
1020 1021
        SCROLL_DrawInterior( hwnd, hdc, nBar, &rect, arrowSize, thumbSize,
                             thumbPos, infoPtr->flags, vertical,
1022 1023
                             FALSE, (hittest == SCROLL_trackHitTest) );
        if (hittest == SCROLL_trackHitTest)
Alexandre Julliard's avatar
Alexandre Julliard committed
1024 1025
        {
            if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
Alexandre Julliard's avatar
Alexandre Julliard committed
1026
            {
1027
                SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
1028
                                SB_PAGEDOWN, (LPARAM)hwndCtl );
Alexandre Julliard's avatar
Alexandre Julliard committed
1029
            }
1030
            SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
1031
                              SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
1032
        }
1033
        else KillSystemTimer( hwnd, SCROLL_TIMER );
Alexandre Julliard's avatar
Alexandre Julliard committed
1034
        break;
1035

Alexandre Julliard's avatar
Alexandre Julliard committed
1036 1037
    case SCROLL_BOTTOM_ARROW:
        SCROLL_DrawArrows( hdc, infoPtr, &rect, arrowSize, vertical,
1038 1039
                           FALSE, (hittest == SCROLL_trackHitTest) );
        if (hittest == SCROLL_trackHitTest)
Alexandre Julliard's avatar
Alexandre Julliard committed
1040 1041
        {
            if ((msg == WM_LBUTTONDOWN) || (msg == WM_SYSTIMER))
Alexandre Julliard's avatar
Alexandre Julliard committed
1042
            {
1043
                SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
1044
                                SB_LINEDOWN, (LPARAM)hwndCtl );
1045 1046 1047
	    }

	    SetSystemTimer( hwnd, SCROLL_TIMER, (msg == WM_LBUTTONDOWN) ?
1048
                            SCROLL_FIRST_DELAY : SCROLL_REPEAT_DELAY, NULL );
Alexandre Julliard's avatar
Alexandre Julliard committed
1049
        }
1050
        else KillSystemTimer( hwnd, SCROLL_TIMER );
Alexandre Julliard's avatar
Alexandre Julliard committed
1051 1052 1053
        break;
    }

1054 1055 1056 1057 1058 1059 1060
    if (msg == WM_LBUTTONDOWN)
    {

        if (hittest == SCROLL_THUMB)
        {
            UINT val = SCROLL_GetThumbVal( infoPtr, &rect, vertical,
                                 trackThumbPos + lastMousePos - lastClickPos );
1061
            SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
1062 1063 1064 1065
                            MAKEWPARAM( SB_THUMBTRACK, val ), (LPARAM)hwndCtl );
        }
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
1066 1067
    if (msg == WM_LBUTTONUP)
    {
1068 1069 1070 1071
	hittest = SCROLL_trackHitTest;
	SCROLL_trackHitTest = SCROLL_NOWHERE;  /* Terminate tracking */

        if (hittest == SCROLL_THUMB)
Alexandre Julliard's avatar
Alexandre Julliard committed
1072
        {
1073
            UINT val = SCROLL_GetThumbVal( infoPtr, &rect, vertical,
Alexandre Julliard's avatar
Alexandre Julliard committed
1074
                                 trackThumbPos + lastMousePos - lastClickPos );
1075
            SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
1076
                            MAKEWPARAM( SB_THUMBPOSITION, val ), (LPARAM)hwndCtl );
Alexandre Julliard's avatar
Alexandre Julliard committed
1077
        }
1078
        /* SB_ENDSCROLL doesn't report thumb position */
1079
        SendMessageW( hwndOwner, vertical ? WM_VSCROLL : WM_HSCROLL,
1080
                          SB_ENDSCROLL, (LPARAM)hwndCtl );
1081 1082 1083

        /* Terminate tracking */
        SCROLL_TrackingWin = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1084 1085
    }

1086
    ReleaseDC( hwnd, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
1087 1088 1089
}


1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102
/***********************************************************************
 *           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)
    {
1103 1104
        RECT rect;
        WIN_GetRectangles( hwnd, COORDS_CLIENT, &rect, NULL );
1105
        ScreenToClient( hwnd, &pt );
1106 1107
        pt.x -= rect.left;
        pt.y -= rect.top;
1108 1109 1110 1111 1112 1113 1114 1115
    }

    SCROLL_HandleScrollEvent( hwnd, scrollbar, WM_LBUTTONDOWN, pt );

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


1138 1139 1140
/***********************************************************************
 *           SCROLL_CreateScrollBar
 *
1141 1142 1143 1144 1145
 * Create a scroll bar
 *
 * PARAMS
 *    hwnd     [I] Handle of window with scrollbar(s)
 *    lpCreate [I] The style and place of the scroll bar
1146
 */
1147
static void SCROLL_CreateScrollBar(HWND hwnd, LPCREATESTRUCTW lpCreate)
1148
{
1149
    LPSCROLLBAR_INFO info = SCROLL_GetInternalInfo(hwnd, SB_CTL, TRUE);
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159
    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");
    }

1160

1161
    if (lpCreate->style & (SBS_SIZEGRIP | SBS_SIZEBOX))
1162
    {
1163 1164 1165 1166 1167 1168 1169 1170
        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 );
1171 1172
    }
    else if (lpCreate->style & SBS_VERT)
1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193
    {
        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 );
    }
1194 1195 1196
}


1197 1198 1199
/*************************************************************************
 *           SCROLL_GetScrollInfo
 *
1200 1201 1202 1203 1204 1205
 * 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
1206 1207 1208
 *
 * RETURNS
 *    FALSE if requested field not filled (f.i. scroll bar does not exist)
1209
 */
1210
static BOOL SCROLL_GetScrollInfo(HWND hwnd, INT nBar, LPSCROLLINFO info)
1211 1212 1213 1214
{
    LPSCROLLBAR_INFO infoPtr;

    /* handle invalid data structure */
1215
    if (!SCROLL_ScrollInfoValid(info)
1216
        || !(infoPtr = SCROLL_GetInternalInfo(hwnd, nBar, FALSE)))
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229
            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;
    }

1230 1231 1232 1233
    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);

1234 1235 1236 1237
    return (info->fMask & SIF_ALL) != 0;
}


1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257
/*************************************************************************
 *           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;
1258
    RECT rect;
1259 1260 1261 1262 1263 1264 1265 1266 1267 1268

    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 */
1269
    if (info->cbSize != sizeof(*info))
1270 1271
        return FALSE;

1272 1273
    SCROLL_GetScrollBarRect(hwnd, nBar, &info->rcScrollBar, &nDummy,
                            &info->dxyLineButton, &info->xyThumbTop);
1274 1275 1276
    /* rcScrollBar needs to be in screen coordinates */
    GetWindowRect(hwnd, &rect);
    OffsetRect(&info->rcScrollBar, rect.left, rect.top);
1277 1278 1279 1280

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

    infoPtr = SCROLL_GetInternalInfo(hwnd, nBar, TRUE);
1281 1282 1283
    if (!infoPtr)
        return FALSE;

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 1332 1333 1334 1335 1336 1337
    /* 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;
}


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


/*************************************************************************
 *           SCROLL_GetScrollRange
 *
 *  Internal helper for the API function
 *
1359 1360 1361 1362 1363 1364
 * 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
 *
1365 1366 1367
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
1368
 */
1369
static BOOL SCROLL_GetScrollRange(HWND hwnd, INT nBar, LPINT lpMin, LPINT lpMax)
1370
{
1371
    LPSCROLLBAR_INFO infoPtr = SCROLL_GetInternalInfo(hwnd, nBar, FALSE);
1372 1373 1374 1375

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

1376
    return TRUE;
1377 1378 1379
}


1380 1381 1382
/*************************************************************************
 *           SCROLL_SetScrollRange
 *
1383 1384 1385 1386 1387 1388
 * 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
 *
1389
 */
1390
static BOOL SCROLL_SetScrollRange(HWND hwnd, INT nBar, INT minVal, INT maxVal)
1391
{
1392
    LPSCROLLBAR_INFO infoPtr = SCROLL_GetInternalInfo(hwnd, nBar, FALSE);
1393 1394 1395 1396 1397 1398 1399 1400

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

    if (infoPtr)
    {
        infoPtr->minVal = minVal;
        infoPtr->maxVal = maxVal;
    }
1401
    return TRUE;
1402 1403
}

1404

Alexandre Julliard's avatar
Alexandre Julliard committed
1405
/***********************************************************************
1406
 *           ScrollBarWndProc_common
Alexandre Julliard's avatar
Alexandre Julliard committed
1407
 */
1408
LRESULT ScrollBarWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
1409
{
1410 1411
    if (!IsWindow( hwnd )) return 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
1412 1413 1414
    switch(message)
    {
    case WM_CREATE:
1415 1416
        SCROLL_CreateScrollBar(hwnd, (LPCREATESTRUCTW)lParam);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1417 1418 1419 1420

    case WM_ENABLE:
        {
	    SCROLLBAR_INFO *infoPtr;
1421
	    if ((infoPtr = SCROLL_GetInternalInfo( hwnd, SB_CTL, FALSE )))
Alexandre Julliard's avatar
Alexandre Julliard committed
1422 1423 1424 1425 1426 1427 1428
	    {
		infoPtr->flags = wParam ? ESB_ENABLE_BOTH : ESB_DISABLE_BOTH;
		SCROLL_RefreshScrollBar(hwnd, SB_CTL, TRUE, TRUE);
	    }
	}
	return 0;

1429
    case WM_LBUTTONDBLCLK:
Alexandre Julliard's avatar
Alexandre Julliard committed
1430
    case WM_LBUTTONDOWN:
1431 1432 1433 1434 1435 1436 1437
        if (GetWindowLongW( hwnd, GWL_STYLE ) & SBS_SIZEGRIP)
        {
            SendMessageW( GetParent(hwnd), WM_SYSCOMMAND,
                          SC_SIZE + ((GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) ?
                                     WMSZ_BOTTOMLEFT : WMSZ_BOTTOMRIGHT), lParam );
        }
        else
1438 1439
        {
	    POINT pt;
1440 1441
	    pt.x = (short)LOWORD(lParam);
	    pt.y = (short)HIWORD(lParam);
1442
            SCROLL_TrackScrollBar( hwnd, SB_CTL, pt );
1443 1444
	}
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1445 1446 1447
    case WM_LBUTTONUP:
    case WM_MOUSEMOVE:
    case WM_SYSTIMER:
Alexandre Julliard's avatar
Alexandre Julliard committed
1448
        {
1449
            POINT pt;
1450 1451
            pt.x = (short)LOWORD(lParam);
            pt.y = (short)HIWORD(lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1452 1453
            SCROLL_HandleScrollEvent( hwnd, SB_CTL, message, pt );
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1454 1455 1456
        break;

    case WM_KEYDOWN:
1457
        SCROLL_HandleKbdEvent(hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1458 1459
        break;

1460 1461 1462 1463 1464 1465
    case WM_KEYUP:
        ShowCaret(hwnd);
        break;

    case WM_SETFOCUS:
        {
Andreas Mohr's avatar
Andreas Mohr committed
1466
            /* Create a caret when a ScrollBar get focus */
1467 1468
            RECT rect;
            int arrowSize, thumbSize, thumbPos, vertical;
1469 1470 1471
            vertical = SCROLL_GetScrollBarRect( hwnd, SB_CTL, &rect,
                                                &arrowSize, &thumbSize, &thumbPos );
            if (!vertical)
1472
            {
1473
                CreateCaret(hwnd, (HBITMAP)1, thumbSize-2, rect.bottom-rect.top-2);
1474
                SetCaretPos(thumbPos+1, rect.top+1);
1475
            }
1476 1477
            else
            {
1478
                CreateCaret(hwnd, (HBITMAP)1, rect.right-rect.left-2,thumbSize-2);
1479 1480 1481
                SetCaretPos(rect.top+1, thumbPos+1);
            }
            ShowCaret(hwnd);
1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504
        }
        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
1505
    case WM_ERASEBKGND:
Alexandre Julliard's avatar
Alexandre Julliard committed
1506 1507 1508 1509
         return 1;

    case WM_GETDLGCODE:
         return DLGC_WANTARROWS; /* Windows returns this value */
Alexandre Julliard's avatar
Alexandre Julliard committed
1510 1511 1512

    case WM_PAINT:
        {
1513
            PAINTSTRUCT ps;
1514
            HDC hdc = wParam ? (HDC)wParam : BeginPaint(hwnd, &ps);
1515 1516 1517 1518 1519
            if (GetWindowLongW( hwnd, GWL_STYLE ) & SBS_SIZEGRIP)
            {
                SCROLL_DrawSizeGrip( hwnd, hdc);
            }
            else if (GetWindowLongW( hwnd, GWL_STYLE ) & SBS_SIZEBOX)
1520 1521 1522 1523 1524
            {
                RECT rc;
                GetClientRect( hwnd, &rc );
                FillRect( hdc, &rc, GetSysColorBrush(COLOR_SCROLLBAR) );
            }
1525 1526
            else
                SCROLL_DrawScrollBar( hwnd, hdc, SB_CTL, TRUE, TRUE );
1527
            if (!wParam) EndPaint(hwnd, &ps);
Alexandre Julliard's avatar
Alexandre Julliard committed
1528 1529 1530
        }
        break;

1531 1532 1533 1534 1535 1536 1537 1538
    case WM_SETCURSOR:
        if (GetWindowLongW( hwnd, GWL_STYLE ) & SBS_SIZEGRIP)
        {
            ULONG_PTR cursor = (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) ? IDC_SIZENESW : IDC_SIZENWSE;
            return (LRESULT)SetCursor( LoadCursorA( 0, (LPSTR)cursor ));
        }
        return DefWindowProcW( hwnd, message, wParam, lParam );

1539 1540
    case SBM_SETPOS:
        return SetScrollPos( hwnd, SB_CTL, wParam, (BOOL)lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1541

1542
    case SBM_GETPOS:
1543
       return SCROLL_GetScrollPos(hwnd, SB_CTL);
Alexandre Julliard's avatar
Alexandre Julliard committed
1544

1545
    case SBM_SETRANGEREDRAW:
1546
    case SBM_SETRANGE:
1547
        {
1548
            INT oldPos = SCROLL_GetScrollPos( hwnd, SB_CTL );
1549 1550 1551
            SCROLL_SetScrollRange( hwnd, SB_CTL, wParam, lParam );
            if (message == SBM_SETRANGEREDRAW)
                SCROLL_RefreshScrollBar( hwnd, SB_CTL, TRUE, TRUE );
1552
            if (oldPos != SCROLL_GetScrollPos( hwnd, SB_CTL )) return oldPos;
1553 1554
        }
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1555

1556
    case SBM_GETRANGE:
1557
        return SCROLL_GetScrollRange(hwnd, SB_CTL, (LPINT)wParam, (LPINT)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1558

1559 1560
    case SBM_ENABLE_ARROWS:
        return EnableScrollBar( hwnd, SB_CTL, wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1561

1562
    case SBM_SETSCROLLINFO:
1563
        return SCROLL_SetScrollInfo( hwnd, SB_CTL, (SCROLLINFO *)lParam, wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1564

1565
    case SBM_GETSCROLLINFO:
1566
        return SCROLL_GetScrollInfo(hwnd, SB_CTL, (SCROLLINFO *)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1567

1568 1569 1570
    case SBM_GETSCROLLBARINFO:
        return SCROLL_GetScrollBarInfo(hwnd, OBJID_CLIENT, (SCROLLBARINFO *)lParam);

Alexandre Julliard's avatar
Alexandre Julliard committed
1571 1572 1573 1574 1575 1576 1577
    case 0x00e5:
    case 0x00e7:
    case 0x00e8:
    case 0x00ec:
    case 0x00ed:
    case 0x00ee:
    case 0x00ef:
1578
        ERR("unknown Win32 msg %04x wp=%08lx lp=%08lx\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
1579
		    message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1580
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1581

Alexandre Julliard's avatar
Alexandre Julliard committed
1582
    default:
Alexandre Julliard's avatar
Alexandre Julliard committed
1583
        if (message >= WM_USER)
1584
            WARN("unknown msg %04x wp=%04lx lp=%08lx\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
1585
			 message, wParam, lParam );
1586 1587 1588 1589
        if (unicode)
            return DefWindowProcW( hwnd, message, wParam, lParam );
        else
            return DefWindowProcA( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1590 1591 1592 1593
    }
    return 0;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1594

Alexandre Julliard's avatar
Alexandre Julliard committed
1595
/*************************************************************************
1596
 *           SetScrollInfo   (USER32.@)
1597
 *
1598
 * SetScrollInfo can be used to set the position, upper bound,
Alexandre Julliard's avatar
Alexandre Julliard committed
1599 1600
 * lower bound, and page size of a scrollbar control.
 *
1601 1602 1603 1604 1605 1606
 * 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
1607 1608 1609 1610 1611 1612 1613 1614
 * 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
1615
 */
1616
INT WINAPI DECLSPEC_HOTPATCH SetScrollInfo(HWND hwnd, INT nBar, const SCROLLINFO *info, BOOL bRedraw)
Alexandre Julliard's avatar
Alexandre Julliard committed
1617
{
1618
    TRACE("hwnd=%p nBar=%d info=%p, bRedraw=%d\n", hwnd, nBar, info, bRedraw);
1619

1620 1621 1622
    /* Refer SB_CTL requests to the window */
    if (nBar == SB_CTL)
        return SendMessageW(hwnd, SBM_SETSCROLLINFO, bRedraw, (LPARAM)info);
1623
    else
1624
        return SCROLL_SetScrollInfo( hwnd, nBar, info, bRedraw );
1625 1626
}

1627
static INT SCROLL_SetScrollInfo( HWND hwnd, INT nBar, LPCSCROLLINFO info, BOOL bRedraw )
1628
{
1629
    /* Update the scrollbar state and set action flags according to
1630 1631
     * what has to be done graphics wise. */

Alexandre Julliard's avatar
Alexandre Julliard committed
1632
    SCROLLBAR_INFO *infoPtr;
1633
    UINT new_flags;
1634
    INT action = 0;
1635

1636 1637 1638 1639
    /* handle invalid data structure */
    if (!SCROLL_ScrollInfoValid(info)
        || !(infoPtr = SCROLL_GetInternalInfo(hwnd, nBar, TRUE)))
            return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1640

1641 1642
    if (TRACE_ON(scroll))
    {
1643
        TRACE("hwnd=%p bar=%d", hwnd, nBar);
1644 1645 1646 1647
        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");
1648 1649
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
1650 1651 1652 1653
    /* Set the page size */

    if (info->fMask & SIF_PAGE)
    {
1654
	if( infoPtr->page != info->nPage )
1655
	{
1656
            infoPtr->page = info->nPage;
1657
            action |= SA_SSI_REFRESH;
1658
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1659 1660 1661 1662 1663 1664
    }

    /* Set the scroll pos */

    if (info->fMask & SIF_POS)
    {
1665
	if( infoPtr->curVal != info->nPos )
1666
	{
1667
	    infoPtr->curVal = info->nPos;
1668
            action |= SA_SSI_REFRESH;
1669
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1670 1671 1672 1673 1674 1675 1676 1677
    }

    /* Set the scroll range */

    if (info->fMask & SIF_RANGE)
    {
        /* Invalid range -> range is set to (0,0) */
        if ((info->nMin > info->nMax) ||
1678
            ((UINT)(info->nMax - info->nMin) >= 0x80000000))
Alexandre Julliard's avatar
Alexandre Julliard committed
1679
        {
1680
            action |= SA_SSI_REFRESH;
1681 1682
            infoPtr->minVal = 0;
            infoPtr->maxVal = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1683 1684 1685
        }
        else
        {
1686 1687
	    if( infoPtr->minVal != info->nMin ||
		infoPtr->maxVal != info->nMax )
1688
	    {
1689
                action |= SA_SSI_REFRESH;
1690 1691
                infoPtr->minVal = info->nMin;
                infoPtr->maxVal = info->nMax;
1692
	    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1693 1694 1695 1696
        }
    }

    /* Make sure the page size is valid */
1697 1698 1699
    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
1700 1701 1702

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

1703 1704 1705 1706
    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
1707

1708
    TRACE("    new values: page=%d pos=%d min=%d max=%d\n",
1709 1710
		 infoPtr->page, infoPtr->curVal,
		 infoPtr->minVal, infoPtr->maxVal );
Alexandre Julliard's avatar
Alexandre Julliard committed
1711

1712
    /* don't change the scrollbar state if SetScrollInfo
1713 1714 1715 1716
     * is just called with SIF_DISABLENOSCROLL
     */
    if(!(info->fMask & SIF_ALL)) goto done;

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1719
    if (info->fMask & (SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL))
Alexandre Julliard's avatar
Alexandre Julliard committed
1720
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1721
        new_flags = infoPtr->flags;
1722
        if (infoPtr->minVal >= infoPtr->maxVal - max( infoPtr->page-1, 0 ))
Alexandre Julliard's avatar
Alexandre Julliard committed
1723
        {
Alexandre Julliard's avatar
Alexandre Julliard committed
1724 1725
            /* Hide or disable scroll-bar */
            if (info->fMask & SIF_DISABLENOSCROLL)
1726
	    {
Alexandre Julliard's avatar
Alexandre Julliard committed
1727
                new_flags = ESB_DISABLE_BOTH;
1728
                action |= SA_SSI_REFRESH;
1729
	    }
1730
            else if ((nBar != SB_CTL) && (action & SA_SSI_REFRESH))
1731
	    {
1732
                action = SA_SSI_HIDE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1733 1734
            }
        }
1735 1736
        else  /* Show and enable scroll-bar only if no page only changed. */
        if (info->fMask != SIF_PAGE)
Alexandre Julliard's avatar
Alexandre Julliard committed
1737
        {
1738 1739
	    new_flags = ESB_ENABLE_BOTH;
            if ((nBar != SB_CTL) && ( (action & SA_SSI_REFRESH) ))
1740
                action |= SA_SSI_SHOW;
Alexandre Julliard's avatar
Alexandre Julliard committed
1741 1742
        }

1743
        if (infoPtr->flags != new_flags) /* check arrow flags */
Alexandre Julliard's avatar
Alexandre Julliard committed
1744 1745
        {
            infoPtr->flags = new_flags;
1746
            action |= SA_SSI_REPAINT_ARROWS;
Alexandre Julliard's avatar
Alexandre Julliard committed
1747
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1748 1749
    }

1750
done:
1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763
    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
1764

1765
    /* Return current position */
1766
    return infoPtr->curVal;
Alexandre Julliard's avatar
Alexandre Julliard committed
1767 1768 1769 1770
}


/*************************************************************************
1771
 *           GetScrollInfo   (USER32.@)
1772
 *
1773
 * GetScrollInfo can be used to retrieve the position, upper bound,
Alexandre Julliard's avatar
Alexandre Julliard committed
1774 1775
 * lower bound, and page size of a scrollbar control.
 *
1776 1777 1778 1779 1780
 * 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
 *
1781 1782
 * RETURNS
 *  TRUE if SCROLLINFO is filled
1783 1784
 *  ( if nBar is SB_CTL, GetScrollInfo returns TRUE even if nothing
 *  is filled)
Alexandre Julliard's avatar
Alexandre Julliard committed
1785
 */
1786
BOOL WINAPI DECLSPEC_HOTPATCH GetScrollInfo(HWND hwnd, INT nBar, LPSCROLLINFO info)
Alexandre Julliard's avatar
Alexandre Julliard committed
1787
{
1788
    TRACE("hwnd=%p nBar=%d info=%p\n", hwnd, nBar, info);
Alexandre Julliard's avatar
Alexandre Julliard committed
1789

1790 1791
    /* Refer SB_CTL requests to the window */
    if (nBar == SB_CTL)
1792
    {
1793
        SendMessageW(hwnd, SBM_GETSCROLLINFO, 0, (LPARAM)info);
1794 1795 1796
        return TRUE;
    }
    return SCROLL_GetScrollInfo(hwnd, nBar, info);
Alexandre Julliard's avatar
Alexandre Julliard committed
1797 1798 1799
}


1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815
/*************************************************************************
 *           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)
{
1816
    TRACE("hwnd=%p idObject=%d info=%p\n", hwnd, idObject, info);
1817 1818 1819

    /* Refer OBJID_CLIENT requests to the window */
    if (idObject == OBJID_CLIENT)
1820
        return SendMessageW(hwnd, SBM_GETSCROLLBARINFO, 0, (LPARAM)info);
1821 1822 1823 1824 1825
    else
        return SCROLL_GetScrollBarInfo(hwnd, idObject, info);
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1826
/*************************************************************************
1827
 *           SetScrollPos   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1828
 *
1829 1830
 * Sets the current position of the scroll thumb.
 *
1831 1832 1833 1834 1835 1836
 * 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
1837 1838 1839 1840 1841 1842 1843
 * 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
1844
 */
1845
INT WINAPI DECLSPEC_HOTPATCH SetScrollPos( HWND hwnd, INT nBar, INT nPos, BOOL bRedraw)
Alexandre Julliard's avatar
Alexandre Julliard committed
1846
{
Alexandre Julliard's avatar
Alexandre Julliard committed
1847
    SCROLLINFO info;
Alexandre Julliard's avatar
Alexandre Julliard committed
1848
    SCROLLBAR_INFO *infoPtr;
1849
    INT oldPos;
Alexandre Julliard's avatar
Alexandre Julliard committed
1850

1851
    if (!(infoPtr = SCROLL_GetInternalInfo( hwnd, nBar, FALSE ))) return 0;
1852
    oldPos      = infoPtr->curVal;
Alexandre Julliard's avatar
Alexandre Julliard committed
1853 1854 1855
    info.cbSize = sizeof(info);
    info.nPos   = nPos;
    info.fMask  = SIF_POS;
1856
    SetScrollInfo( hwnd, nBar, &info, bRedraw );
Alexandre Julliard's avatar
Alexandre Julliard committed
1857 1858 1859 1860
    return oldPos;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1861
/*************************************************************************
1862
 *           GetScrollPos   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1863
 *
1864 1865
 * Gets the current position of the scroll thumb.
 *
1866 1867 1868 1869
 * 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
1870 1871
 * RETURNS
 *    Success: Current position
1872
 *    Failure: 0
Alexandre Julliard's avatar
Alexandre Julliard committed
1873 1874
 *
 * REMARKS
1875
 *    There is ambiguity when 0 is returned.  Use GetLastError
Alexandre Julliard's avatar
Alexandre Julliard committed
1876
 *    to make sure there was an error (and to know which one).
Alexandre Julliard's avatar
Alexandre Julliard committed
1877
 */
1878
INT WINAPI DECLSPEC_HOTPATCH GetScrollPos(HWND hwnd, INT nBar)
Alexandre Julliard's avatar
Alexandre Julliard committed
1879
{
1880
    TRACE("hwnd=%p nBar=%d\n", hwnd, nBar);
Alexandre Julliard's avatar
Alexandre Julliard committed
1881

1882 1883
    /* Refer SB_CTL requests to the window */
    if (nBar == SB_CTL)
1884
        return SendMessageW(hwnd, SBM_GETPOS, 0, 0);
1885 1886
    else
        return SCROLL_GetScrollPos(hwnd, nBar);
Alexandre Julliard's avatar
Alexandre Julliard committed
1887 1888 1889 1890
}


/*************************************************************************
1891
 *           SetScrollRange   (USER32.@)
1892 1893
 * 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
1894
 *
1895 1896
 * Sets the range of the scroll bar.
 *
1897 1898 1899 1900 1901 1902 1903
 * 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?
 *
1904 1905 1906
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
Alexandre Julliard's avatar
Alexandre Julliard committed
1907
 */
1908
BOOL WINAPI DECLSPEC_HOTPATCH SetScrollRange(HWND hwnd, INT nBar, INT minVal, INT maxVal, BOOL bRedraw)
Alexandre Julliard's avatar
Alexandre Julliard committed
1909
{
1910 1911 1912
    SCROLLINFO info;
 
    TRACE("hwnd=%p nBar=%d min=%d max=%d, bRedraw=%d\n", hwnd, nBar, minVal, maxVal, bRedraw);
1913

1914 1915 1916 1917 1918 1919
    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
1920
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1921

Alexandre Julliard's avatar
Alexandre Julliard committed
1922 1923

/*************************************************************************
1924
 *           GetScrollRange   (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1925
 *
1926 1927
 * Gets the range of the scroll bar.
 *
1928 1929 1930 1931 1932 1933
 * 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
 *
1934 1935
 * RETURNS
 *    TRUE if values is filled
Alexandre Julliard's avatar
Alexandre Julliard committed
1936
 */
1937
BOOL WINAPI DECLSPEC_HOTPATCH GetScrollRange(HWND hwnd, INT nBar, LPINT lpMin, LPINT lpMax)
Alexandre Julliard's avatar
Alexandre Julliard committed
1938
{
1939
    TRACE("hwnd=%p nBar=%d lpMin=%p lpMax=%p\n", hwnd, nBar, lpMin, lpMax);
Alexandre Julliard's avatar
Alexandre Julliard committed
1940

1941 1942
    /* Refer SB_CTL requests to the window */
    if (nBar == SB_CTL)
1943
        SendMessageW(hwnd, SBM_GETRANGE, (WPARAM)lpMin, (LPARAM)lpMax);
1944
    else
1945 1946 1947
        SCROLL_GetScrollRange(hwnd, nBar, lpMin, lpMax);

    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1948 1949 1950 1951
}


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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1962
    switch(nBar)
Alexandre Julliard's avatar
Alexandre Julliard committed
1963 1964
    {
    case SB_CTL:
1965
        ShowWindow( hwnd, fShowH ? SW_SHOW : SW_HIDE );
1966
        return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1967

1968
    case SB_BOTH:
Alexandre Julliard's avatar
Alexandre Julliard committed
1969
    case SB_HORZ:
1970 1971 1972 1973
        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
1974
    case SB_VERT:
1975 1976
        if (fShowV) set_bits |= WS_VSCROLL;
        else clear_bits |= WS_VSCROLL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1977 1978 1979
        break;

    default:
1980
        return FALSE;  /* Nothing to do! */
Alexandre Julliard's avatar
Alexandre Julliard committed
1981
    }
1982

1983 1984
    old_style = WIN_SetStyle( hwnd, set_bits, clear_bits );
    if ((old_style & clear_bits) != 0 || (old_style & set_bits) != set_bits)
1985
    {
1986 1987
        /* 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
1988
                    | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED );
1989
        return TRUE;
1990
    }
1991
    return FALSE; /* no frame changes */
1992 1993 1994 1995
}


/*************************************************************************
1996
 *           ShowScrollBar   (USER32.@)
1997
 *
1998 1999
 * Shows or hides the scroll bar.
 *
2000 2001 2002 2003 2004
 * 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
 *
2005 2006 2007
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
2008
 */
2009
BOOL WINAPI DECLSPEC_HOTPATCH ShowScrollBar(HWND hwnd, INT nBar, BOOL fShow)
2010
{
2011 2012 2013
    if ( !hwnd )
        return FALSE;

2014 2015
    SCROLL_ShowScrollBar( hwnd, nBar, (nBar == SB_VERT) ? 0 : fShow,
                                      (nBar == SB_HORZ) ? 0 : fShow );
Alexandre Julliard's avatar
Alexandre Julliard committed
2016 2017
    return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
2018 2019 2020


/*************************************************************************
2021
 *           EnableScrollBar   (USER32.@)
2022 2023
 *
 * Enables or disables the scroll bars.
Alexandre Julliard's avatar
Alexandre Julliard committed
2024
 */
2025
BOOL WINAPI DECLSPEC_HOTPATCH EnableScrollBar( HWND hwnd, UINT nBar, UINT flags )
Alexandre Julliard's avatar
Alexandre Julliard committed
2026
{
2027
    BOOL bFineWithMe;
Alexandre Julliard's avatar
Alexandre Julliard committed
2028 2029
    SCROLLBAR_INFO *infoPtr;

Alexandre Julliard's avatar
Alexandre Julliard committed
2030
    flags &= ESB_DISABLE_BOTH;
2031 2032 2033

    if (nBar == SB_BOTH)
    {
2034
	if (!(infoPtr = SCROLL_GetInternalInfo( hwnd, SB_VERT, TRUE ))) return FALSE;
2035 2036 2037 2038 2039 2040 2041 2042 2043
	if (!(bFineWithMe = (infoPtr->flags == flags)) )
	{
	    infoPtr->flags = flags;
	    SCROLL_RefreshScrollBar( hwnd, SB_VERT, TRUE, TRUE );
	}
	nBar = SB_HORZ;
    }
    else
	bFineWithMe = TRUE;
2044

2045
    if (!(infoPtr = SCROLL_GetInternalInfo( hwnd, nBar, TRUE ))) return FALSE;
2046
    if (bFineWithMe && infoPtr->flags == flags) return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
2047
    infoPtr->flags = flags;
Alexandre Julliard's avatar
Alexandre Julliard committed
2048

2049 2050 2051
    if (nBar == SB_CTL && (flags == ESB_DISABLE_BOTH || flags == ESB_ENABLE_BOTH))
        EnableWindow(hwnd, flags == ESB_ENABLE_BOTH);

2052
    SCROLL_RefreshScrollBar( hwnd, nBar, TRUE, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
2053 2054
    return TRUE;
}