pager.c 39.5 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3
/*
 * Pager control
 *
4
 * Copyright 1998, 1999 Eric Kohl
Alexandre Julliard's avatar
Alexandre Julliard committed
5
 *
6 7 8 9 10 11 12 13 14 15 16 17
 * 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
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
20 21
 * NOTES
 *
22 23 24 25 26 27 28
 * This code was audited for completeness against the documented features
 * of Comctl32.dll version 6.0 on Sep. 18, 2004, by Robert Shearman.
 * 
 * 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
 * TODO:
30 31 32
 *    Implement repetitive button press.
 *    Adjust arrow size relative to size of button.
 *    Allow border size changes.
33 34 35 36
 *    Styles:
 *      PGS_DRAGNDROP
 *    Notifications:
 *      PGN_HOTITEMCHANGE
37 38
 *    Messages:
 *      WM_PRINT and/or WM_PRINTCLIENT
39 40 41 42 43 44 45 46 47 48 49 50 51 52
 *
 * TESTING:
 *    Tested primarily with the controlspy Pager application.
 *       Susan Farley (susan@codeweavers.com)
 *
 * IMPLEMENTATION NOTES:
 *    This control uses WM_NCPAINT instead of WM_PAINT to paint itself
 *    as we need to scroll a child window. In order to do this we move 
 *    the child window in the control's client area, using the clipping
 *    region that is automatically set around the client area. As the 
 *    entire client area now consists of the child window, we must 
 *    allocate space (WM_NCCALCSIZE) for the buttons and draw them as 
 *    a non-client area (WM_NCPAINT).
 *       Robert Shearman <rob@codeweavers.com>
Alexandre Julliard's avatar
Alexandre Julliard committed
53 54
 */

55
#include <stdarg.h>
56
#include <string.h>
57
#include "windef.h"
58
#include "winbase.h"
59 60 61
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
62
#include "commctrl.h"
63
#include "comctl32.h"
64
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
65

66
WINE_DEFAULT_DEBUG_CHANNEL(pager);
67

68 69
typedef struct
{
70
    HWND   hwndSelf;   /* handle of the control wnd */
71
    HWND   hwndChild;  /* handle of the contained wnd */
72
    HWND   hwndNotify; /* handle of the parent wnd */
73
    DWORD  dwStyle;    /* styles for this control */
74 75 76 77 78
    COLORREF clrBk;    /* background color */
    INT    nBorder;    /* border size for the control */
    INT    nButtonSize;/* size of the pager btns */
    INT    nPos;       /* scroll position */
    INT    nWidth;     /* from child wnd's response to PGN_CALCSIZE */
79
    INT    nHeight;    /* from child wnd's response to PGN_CALCSIZE */
80
    BOOL   bForward;   /* forward WM_MOUSEMOVE msgs to the contained wnd */
81
    BOOL   bCapture;   /* we have captured the mouse  */
82 83
    INT    TLbtnState; /* state of top or left btn */
    INT    BRbtnState; /* state of bottom or right btn */
84
    INT    direction;  /* direction of the scroll, (e.g. PGF_SCROLLUP) */
85
} PAGER_INFO;
Alexandre Julliard's avatar
Alexandre Julliard committed
86

87 88 89
#define MIN_ARROW_WIDTH  8
#define MIN_ARROW_HEIGHT 5

90 91 92 93
#define TIMERID1         1
#define TIMERID2         2
#define INITIAL_DELAY    500
#define REPEAT_DELAY     50
94

95
static void
96
PAGER_GetButtonRects(const PAGER_INFO* infoPtr, RECT* prcTopLeft, RECT* prcBottomRight, BOOL bClientCoords)
97 98
{
    RECT rcWindow;
99
    GetWindowRect (infoPtr->hwndSelf, &rcWindow);
100 101

    if (bClientCoords)
102
        MapWindowPoints( 0, infoPtr->hwndSelf, (POINT *)&rcWindow, 2 );
103 104 105 106
    else
        OffsetRect(&rcWindow, -rcWindow.left, -rcWindow.top);

    *prcTopLeft = *prcBottomRight = rcWindow;
107
    if (infoPtr->dwStyle & PGS_HORZ)
108 109 110 111 112 113 114 115 116 117 118
    {
        prcTopLeft->right = prcTopLeft->left + infoPtr->nButtonSize;
        prcBottomRight->left = prcBottomRight->right - infoPtr->nButtonSize;
    }
    else
    {
        prcTopLeft->bottom = prcTopLeft->top + infoPtr->nButtonSize;
        prcBottomRight->top = prcBottomRight->bottom - infoPtr->nButtonSize;
    }
}

119
/* the horizontal arrows are:
120 121 122 123 124 125 126 127
 *
 * 01234    01234
 * 1  *      *
 * 2 **      **
 * 3***      ***
 * 4***      ***
 * 5 **      **
 * 6  *      *
128
 * 7
129 130 131 132 133 134
 *
 */
static void
PAGER_DrawHorzArrow (HDC hdc, RECT r, INT colorRef, BOOL left)
{
    INT x, y, w, h;
135 136
    HPEN hPen, hOldPen;

137 138 139 140 141
    w = r.right - r.left + 1;
    h = r.bottom - r.top + 1;
    if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
        return;  /* refuse to draw partial arrow */

142 143
    if (!(hPen = CreatePen( PS_SOLID, 1, GetSysColor( colorRef )))) return;
    hOldPen = SelectObject ( hdc, hPen );
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
    if (left)
    {
        x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 3;
        y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
        MoveToEx (hdc, x, y, NULL);
        LineTo (hdc, x--, y+5); y++;
        MoveToEx (hdc, x, y, NULL);
        LineTo (hdc, x--, y+3); y++;
        MoveToEx (hdc, x, y, NULL);
        LineTo (hdc, x, y+1);
    }
    else
    {
        x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
        y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
        MoveToEx (hdc, x, y, NULL);
        LineTo (hdc, x++, y+5); y++;
        MoveToEx (hdc, x, y, NULL);
        LineTo (hdc, x++, y+3); y++;
        MoveToEx (hdc, x, y, NULL);
        LineTo (hdc, x, y+1);
    }

    SelectObject( hdc, hOldPen );
168
    DeleteObject( hPen );
169 170
}

171
/* the vertical arrows are:
172 173
 *
 * 01234567    01234567
174
 * 1******        **
175 176 177 178 179 180 181 182 183
 * 2 ****        ****
 * 3  **        ******
 * 4
 *
 */
static void
PAGER_DrawVertArrow (HDC hdc, RECT r, INT colorRef, BOOL up)
{
    INT x, y, w, h;
184 185
    HPEN hPen, hOldPen;

186 187 188 189 190
    w = r.right - r.left + 1;
    h = r.bottom - r.top + 1;
    if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
        return;  /* refuse to draw partial arrow */

191 192
    if (!(hPen = CreatePen( PS_SOLID, 1, GetSysColor( colorRef )))) return;
    hOldPen = SelectObject ( hdc, hPen );
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
    if (up)
    {
        x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
        y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 3;
        MoveToEx (hdc, x, y, NULL);
        LineTo (hdc, x+5, y--); x++;
        MoveToEx (hdc, x, y, NULL);
        LineTo (hdc, x+3, y--); x++;
        MoveToEx (hdc, x, y, NULL);
        LineTo (hdc, x+1, y);
    }
    else
    {
        x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
        y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
        MoveToEx (hdc, x, y, NULL);
        LineTo (hdc, x+5, y++); x++;
        MoveToEx (hdc, x, y, NULL);
        LineTo (hdc, x+3, y++); x++;
        MoveToEx (hdc, x, y, NULL);
        LineTo (hdc, x+1, y);
    }

    SelectObject( hdc, hOldPen );
217
    DeleteObject( hPen );
218 219 220 221 222 223 224 225 226
}

static void
PAGER_DrawButton(HDC hdc, COLORREF clrBk, RECT arrowRect,
                 BOOL horz, BOOL topLeft, INT btnState)
{
    HBRUSH   hBrush, hOldBrush;
    RECT     rc = arrowRect;

227 228 229
    TRACE("arrowRect = %s, btnState = %d\n", wine_dbgstr_rect(&arrowRect), btnState);

    if (btnState == PGF_INVISIBLE)
230 231 232
        return;

    if ((rc.right - rc.left <= 0) || (rc.bottom - rc.top <= 0))
233
        return;
234 235

    hBrush = CreateSolidBrush(clrBk);
236
    hOldBrush = SelectObject(hdc, hBrush);
237 238 239

    FillRect(hdc, &rc, hBrush);

240
    if (btnState == PGF_HOT)
241
    {
242
       DrawEdge( hdc, &rc, BDR_RAISEDINNER, BF_RECT);
243 244 245 246 247
       if (horz)
           PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
       else
           PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
    }
248
    else if (btnState == PGF_NORMAL)
249 250 251 252 253 254 255
    {
       DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
       if (horz)
           PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
       else
           PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
    }
256
    else if (btnState == PGF_DEPRESSED)
257 258 259 260 261 262 263
    {
       DrawEdge( hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
       if (horz)
           PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
       else
           PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
    }
264
    else if (btnState == PGF_GRAYED)
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
    {
       DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
       if (horz)
       {
           PAGER_DrawHorzArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
           rc.left++, rc.top++; rc.right++, rc.bottom++;
           PAGER_DrawHorzArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
       }
       else
       {
           PAGER_DrawVertArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
           rc.left++, rc.top++; rc.right++, rc.bottom++;
           PAGER_DrawVertArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
       }
    }

    SelectObject( hdc, hOldBrush );
    DeleteObject(hBrush);
}

/* << PAGER_GetDropTarget >> */
Alexandre Julliard's avatar
Alexandre Julliard committed
286

Patrik Stridvall's avatar
Patrik Stridvall committed
287
static inline LRESULT
288
PAGER_ForwardMouse (PAGER_INFO* infoPtr, BOOL bFwd)
Alexandre Julliard's avatar
Alexandre Julliard committed
289
{
290
    TRACE("[%p]\n", infoPtr->hwndSelf);
Alexandre Julliard's avatar
Alexandre Julliard committed
291

292
    infoPtr->bForward = bFwd;
Alexandre Julliard's avatar
Alexandre Julliard committed
293 294 295 296

    return 0;
}

Patrik Stridvall's avatar
Patrik Stridvall committed
297
static inline LRESULT
298
PAGER_GetButtonState (const PAGER_INFO* infoPtr, INT btn)
Alexandre Julliard's avatar
Alexandre Julliard committed
299
{
300
    LRESULT btnState = PGF_INVISIBLE;
301
    TRACE("[%p]\n", infoPtr->hwndSelf);
Alexandre Julliard's avatar
Alexandre Julliard committed
302

303 304 305 306 307 308
    if (btn == PGB_TOPORLEFT)
        btnState = infoPtr->TLbtnState;
    else if (btn == PGB_BOTTOMORRIGHT)
        btnState = infoPtr->BRbtnState;

    return btnState;
Alexandre Julliard's avatar
Alexandre Julliard committed
309 310 311
}


312
static inline INT
313
PAGER_GetPos(const PAGER_INFO *infoPtr)
Alexandre Julliard's avatar
Alexandre Julliard committed
314
{
315 316
    TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nPos);
    return infoPtr->nPos;
317
}
Alexandre Julliard's avatar
Alexandre Julliard committed
318

319
static inline INT
320
PAGER_GetButtonSize(const PAGER_INFO *infoPtr)
321
{
322 323
    TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nButtonSize);
    return infoPtr->nButtonSize;
Alexandre Julliard's avatar
Alexandre Julliard committed
324 325
}

326
static inline INT
327
PAGER_GetBorder(const PAGER_INFO *infoPtr)
328
{
329 330
    TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nBorder);
    return infoPtr->nBorder;
331
}
Alexandre Julliard's avatar
Alexandre Julliard committed
332

333
static inline COLORREF
334
PAGER_GetBkColor(const PAGER_INFO *infoPtr)
Alexandre Julliard's avatar
Alexandre Julliard committed
335
{
336
    TRACE("[%p] returns %06x\n", infoPtr->hwndSelf, infoPtr->clrBk);
337
    return infoPtr->clrBk;
338
}
Alexandre Julliard's avatar
Alexandre Julliard committed
339

340
static void
341
PAGER_CalcSize (const PAGER_INFO *infoPtr, INT* size, BOOL getWidth)
342 343 344
{
    NMPGCALCSIZE nmpgcs;
    ZeroMemory (&nmpgcs, sizeof (NMPGCALCSIZE));
345 346
    nmpgcs.hdr.hwndFrom = infoPtr->hwndSelf;
    nmpgcs.hdr.idFrom   = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
347 348 349 350
    nmpgcs.hdr.code = PGN_CALCSIZE;
    nmpgcs.dwFlag = getWidth ? PGF_CALCWIDTH : PGF_CALCHEIGHT;
    nmpgcs.iWidth = getWidth ? *size : 0;
    nmpgcs.iHeight = getWidth ? 0 : *size;
351
    SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, nmpgcs.hdr.idFrom, (LPARAM)&nmpgcs);
352 353 354

    *size = getWidth ? nmpgcs.iWidth : nmpgcs.iHeight;

355
    TRACE("[%p] PGN_CALCSIZE returns %s=%d\n", infoPtr->hwndSelf,
356
                  getWidth ? "width" : "height", *size);
Alexandre Julliard's avatar
Alexandre Julliard committed
357 358
}

359
static void
360
PAGER_PositionChildWnd(PAGER_INFO* infoPtr)
361 362 363
{
    if (infoPtr->hwndChild)
    {
364
        RECT rcClient;
365 366 367 368 369 370
        int nPos = infoPtr->nPos;

        /* compensate for a grayed btn, which will soon become invisible */
        if (infoPtr->TLbtnState == PGF_GRAYED)
            nPos += infoPtr->nButtonSize;

371
        GetClientRect(infoPtr->hwndSelf, &rcClient);
372

373
        if (infoPtr->dwStyle & PGS_HORZ)
374
        {
375 376 377 378
            int wndSize = max(0, rcClient.right - rcClient.left);
            if (infoPtr->nWidth < wndSize)
                infoPtr->nWidth = wndSize;

379
            TRACE("[%p] SWP %dx%d at (%d,%d)\n", infoPtr->hwndSelf,
380 381 382 383 384
                         infoPtr->nWidth, infoPtr->nHeight,
                         -nPos, 0);
            SetWindowPos(infoPtr->hwndChild, 0,
                         -nPos, 0,
                         infoPtr->nWidth, infoPtr->nHeight,
385
                         SWP_NOZORDER);
386 387 388
        }
        else
        {
389 390 391 392
            int wndSize = max(0, rcClient.bottom - rcClient.top);
            if (infoPtr->nHeight < wndSize)
                infoPtr->nHeight = wndSize;

393
            TRACE("[%p] SWP %dx%d at (%d,%d)\n", infoPtr->hwndSelf,
394
                         infoPtr->nWidth, infoPtr->nHeight,
395 396 397 398 399 400 401
                         0, -nPos);
            SetWindowPos(infoPtr->hwndChild, 0,
                         0, -nPos,
                         infoPtr->nWidth, infoPtr->nHeight,
                         SWP_NOZORDER);
        }

402
        InvalidateRect(infoPtr->hwndChild, NULL, TRUE);
403 404
    }
}
Alexandre Julliard's avatar
Alexandre Julliard committed
405

406
static INT
407
PAGER_GetScrollRange(PAGER_INFO* infoPtr)
Alexandre Julliard's avatar
Alexandre Julliard committed
408
{
409
    INT scrollRange = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
410

411 412 413 414
    if (infoPtr->hwndChild)
    {
        INT wndSize, childSize;
        RECT wndRect;
415
        GetWindowRect(infoPtr->hwndSelf, &wndRect);
416

417
        if (infoPtr->dwStyle & PGS_HORZ)
418 419
        {
            wndSize = wndRect.right - wndRect.left;
420
            PAGER_CalcSize(infoPtr, &infoPtr->nWidth, TRUE);
421 422 423 424 425
            childSize = infoPtr->nWidth;
        }
        else
        {
            wndSize = wndRect.bottom - wndRect.top;
426
            PAGER_CalcSize(infoPtr, &infoPtr->nHeight, FALSE);
427 428 429
            childSize = infoPtr->nHeight;
        }

430
        TRACE("childSize = %d,  wndSize = %d\n", childSize, wndSize);
431 432 433
        if (childSize > wndSize)
            scrollRange = childSize - wndSize + infoPtr->nButtonSize;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
434

435
    TRACE("[%p] returns %d\n", infoPtr->hwndSelf, scrollRange);
436
    return scrollRange;
Alexandre Julliard's avatar
Alexandre Julliard committed
437 438
}

439
static void
440
PAGER_UpdateBtns(PAGER_INFO *infoPtr, INT scrollRange, BOOL hideGrayBtns)
441
{
442 443 444 445 446 447 448 449
    BOOL resizeClient;
    BOOL repaintBtns;
    INT oldTLbtnState = infoPtr->TLbtnState;
    INT oldBRbtnState = infoPtr->BRbtnState;
    POINT pt;
    RECT rcTopLeft, rcBottomRight;

    /* get button rects */
450
    PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, FALSE);
451 452 453 454

    GetCursorPos(&pt);

    /* update states based on scroll position */
455 456
    if (infoPtr->nPos > 0)
    {
457
        if (infoPtr->TLbtnState == PGF_INVISIBLE || infoPtr->TLbtnState == PGF_GRAYED)
458 459
            infoPtr->TLbtnState = PGF_NORMAL;
    }
460
    else if (PtInRect(&rcTopLeft, pt))
461
        infoPtr->TLbtnState = PGF_GRAYED;
462 463
    else
        infoPtr->TLbtnState = PGF_INVISIBLE;
Alexandre Julliard's avatar
Alexandre Julliard committed
464

465 466
    if (scrollRange <= 0)
    {
467 468
        infoPtr->TLbtnState = PGF_INVISIBLE;
        infoPtr->BRbtnState = PGF_INVISIBLE;
469 470 471
    }
    else if (infoPtr->nPos < scrollRange)
    {
472
        if (infoPtr->BRbtnState == PGF_INVISIBLE || infoPtr->BRbtnState == PGF_GRAYED)
473 474
            infoPtr->BRbtnState = PGF_NORMAL;
    }
475
    else if (PtInRect(&rcBottomRight, pt))
476 477
        infoPtr->BRbtnState = PGF_GRAYED;
    else
478
        infoPtr->BRbtnState = PGF_INVISIBLE;
479

480 481 482 483 484 485
    /* only need to resize when entering or leaving PGF_INVISIBLE state */
    resizeClient =
        ((oldTLbtnState == PGF_INVISIBLE) != (infoPtr->TLbtnState == PGF_INVISIBLE)) ||
        ((oldBRbtnState == PGF_INVISIBLE) != (infoPtr->BRbtnState == PGF_INVISIBLE));
    /* initiate NCCalcSize to resize client wnd if necessary */
    if (resizeClient)
486
        SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
487 488 489
                     SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
                     SWP_NOZORDER | SWP_NOACTIVATE);

490 491 492
    /* repaint when changing any state */
    repaintBtns = (oldTLbtnState != infoPtr->TLbtnState) || 
                  (oldBRbtnState != infoPtr->BRbtnState);
493
    if (repaintBtns)
494
        SendMessageW(infoPtr->hwndSelf, WM_NCPAINT, 0, 0);
495 496
}

497
static LRESULT
498
PAGER_SetPos(PAGER_INFO* infoPtr, INT newPos, BOOL fromBtnPress)
Alexandre Julliard's avatar
Alexandre Julliard committed
499
{
500
    INT scrollRange = PAGER_GetScrollRange(infoPtr);
501
    INT oldPos = infoPtr->nPos;
502

503 504
    if ((scrollRange <= 0) || (newPos < 0))
        infoPtr->nPos = 0;
505
    else if (newPos > scrollRange)
506 507
        infoPtr->nPos = scrollRange;
    else
508
        infoPtr->nPos = newPos;
Alexandre Julliard's avatar
Alexandre Julliard committed
509

510
    TRACE("[%p] pos=%d, oldpos=%d\n", infoPtr->hwndSelf, infoPtr->nPos, oldPos);
511

512 513 514
    if (infoPtr->nPos != oldPos)
    {
        /* gray and restore btns, and if from WM_SETPOS, hide the gray btns */
515 516
        PAGER_UpdateBtns(infoPtr, scrollRange, !fromBtnPress);
        PAGER_PositionChildWnd(infoPtr);
517
    }
518 519 520

    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
521

522
static LRESULT
523
PAGER_WindowPosChanging(PAGER_INFO* infoPtr, WINDOWPOS *winpos)
524
{
525
    if ((infoPtr->dwStyle & CCS_NORESIZE) && !(winpos->flags & SWP_NOSIZE))
526 527 528 529 530
    {
        /* don't let the app resize the nonscrollable dimension of a control
         * that was created with CCS_NORESIZE style
         * (i.e. height for a horizontal pager, or width for a vertical one) */

531 532 533
	/* except if the current dimension is 0 and app is setting for
	 * first time, then save amount as dimension. - GA 8/01 */

534
        if (infoPtr->dwStyle & PGS_HORZ)
535 536 537 538
	    if (!infoPtr->nHeight && winpos->cy)
		infoPtr->nHeight = winpos->cy;
	    else
		winpos->cy = infoPtr->nHeight;
539
        else
540 541 542 543 544
	    if (!infoPtr->nWidth && winpos->cx)
		infoPtr->nWidth = winpos->cx;
	    else
		winpos->cx = infoPtr->nWidth;
	return 0;
545
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
546

547
    return DefWindowProcW (infoPtr->hwndSelf, WM_WINDOWPOSCHANGING, 0, (LPARAM)winpos);
548
}
Alexandre Julliard's avatar
Alexandre Julliard committed
549

550
static INT
551
PAGER_SetFixedWidth(PAGER_INFO* infoPtr)
552 553
{
  /* Must set the non-scrollable dimension to be less than the full height/width
Austin English's avatar
Austin English committed
554 555
   * so that NCCalcSize is called.  The Microsoft docs mention 3/4 factor for button
   * size, and experimentation shows that the effect is almost right. */
556 557

    RECT wndRect;
558
    INT delta, h;
559
    GetWindowRect(infoPtr->hwndSelf, &wndRect);
560 561

    /* see what the app says for btn width */
562
    PAGER_CalcSize(infoPtr, &infoPtr->nWidth, TRUE);
563

564
    if (infoPtr->dwStyle & CCS_NORESIZE)
565 566 567 568 569 570 571 572
    {
        delta = wndRect.right - wndRect.left - infoPtr->nWidth;
        if (delta > infoPtr->nButtonSize)
            infoPtr->nWidth += 4 * infoPtr->nButtonSize / 3;
        else if (delta > 0)
            infoPtr->nWidth +=  infoPtr->nButtonSize / 3;
    }

573
    h = wndRect.bottom - wndRect.top + infoPtr->nButtonSize;
574

575
    TRACE("[%p] infoPtr->nWidth set to %d\n",
576
	       infoPtr->hwndSelf, infoPtr->nWidth);
577 578

    return h;
579 580
}

581
static INT
582
PAGER_SetFixedHeight(PAGER_INFO* infoPtr)
583 584
{
  /* Must set the non-scrollable dimension to be less than the full height/width
Austin English's avatar
Austin English committed
585 586
   * so that NCCalcSize is called.  The Microsoft docs mention 3/4 factor for button
   * size, and experimentation shows that the effect is almost right. */
587 588

    RECT wndRect;
589
    INT delta, w;
590
    GetWindowRect(infoPtr->hwndSelf, &wndRect);
591 592

    /* see what the app says for btn height */
593
    PAGER_CalcSize(infoPtr, &infoPtr->nHeight, FALSE);
594

595
    if (infoPtr->dwStyle & CCS_NORESIZE)
596 597 598
    {
        delta = wndRect.bottom - wndRect.top - infoPtr->nHeight;
        if (delta > infoPtr->nButtonSize)
599
            infoPtr->nHeight += infoPtr->nButtonSize;
600 601 602 603
        else if (delta > 0)
            infoPtr->nHeight +=  infoPtr->nButtonSize / 3;
    }

604
    w = wndRect.right - wndRect.left + infoPtr->nButtonSize;
605

606
    TRACE("[%p] infoPtr->nHeight set to %d\n",
607
	       infoPtr->hwndSelf, infoPtr->nHeight);
608 609

    return w;
610 611
}

612 613 614 615 616
/******************************************************************
 * For the PGM_RECALCSIZE message (but not the other uses in      *
 * this module), the native control does only the following:      *
 *                                                                *
 *    if (some condition)                                         *
617 618
 *          PostMessageW(hwnd, EM_FMTLINES, 0, 0);                *
 *    return DefWindowProcW(hwnd, PGM_RECALCSIZE, 0, 0);          *
619 620 621 622 623
 *                                                                *
 * When we figure out what the "some condition" is we will        *
 * implement that for the message processing.                     *
 ******************************************************************/

624
static LRESULT
625
PAGER_RecalcSize(PAGER_INFO *infoPtr)
626
{
627
    TRACE("[%p]\n", infoPtr->hwndSelf);
Alexandre Julliard's avatar
Alexandre Julliard committed
628

629
    if (infoPtr->hwndChild)
630
    {
631
        INT scrollRange = PAGER_GetScrollRange(infoPtr);
632 633

        if (scrollRange <= 0)
634 635
        {
            infoPtr->nPos = -1;
636
            PAGER_SetPos(infoPtr, 0, FALSE);
637
        }
638
        else
639
            PAGER_PositionChildWnd(infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
640
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
641

642
    return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
643
}
Alexandre Julliard's avatar
Alexandre Julliard committed
644 645


646 647
static COLORREF
PAGER_SetBkColor (PAGER_INFO* infoPtr, COLORREF clrBk)
Alexandre Julliard's avatar
Alexandre Julliard committed
648 649 650
{
    COLORREF clrTemp = infoPtr->clrBk;

651
    infoPtr->clrBk = clrBk;
652
    TRACE("[%p] %06x\n", infoPtr->hwndSelf, infoPtr->clrBk);
Alexandre Julliard's avatar
Alexandre Julliard committed
653

654
    /* the native control seems to do things this way */
655
    SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
656 657 658
		 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
		 SWP_NOZORDER | SWP_NOACTIVATE);

659
    RedrawWindow(infoPtr->hwndSelf, 0, 0, RDW_ERASE | RDW_INVALIDATE);
Alexandre Julliard's avatar
Alexandre Julliard committed
660

661
    return clrTemp;
Alexandre Julliard's avatar
Alexandre Julliard committed
662 663 664
}


665 666
static INT
PAGER_SetBorder (PAGER_INFO* infoPtr, INT iBorder)
Alexandre Julliard's avatar
Alexandre Julliard committed
667
{
668
    INT nTemp = infoPtr->nBorder;
Alexandre Julliard's avatar
Alexandre Julliard committed
669

670 671
    infoPtr->nBorder = iBorder;
    TRACE("[%p] %d\n", infoPtr->hwndSelf, infoPtr->nBorder);
Alexandre Julliard's avatar
Alexandre Julliard committed
672

673
    PAGER_RecalcSize(infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
674

675
    return nTemp;
Alexandre Julliard's avatar
Alexandre Julliard committed
676 677 678
}


679 680
static INT
PAGER_SetButtonSize (PAGER_INFO* infoPtr, INT iButtonSize)
Alexandre Julliard's avatar
Alexandre Julliard committed
681
{
682
    INT nTemp = infoPtr->nButtonSize;
Alexandre Julliard's avatar
Alexandre Julliard committed
683

684 685
    infoPtr->nButtonSize = iButtonSize;
    TRACE("[%p] %d\n", infoPtr->hwndSelf, infoPtr->nButtonSize);
Alexandre Julliard's avatar
Alexandre Julliard committed
686

687
    PAGER_RecalcSize(infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
688

689
    return nTemp;
Alexandre Julliard's avatar
Alexandre Julliard committed
690 691 692
}


693
static LRESULT
694
PAGER_SetChild (PAGER_INFO* infoPtr, HWND hwndChild)
Alexandre Julliard's avatar
Alexandre Julliard committed
695
{
696
    INT hw;
Alexandre Julliard's avatar
Alexandre Julliard committed
697

698
    infoPtr->hwndChild = IsWindow (hwndChild) ? hwndChild : 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
699

700 701
    if (infoPtr->hwndChild)
    {
702
        TRACE("[%p] hwndChild=%p\n", infoPtr->hwndSelf, infoPtr->hwndChild);
703

704 705
        if (infoPtr->dwStyle & PGS_HORZ) {
            hw = PAGER_SetFixedHeight(infoPtr);
706
	    /* adjust non-scrollable dimension to fit the child */
707
	    SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, hw, infoPtr->nHeight,
708 709 710 711
			 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
			 SWP_NOSIZE | SWP_NOACTIVATE);
	}
        else {
712
            hw = PAGER_SetFixedWidth(infoPtr);
713
	    /* adjust non-scrollable dimension to fit the child */
714
	    SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, infoPtr->nWidth, hw,
715 716 717
			 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
			 SWP_NOSIZE | SWP_NOACTIVATE);
	}
718 719 720 721

        /* position child within the page scroller */
        SetWindowPos(infoPtr->hwndChild, HWND_TOP,
                     0,0,0,0,
722
                     SWP_SHOWWINDOW | SWP_NOSIZE);  /* native is 0 */
723

724
        infoPtr->nPos = -1;
725
        PAGER_SetPos(infoPtr, 0, FALSE);
726
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
727 728 729 730

    return 0;
}

731
static void
732
PAGER_Scroll(PAGER_INFO* infoPtr, INT dir)
Alexandre Julliard's avatar
Alexandre Julliard committed
733
{
734
    NMPGSCROLL nmpgScroll;
735
    RECT rcWnd;
Alexandre Julliard's avatar
Alexandre Julliard committed
736

737 738 739
    if (infoPtr->hwndChild)
    {
        ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL));
740 741
        nmpgScroll.hdr.hwndFrom = infoPtr->hwndSelf;
        nmpgScroll.hdr.idFrom   = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
742 743
        nmpgScroll.hdr.code = PGN_SCROLL;

744 745
        GetWindowRect(infoPtr->hwndSelf, &rcWnd);
        GetClientRect(infoPtr->hwndSelf, &nmpgScroll.rcParent);
746 747 748
        nmpgScroll.iXpos = nmpgScroll.iYpos = 0;
        nmpgScroll.iDir = dir;

749
        if (infoPtr->dwStyle & PGS_HORZ)
750
        {
751
            nmpgScroll.iScroll = rcWnd.right - rcWnd.left;
752
            nmpgScroll.iXpos = infoPtr->nPos;
753
        }
754
        else
755
        {
756
            nmpgScroll.iScroll = rcWnd.bottom - rcWnd.top;
757
            nmpgScroll.iYpos = infoPtr->nPos;
758
        }
759
        nmpgScroll.iScroll -= 2*infoPtr->nButtonSize;
760

761
        SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll);
762

763
        TRACE("[%p] PGN_SCROLL returns iScroll=%d\n", infoPtr->hwndSelf, nmpgScroll.iScroll);
764

765
        if (nmpgScroll.iScroll > 0)
766
        {
767 768
            infoPtr->direction = dir;

769
            if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP)
770
                PAGER_SetPos(infoPtr, infoPtr->nPos - nmpgScroll.iScroll, TRUE);
771
            else
772
                PAGER_SetPos(infoPtr, infoPtr->nPos + nmpgScroll.iScroll, TRUE);
773
        }
774 775
        else
            infoPtr->direction = -1;
776
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
777 778
}

779
static LRESULT
780
PAGER_FmtLines(const PAGER_INFO *infoPtr)
781 782
{
    /* initiate NCCalcSize to resize client wnd and get size */
783
    SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
784 785 786
		 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
		 SWP_NOZORDER | SWP_NOACTIVATE);

787 788
    SetWindowPos(infoPtr->hwndChild, 0,
		 0,0,infoPtr->nWidth,infoPtr->nHeight,
789 790
		 0);

791
    return DefWindowProcW (infoPtr->hwndSelf, EM_FMTLINES, 0, 0);
792 793
}

Alexandre Julliard's avatar
Alexandre Julliard committed
794
static LRESULT
795
PAGER_Create (HWND hwnd, const CREATESTRUCTW *lpcs)
Alexandre Julliard's avatar
Alexandre Julliard committed
796 797
{
    PAGER_INFO *infoPtr;
798

Alexandre Julliard's avatar
Alexandre Julliard committed
799
    /* allocate memory for info structure */
800
    infoPtr = Alloc (sizeof(PAGER_INFO));
801
    if (!infoPtr) return -1;
802
    SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
803 804

    /* set default settings */
805
    infoPtr->hwndSelf = hwnd;
806
    infoPtr->hwndChild = NULL;
807 808
    infoPtr->hwndNotify = lpcs->hwndParent;
    infoPtr->dwStyle = lpcs->style;
809
    infoPtr->clrBk = GetSysColor(COLOR_BTNFACE);
Alexandre Julliard's avatar
Alexandre Julliard committed
810
    infoPtr->nBorder = 0;
811
    infoPtr->nButtonSize = 12;
Alexandre Julliard's avatar
Alexandre Julliard committed
812
    infoPtr->nPos = 0;
813 814 815
    infoPtr->nWidth = 0;
    infoPtr->nHeight = 0;
    infoPtr->bForward = FALSE;
816
    infoPtr->bCapture = FALSE;
817 818
    infoPtr->TLbtnState = PGF_INVISIBLE;
    infoPtr->BRbtnState = PGF_INVISIBLE;
819
    infoPtr->direction = -1;
820

821 822
    if (infoPtr->dwStyle & PGS_DRAGNDROP)
        FIXME("[%p] Drag and Drop style is not implemented yet.\n", infoPtr->hwndSelf);
823

Alexandre Julliard's avatar
Alexandre Julliard committed
824 825 826 827 828
    return 0;
}


static LRESULT
829
PAGER_Destroy (PAGER_INFO *infoPtr)
Alexandre Julliard's avatar
Alexandre Julliard committed
830
{
831 832
    SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
    Free (infoPtr);  /* free pager info data */
833 834
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
835

836
static LRESULT
837
PAGER_NCCalcSize(PAGER_INFO* infoPtr, WPARAM wParam, LPRECT lpRect)
838
{
839
    RECT rcChild, rcWindow;
840

841
    /*
842
     * lpRect points to a RECT struct.  On entry, the struct
843
     * contains the proposed wnd rectangle for the window.
844 845 846
     * On exit, the struct should contain the screen
     * coordinates of the corresponding window's client area.
     */
847

848
    DefWindowProcW (infoPtr->hwndSelf, WM_NCCALCSIZE, wParam, (LPARAM)lpRect);
849

850 851 852
    TRACE("orig rect=%s\n", wine_dbgstr_rect(lpRect));

    GetWindowRect (infoPtr->hwndChild, &rcChild);
853 854
    MapWindowPoints (0, infoPtr->hwndSelf, (LPPOINT)&rcChild, 2); /* FIXME: RECT != 2 POINTS */
    GetWindowRect (infoPtr->hwndSelf, &rcWindow);
855

856
    if (infoPtr->dwStyle & PGS_HORZ)
857
    {
858
	infoPtr->nWidth = lpRect->right - lpRect->left;
859
	PAGER_CalcSize (infoPtr, &infoPtr->nWidth, TRUE);
860

861
	if (infoPtr->TLbtnState && (lpRect->left + infoPtr->nButtonSize < lpRect->right))
862
	    lpRect->left += infoPtr->nButtonSize;
863
	if (infoPtr->BRbtnState && (lpRect->right - infoPtr->nButtonSize > lpRect->left))
864
	    lpRect->right -= infoPtr->nButtonSize;
865 866 867
    }
    else
    {
868
	infoPtr->nHeight = lpRect->bottom - lpRect->top;
869
	PAGER_CalcSize (infoPtr, &infoPtr->nHeight, FALSE);
870

871
	if (infoPtr->TLbtnState && (lpRect->top + infoPtr->nButtonSize < lpRect->bottom))
872
	    lpRect->top += infoPtr->nButtonSize;
873
	if (infoPtr->BRbtnState && (lpRect->bottom - infoPtr->nButtonSize > lpRect->top))
874
	    lpRect->bottom -= infoPtr->nButtonSize;
875
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
876

877
    TRACE("nPos=%d, nHeight=%d, window=%s\n",
878 879 880
          infoPtr->nPos, infoPtr->nHeight,
          wine_dbgstr_rect(&rcWindow));

881
    TRACE("[%p] client rect set to %dx%d at (%d,%d) BtnState[%d,%d]\n",
882
	  infoPtr->hwndSelf, lpRect->right-lpRect->left, lpRect->bottom-lpRect->top,
883 884
	  lpRect->left, lpRect->top,
	  infoPtr->TLbtnState, infoPtr->BRbtnState);
Alexandre Julliard's avatar
Alexandre Julliard committed
885

886 887
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
888

889
static LRESULT
890
PAGER_NCPaint (const PAGER_INFO* infoPtr, HRGN hRgn)
891
{
892
    RECT rcBottomRight, rcTopLeft;
893 894
    HDC hdc;

895
    if (infoPtr->dwStyle & WS_MINIMIZE)
896 897
        return 0;

898
    DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)hRgn, 0);
899

900
    if (!(hdc = GetDCEx (infoPtr->hwndSelf, 0, DCX_USESTYLE | DCX_WINDOW)))
901 902
        return 0;

903
    PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, FALSE);
904 905

    PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft,
906
                     infoPtr->dwStyle & PGS_HORZ, TRUE, infoPtr->TLbtnState);
907
    PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight,
908
                     infoPtr->dwStyle & PGS_HORZ, FALSE, infoPtr->BRbtnState);
909

910
    ReleaseDC( infoPtr->hwndSelf, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
911 912 913
    return 0;
}

914
static INT
915
PAGER_HitTest (const PAGER_INFO* infoPtr, const POINT * pt)
916
{
917 918
    RECT clientRect, rcTopLeft, rcBottomRight;
    POINT ptWindow;
919

920
    GetClientRect (infoPtr->hwndSelf, &clientRect);
921 922 923

    if (PtInRect(&clientRect, *pt))
    {
924 925
        TRACE("child\n");
        return -1;
926 927
    }

928
    ptWindow = *pt;
929
    PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, TRUE);
930 931

    if ((infoPtr->TLbtnState != PGF_INVISIBLE) && PtInRect(&rcTopLeft, ptWindow))
932
    {
933 934
        TRACE("PGB_TOPORLEFT\n");
        return PGB_TOPORLEFT;
935
    }
936
    else if ((infoPtr->BRbtnState != PGF_INVISIBLE) && PtInRect(&rcBottomRight, ptWindow))
937
    {
938 939
        TRACE("PGB_BOTTOMORRIGHT\n");
        return PGB_BOTTOMORRIGHT;
940 941
    }

942 943
    TRACE("nowhere\n");
    return -1;
944 945 946
}

static LRESULT
947
PAGER_NCHitTest (const PAGER_INFO* infoPtr, INT x, INT y)
948
{
949
    POINT pt;
950
    INT nHit;
951

952 953
    pt.x = x;
    pt.y = y;
954

955 956 957 958
    ScreenToClient (infoPtr->hwndSelf, &pt);
    nHit = PAGER_HitTest(infoPtr, &pt);

    return (nHit < 0) ? HTTRANSPARENT : HTCLIENT;
959
}
Alexandre Julliard's avatar
Alexandre Julliard committed
960

961
static LRESULT
962
PAGER_MouseMove (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
963
{
964
    POINT clpt, pt;
965
    RECT wnrect, *btnrect = NULL;
966 967 968 969 970
    BOOL topLeft = FALSE;
    INT btnstate = 0;
    INT hit;
    HDC hdc;

971 972
    pt.x = x;
    pt.y = y;
973

974 975 976
    TRACE("[%p] to (%d,%d)\n", infoPtr->hwndSelf, x, y);
    ClientToScreen(infoPtr->hwndSelf, &pt);
    GetWindowRect(infoPtr->hwndSelf, &wnrect);
977
    if (PtInRect(&wnrect, pt)) {
978
        RECT TLbtnrect, BRbtnrect;
979
        PAGER_GetButtonRects(infoPtr, &TLbtnrect, &BRbtnrect, FALSE);
980 981

	clpt = pt;
982 983
	MapWindowPoints(0, infoPtr->hwndSelf, &clpt, 1);
	hit = PAGER_HitTest(infoPtr, &clpt);
984 985
	if ((hit == PGB_TOPORLEFT) && (infoPtr->TLbtnState == PGF_NORMAL))
	{
986 987
	    topLeft = TRUE;
	    btnrect = &TLbtnrect;
988
	    infoPtr->TLbtnState = PGF_HOT;
989 990
	    btnstate = infoPtr->TLbtnState;
	}
991 992
	else if ((hit == PGB_BOTTOMORRIGHT) && (infoPtr->BRbtnState == PGF_NORMAL))
	{
993 994
	    topLeft = FALSE;
	    btnrect = &BRbtnrect;
995
	    infoPtr->BRbtnState = PGF_HOT;
996 997 998 999
	    btnstate = infoPtr->BRbtnState;
	}

	/* If in one of the buttons the capture and draw buttons */
1000 1001
	if (btnrect)
	{
1002 1003
            TRACE("[%p] draw btn (%s), Capture %s, style %08x\n",
                  infoPtr->hwndSelf, wine_dbgstr_rect(btnrect),
1004
		  (infoPtr->bCapture) ? "TRUE" : "FALSE",
1005
		  infoPtr->dwStyle);
1006
	    if (!infoPtr->bCapture)
1007
	    {
1008 1009
	        TRACE("[%p] SetCapture\n", infoPtr->hwndSelf);
	        SetCapture(infoPtr->hwndSelf);
1010 1011
	        infoPtr->bCapture = TRUE;
	    }
1012 1013 1014
	    if (infoPtr->dwStyle & PGS_AUTOSCROLL)
		SetTimer(infoPtr->hwndSelf, TIMERID1, 0x3e, 0);
	    hdc = GetWindowDC(infoPtr->hwndSelf);
1015 1016
	    /* OffsetRect(wnrect, 0 | 1, 0 | 1) */
	    PAGER_DrawButton(hdc, infoPtr->clrBk, *btnrect,
1017 1018
			     infoPtr->dwStyle & PGS_HORZ, topLeft, btnstate);
	    ReleaseDC(infoPtr->hwndSelf, hdc);
1019
	    return 0;
1020 1021 1022 1023
	}
    }

    /* If we think we are captured, then do release */
1024
    if (infoPtr->bCapture && (WindowFromPoint(pt) != infoPtr->hwndSelf))
1025 1026
    {
    	NMHDR nmhdr;
1027

1028 1029
        infoPtr->bCapture = FALSE;

1030
        if (GetCapture() == infoPtr->hwndSelf)
1031 1032 1033 1034 1035 1036
        {
            ReleaseCapture();

            if (infoPtr->TLbtnState == PGF_GRAYED)
            {
                infoPtr->TLbtnState = PGF_INVISIBLE;
1037
                SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
1038 1039 1040 1041 1042 1043 1044
                             SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
                             SWP_NOZORDER | SWP_NOACTIVATE);
            }
            else if (infoPtr->TLbtnState == PGF_HOT)
            {
        	infoPtr->TLbtnState = PGF_NORMAL;
        	/* FIXME: just invalidate button rect */
1045
                RedrawWindow(infoPtr->hwndSelf, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
1046 1047 1048 1049 1050
            }

            if (infoPtr->BRbtnState == PGF_GRAYED)
            {
                infoPtr->BRbtnState = PGF_INVISIBLE;
1051
                SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
1052 1053 1054 1055 1056 1057 1058
                             SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
                             SWP_NOZORDER | SWP_NOACTIVATE);
            }
            else if (infoPtr->BRbtnState == PGF_HOT)
            {
        	infoPtr->BRbtnState = PGF_NORMAL;
        	/* FIXME: just invalidate button rect */
1059
                RedrawWindow(infoPtr->hwndSelf, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
1060 1061 1062 1063
            }

            /* Notify parent of released mouse capture */
        	memset(&nmhdr, 0, sizeof(NMHDR));
1064 1065
        	nmhdr.hwndFrom = infoPtr->hwndSelf;
        	nmhdr.idFrom   = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1066
        	nmhdr.code = NM_RELEASEDCAPTURE;
1067
		SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmhdr.idFrom, (LPARAM)&nmhdr);
1068
        }
1069 1070
        if (IsWindow(infoPtr->hwndSelf))
            KillTimer(infoPtr->hwndSelf, TIMERID1);
1071
    }
1072
    return 0;
1073 1074
}

Eric Kohl's avatar
Eric Kohl committed
1075
static LRESULT
1076
PAGER_LButtonDown (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
Eric Kohl's avatar
Eric Kohl committed
1077
{
1078
    BOOL repaintBtns = FALSE;
1079
    POINT pt;
1080
    INT hit;
Eric Kohl's avatar
Eric Kohl committed
1081

1082 1083
    pt.x = x;
    pt.y = y;
1084

1085
    TRACE("[%p] at (%d,%d)\n", infoPtr->hwndSelf, x, y);
1086

1087
    hit = PAGER_HitTest(infoPtr, &pt);
1088 1089

    /* put btn in DEPRESSED state */
1090
    if (hit == PGB_TOPORLEFT)
1091 1092 1093
    {
        repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED;
        infoPtr->TLbtnState = PGF_DEPRESSED;
1094
        SetTimer(infoPtr->hwndSelf, TIMERID1, INITIAL_DELAY, 0);
1095
    }
1096
    else if (hit == PGB_BOTTOMORRIGHT)
1097 1098 1099
    {
        repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED;
        infoPtr->BRbtnState = PGF_DEPRESSED;
1100
        SetTimer(infoPtr->hwndSelf, TIMERID1, INITIAL_DELAY, 0);
1101 1102 1103
    }

    if (repaintBtns)
1104
        SendMessageW(infoPtr->hwndSelf, WM_NCPAINT, 0, 0);
1105 1106 1107

    switch(hit)
    {
1108
    case PGB_TOPORLEFT:
1109
        if (infoPtr->dwStyle & PGS_HORZ)
1110
        {
1111 1112
            TRACE("[%p] PGF_SCROLLLEFT\n", infoPtr->hwndSelf);
            PAGER_Scroll(infoPtr, PGF_SCROLLLEFT);
1113 1114 1115
        }
        else
        {
1116 1117
            TRACE("[%p] PGF_SCROLLUP\n", infoPtr->hwndSelf);
            PAGER_Scroll(infoPtr, PGF_SCROLLUP);
1118
        }
1119
        break;
1120
    case PGB_BOTTOMORRIGHT:
1121
        if (infoPtr->dwStyle & PGS_HORZ)
1122
        {
1123 1124
            TRACE("[%p] PGF_SCROLLRIGHT\n", infoPtr->hwndSelf);
            PAGER_Scroll(infoPtr, PGF_SCROLLRIGHT);
1125 1126 1127
        }
        else
        {
1128 1129
            TRACE("[%p] PGF_SCROLLDOWN\n", infoPtr->hwndSelf);
            PAGER_Scroll(infoPtr, PGF_SCROLLDOWN);
1130
        }
1131 1132 1133 1134 1135
        break;
    default:
        break;
    }

1136
    return 0;
Eric Kohl's avatar
Eric Kohl committed
1137 1138
}

1139
static LRESULT
1140
PAGER_LButtonUp (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
1141
{
1142
    TRACE("[%p]\n", infoPtr->hwndSelf);
Eric Kohl's avatar
Eric Kohl committed
1143

1144 1145
    KillTimer (infoPtr->hwndSelf, TIMERID1);
    KillTimer (infoPtr->hwndSelf, TIMERID2);
1146

1147
    /* make PRESSED btns NORMAL but don't hide gray btns */
1148 1149 1150 1151
    if (infoPtr->TLbtnState & (PGF_HOT | PGF_DEPRESSED))
        infoPtr->TLbtnState = PGF_NORMAL;
    if (infoPtr->BRbtnState & (PGF_HOT | PGF_DEPRESSED))
        infoPtr->BRbtnState = PGF_NORMAL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1152

1153 1154
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1155

1156
static LRESULT
1157
PAGER_Timer (PAGER_INFO* infoPtr, INT nTimerId)
1158 1159 1160 1161
{
    INT dir;

    /* if initial timer, kill it and start the repeat timer */
1162
    if (nTimerId == TIMERID1) {
1163
	if (infoPtr->TLbtnState == PGF_HOT)
1164
	    dir = (infoPtr->dwStyle & PGS_HORZ) ?
1165 1166
		PGF_SCROLLLEFT : PGF_SCROLLUP;
	else
1167
	    dir = (infoPtr->dwStyle & PGS_HORZ) ?
1168
		PGF_SCROLLRIGHT : PGF_SCROLLDOWN;
1169
	TRACE("[%p] TIMERID1: style=%08x, dir=%d\n",
1170 1171 1172 1173 1174 1175
              infoPtr->hwndSelf, infoPtr->dwStyle, dir);
	KillTimer(infoPtr->hwndSelf, TIMERID1);
	SetTimer(infoPtr->hwndSelf, TIMERID1, REPEAT_DELAY, 0);
	if (infoPtr->dwStyle & PGS_AUTOSCROLL) {
	    PAGER_Scroll(infoPtr, dir);
	    SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
1176 1177 1178 1179 1180 1181 1182
			 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
			 SWP_NOZORDER | SWP_NOACTIVATE);
	}
	return 0;

    }

1183 1184
    TRACE("[%p] TIMERID2: dir=%d\n", infoPtr->hwndSelf, infoPtr->direction);
    KillTimer(infoPtr->hwndSelf, TIMERID2);
1185
    if (infoPtr->direction > 0) {
1186 1187
	PAGER_Scroll(infoPtr, infoPtr->direction);
	SetTimer(infoPtr->hwndSelf, TIMERID2, REPEAT_DELAY, 0);
1188 1189 1190 1191
    }
    return 0;
}

1192
static LRESULT
1193
PAGER_EraseBackground (const PAGER_INFO* infoPtr, HDC hdc)
1194
{
1195 1196 1197 1198 1199
    POINT pt, ptorig;
    HWND parent;

    pt.x = 0;
    pt.y = 0;
1200 1201
    parent = GetParent(infoPtr->hwndSelf);
    MapWindowPoints(infoPtr->hwndSelf, parent, &pt, 1);
1202
    OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig);
1203
    SendMessageW (parent, WM_ERASEBKGND, (WPARAM)hdc, 0);
1204 1205
    SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0);

1206
    return 0;
1207 1208 1209
}


1210
static LRESULT
1211
PAGER_Size (PAGER_INFO* infoPtr, INT type, INT x, INT y)
1212 1213 1214
{
    /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */

1215
    TRACE("[%p] %d,%d\n", infoPtr->hwndSelf, x, y);
1216

1217
    if (infoPtr->dwStyle & PGS_HORZ)
1218
        infoPtr->nHeight = y;
1219
    else
1220
        infoPtr->nWidth = x;
1221

1222
    return PAGER_RecalcSize(infoPtr);
1223 1224
}

1225

1226
static LRESULT 
1227
PAGER_StyleChanged(PAGER_INFO *infoPtr, WPARAM wStyleType, const STYLESTRUCT *lpss)
1228 1229 1230
{
    DWORD oldStyle = infoPtr->dwStyle;

1231
    TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245
          wStyleType, lpss->styleOld, lpss->styleNew);

    if (wStyleType != GWL_STYLE) return 0;
  
    infoPtr->dwStyle = lpss->styleNew;

    if ((oldStyle ^ lpss->styleNew) & (PGS_HORZ | PGS_VERT))
    {
        PAGER_RecalcSize(infoPtr);
    }

    return 0;
}

1246
static LRESULT WINAPI
1247
PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
1248
{
1249
    PAGER_INFO *infoPtr = (PAGER_INFO *)GetWindowLongPtrW(hwnd, 0);
1250 1251

    if (!infoPtr && (uMsg != WM_CREATE))
1252
	return DefWindowProcW (hwnd, uMsg, wParam, lParam);
1253

Alexandre Julliard's avatar
Alexandre Julliard committed
1254 1255
    switch (uMsg)
    {
1256
        case EM_FMTLINES:
1257
	    return PAGER_FmtLines(infoPtr);
1258

1259
        case PGM_FORWARDMOUSE:
1260
            return PAGER_ForwardMouse (infoPtr, (BOOL)wParam);
1261 1262

        case PGM_GETBKCOLOR:
1263
            return PAGER_GetBkColor(infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1264

1265
        case PGM_GETBORDER:
1266
            return PAGER_GetBorder(infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1267

1268
        case PGM_GETBUTTONSIZE:
1269
            return PAGER_GetButtonSize(infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1270

1271
        case PGM_GETPOS:
1272
            return PAGER_GetPos(infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1273

1274
        case PGM_GETBUTTONSTATE:
1275
            return PAGER_GetButtonState (infoPtr, (INT)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1276

1277
/*      case PGM_GETDROPTARGET: */
Alexandre Julliard's avatar
Alexandre Julliard committed
1278

1279
        case PGM_RECALCSIZE:
1280
            return PAGER_RecalcSize(infoPtr);
1281

1282
        case PGM_SETBKCOLOR:
1283
            return PAGER_SetBkColor (infoPtr, (COLORREF)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1284

1285
        case PGM_SETBORDER:
1286
            return PAGER_SetBorder (infoPtr, (INT)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1287

1288
        case PGM_SETBUTTONSIZE:
1289
            return PAGER_SetButtonSize (infoPtr, (INT)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1290

1291
        case PGM_SETCHILD:
1292
            return PAGER_SetChild (infoPtr, (HWND)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1293

1294
        case PGM_SETPOS:
1295
            return PAGER_SetPos(infoPtr, (INT)lParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1296

1297
        case WM_CREATE:
1298
            return PAGER_Create (hwnd, (LPCREATESTRUCTW)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1299

1300
        case WM_DESTROY:
1301
            return PAGER_Destroy (infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1302

1303
        case WM_SIZE:
1304
            return PAGER_Size (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
Alexandre Julliard's avatar
Alexandre Julliard committed
1305

1306
        case WM_NCPAINT:
1307
            return PAGER_NCPaint (infoPtr, (HRGN)wParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1308

1309
        case WM_WINDOWPOSCHANGING:
1310 1311 1312 1313
            return PAGER_WindowPosChanging (infoPtr, (WINDOWPOS*)lParam);

        case WM_STYLECHANGED:
            return PAGER_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
1314

1315
        case WM_NCCALCSIZE:
1316
            return PAGER_NCCalcSize (infoPtr, wParam, (LPRECT)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1317

1318
        case WM_NCHITTEST:
1319
            return PAGER_NCHitTest (infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
Alexandre Julliard's avatar
Alexandre Julliard committed
1320

1321 1322
        case WM_MOUSEMOVE:
            if (infoPtr->bForward && infoPtr->hwndChild)
1323
                PostMessageW(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam);
1324
            return PAGER_MouseMove (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
Alexandre Julliard's avatar
Alexandre Julliard committed
1325

1326
        case WM_LBUTTONDOWN:
1327
            return PAGER_LButtonDown (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
1328 1329

        case WM_LBUTTONUP:
1330
            return PAGER_LButtonUp (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
1331 1332

        case WM_ERASEBKGND:
1333
            return PAGER_EraseBackground (infoPtr, (HDC)wParam);
1334

1335
        case WM_TIMER:
1336
            return PAGER_Timer (infoPtr, (INT)wParam);
1337

1338 1339
        case WM_NOTIFY:
        case WM_COMMAND:
1340
            return SendMessageW (infoPtr->hwndNotify, uMsg, wParam, lParam);
1341 1342

        default:
1343
            return DefWindowProcW (hwnd, uMsg, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1344 1345 1346 1347
    }
}


1348
VOID
1349
PAGER_Register (void)
Alexandre Julliard's avatar
Alexandre Julliard committed
1350
{
1351
    WNDCLASSW wndClass;
Alexandre Julliard's avatar
Alexandre Julliard committed
1352

1353 1354 1355
    ZeroMemory (&wndClass, sizeof(WNDCLASSW));
    wndClass.style         = CS_GLOBALCLASS;
    wndClass.lpfnWndProc   = PAGER_WindowProc;
Alexandre Julliard's avatar
Alexandre Julliard committed
1356 1357
    wndClass.cbClsExtra    = 0;
    wndClass.cbWndExtra    = sizeof(PAGER_INFO *);
1358 1359 1360
    wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
    wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
    wndClass.lpszClassName = WC_PAGESCROLLERW;
1361

1362
    RegisterClassW (&wndClass);
Alexandre Julliard's avatar
Alexandre Julliard committed
1363 1364
}

1365 1366

VOID
1367
PAGER_Unregister (void)
1368
{
1369
    UnregisterClassW (WC_PAGESCROLLERW, NULL);
1370
}