pager.c 39.7 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 18 19
 * 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
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
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 39 40 41 42 43 44 45 46 47 48 49 50
 *
 * 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
51 52
 */

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

65
WINE_DEFAULT_DEBUG_CHANNEL(pager);
66

67 68
typedef struct
{
69
    HWND   hwndSelf;   /* handle of the control wnd */
70
    HWND   hwndChild;  /* handle of the contained wnd */
71
    HWND   hwndNotify; /* handle of the parent wnd */
72
    DWORD  dwStyle;    /* styles for this control */
73 74 75 76 77
    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 */
78
    INT    nHeight;    /* from child wnd's response to PGN_CALCSIZE */
79
    BOOL   bForward;   /* forward WM_MOUSEMOVE msgs to the contained wnd */
80
    BOOL   bCapture;   /* we have captured the mouse  */
81 82
    INT    TLbtnState; /* state of top or left btn */
    INT    BRbtnState; /* state of bottom or right btn */
83
    INT    direction;  /* direction of the scroll, (e.g. PGF_SCROLLUP) */
84
} PAGER_INFO;
Alexandre Julliard's avatar
Alexandre Julliard committed
85

86 87 88
#define MIN_ARROW_WIDTH  8
#define MIN_ARROW_HEIGHT 5

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

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

    if (bClientCoords)
    {
        POINT pt = {rcWindow.left, rcWindow.top};
103
        ScreenToClient(infoPtr->hwndSelf, &pt);
104 105 106 107 108 109
        OffsetRect(&rcWindow, -(rcWindow.left-pt.x), -(rcWindow.top-pt.y));
    }
    else
        OffsetRect(&rcWindow, -rcWindow.left, -rcWindow.top);

    *prcTopLeft = *prcBottomRight = rcWindow;
110
    if (infoPtr->dwStyle & PGS_HORZ)
111 112 113 114 115 116 117 118 119 120 121
    {
        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;
    }
}

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

140 141 142 143 144
    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 */

145 146
    if (!(hPen = CreatePen( PS_SOLID, 1, GetSysColor( colorRef )))) return;
    hOldPen = SelectObject ( hdc, hPen );
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
    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 );
171
    DeleteObject( hPen );
172 173
}

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

189 190 191 192 193
    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 */

194 195
    if (!(hPen = CreatePen( PS_SOLID, 1, GetSysColor( colorRef )))) return;
    hOldPen = SelectObject ( hdc, hPen );
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
    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 );
220
    DeleteObject( hPen );
221 222 223 224 225 226 227 228 229
}

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

230 231 232
    TRACE("arrowRect = %s, btnState = %d\n", wine_dbgstr_rect(&arrowRect), btnState);

    if (btnState == PGF_INVISIBLE)
233 234 235
        return;

    if ((rc.right - rc.left <= 0) || (rc.bottom - rc.top <= 0))
236
        return;
237 238 239 240 241 242

    hBrush = CreateSolidBrush(clrBk);
    hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);

    FillRect(hdc, &rc, hBrush);

243
    if (btnState == PGF_HOT)
244
    {
245
       DrawEdge( hdc, &rc, BDR_RAISEDINNER, BF_RECT);
246 247 248 249 250
       if (horz)
           PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
       else
           PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
    }
251
    else if (btnState == PGF_NORMAL)
252 253 254 255 256 257 258
    {
       DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
       if (horz)
           PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
       else
           PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
    }
259
    else if (btnState == PGF_DEPRESSED)
260 261 262 263 264 265 266
    {
       DrawEdge( hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
       if (horz)
           PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
       else
           PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
    }
267
    else if (btnState == PGF_GRAYED)
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
    {
       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
289

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

295
    infoPtr->bForward = bFwd;
Alexandre Julliard's avatar
Alexandre Julliard committed
296 297 298 299

    return 0;
}

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

306 307 308 309 310 311
    if (btn == PGB_TOPORLEFT)
        btnState = infoPtr->TLbtnState;
    else if (btn == PGB_BOTTOMORRIGHT)
        btnState = infoPtr->BRbtnState;

    return btnState;
Alexandre Julliard's avatar
Alexandre Julliard committed
312 313 314
}


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

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

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

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

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

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

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

363
static void
364
PAGER_PositionChildWnd(PAGER_INFO* infoPtr)
365 366 367
{
    if (infoPtr->hwndChild)
    {
368
        RECT rcClient;
369 370 371 372 373 374
        int nPos = infoPtr->nPos;

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

375
        GetClientRect(infoPtr->hwndSelf, &rcClient);
376

377
        if (infoPtr->dwStyle & PGS_HORZ)
378
        {
379 380 381 382
            int wndSize = max(0, rcClient.right - rcClient.left);
            if (infoPtr->nWidth < wndSize)
                infoPtr->nWidth = wndSize;

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

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

406
        InvalidateRect(infoPtr->hwndChild, NULL, TRUE);
407 408
    }
}
Alexandre Julliard's avatar
Alexandre Julliard committed
409

410
static INT
411
PAGER_GetScrollRange(PAGER_INFO* infoPtr)
Alexandre Julliard's avatar
Alexandre Julliard committed
412
{
413
    INT scrollRange = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
414

415 416 417 418
    if (infoPtr->hwndChild)
    {
        INT wndSize, childSize;
        RECT wndRect;
419
        GetWindowRect(infoPtr->hwndSelf, &wndRect);
420

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

434
        TRACE("childSize = %d,  wndSize = %d\n", childSize, wndSize);
435 436 437
        if (childSize > wndSize)
            scrollRange = childSize - wndSize + infoPtr->nButtonSize;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
438

439
    TRACE("[%p] returns %d\n", infoPtr->hwndSelf, scrollRange);
440
    return scrollRange;
Alexandre Julliard's avatar
Alexandre Julliard committed
441 442
}

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

    /* get button rects */
454
    PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, FALSE);
455 456 457 458

    GetCursorPos(&pt);

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

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

484 485 486 487 488 489
    /* 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)
490
        SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
491 492 493
                     SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
                     SWP_NOZORDER | SWP_NOACTIVATE);

494 495 496
    /* repaint when changing any state */
    repaintBtns = (oldTLbtnState != infoPtr->TLbtnState) || 
                  (oldBRbtnState != infoPtr->BRbtnState);
497
    if (repaintBtns)
498
        SendMessageW(infoPtr->hwndSelf, WM_NCPAINT, 0, 0);
499 500
}

501
static LRESULT
502
PAGER_SetPos(PAGER_INFO* infoPtr, INT newPos, BOOL fromBtnPress)
Alexandre Julliard's avatar
Alexandre Julliard committed
503
{
504
    INT scrollRange = PAGER_GetScrollRange(infoPtr);
505
    INT oldPos = infoPtr->nPos;
506

507 508
    if ((scrollRange <= 0) || (newPos < 0))
        infoPtr->nPos = 0;
509
    else if (newPos > scrollRange)
510 511
        infoPtr->nPos = scrollRange;
    else
512
        infoPtr->nPos = newPos;
Alexandre Julliard's avatar
Alexandre Julliard committed
513

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

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

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

526
static LRESULT
527
PAGER_WindowPosChanging(PAGER_INFO* infoPtr, WINDOWPOS *winpos)
528
{
529
    if ((infoPtr->dwStyle & CCS_NORESIZE) && !(winpos->flags & SWP_NOSIZE))
530 531 532 533 534
    {
        /* 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) */

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

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

551
    return DefWindowProcW (infoPtr->hwndSelf, WM_WINDOWPOSCHANGING, 0, (LPARAM)winpos);
552
}
Alexandre Julliard's avatar
Alexandre Julliard committed
553

554
static INT
555
PAGER_SetFixedWidth(PAGER_INFO* infoPtr)
556 557 558 559 560 561
{
  /* Must set the non-scrollable dimension to be less than the full height/width
   * so that NCCalcSize is called.  The Msoft docs mention 3/4 factor for button
   * size, and experimentation shows that affect is almost right. */

    RECT wndRect;
562
    INT delta, h;
563
    GetWindowRect(infoPtr->hwndSelf, &wndRect);
564 565

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

568
    if (infoPtr->dwStyle & CCS_NORESIZE)
569 570 571 572 573 574 575 576
    {
        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;
    }

577
    h = wndRect.bottom - wndRect.top + infoPtr->nButtonSize;
578

579
    TRACE("[%p] infoPtr->nWidth set to %d\n",
580
	       infoPtr->hwndSelf, infoPtr->nWidth);
581 582

    return h;
583 584
}

585
static INT
586
PAGER_SetFixedHeight(PAGER_INFO* infoPtr)
587 588 589 590 591 592
{
  /* Must set the non-scrollable dimension to be less than the full height/width
   * so that NCCalcSize is called.  The Msoft docs mention 3/4 factor for button
   * size, and experimentation shows that affect is almost right. */

    RECT wndRect;
593
    INT delta, w;
594
    GetWindowRect(infoPtr->hwndSelf, &wndRect);
595 596

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

599
    if (infoPtr->dwStyle & CCS_NORESIZE)
600 601 602
    {
        delta = wndRect.bottom - wndRect.top - infoPtr->nHeight;
        if (delta > infoPtr->nButtonSize)
603
            infoPtr->nHeight += infoPtr->nButtonSize;
604 605 606 607
        else if (delta > 0)
            infoPtr->nHeight +=  infoPtr->nButtonSize / 3;
    }

608
    w = wndRect.right - wndRect.left + infoPtr->nButtonSize;
609

610
    TRACE("[%p] infoPtr->nHeight set to %d\n",
611
	       infoPtr->hwndSelf, infoPtr->nHeight);
612 613

    return w;
614 615
}

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

628
static LRESULT
629
PAGER_RecalcSize(PAGER_INFO *infoPtr)
630
{
631
    TRACE("[%p]\n", infoPtr->hwndSelf);
Alexandre Julliard's avatar
Alexandre Julliard committed
632

633
    if (infoPtr->hwndChild)
634
    {
635
        INT scrollRange = PAGER_GetScrollRange(infoPtr);
636 637

        if (scrollRange <= 0)
638 639
        {
            infoPtr->nPos = -1;
640
            PAGER_SetPos(infoPtr, 0, FALSE);
641
        }
642
        else
643
            PAGER_PositionChildWnd(infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
644
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
645

646
    return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
647
}
Alexandre Julliard's avatar
Alexandre Julliard committed
648 649


650 651
static COLORREF
PAGER_SetBkColor (PAGER_INFO* infoPtr, COLORREF clrBk)
Alexandre Julliard's avatar
Alexandre Julliard committed
652 653 654
{
    COLORREF clrTemp = infoPtr->clrBk;

655 656
    infoPtr->clrBk = clrBk;
    TRACE("[%p] %06lx\n", infoPtr->hwndSelf, infoPtr->clrBk);
Alexandre Julliard's avatar
Alexandre Julliard committed
657

658
    /* the native control seems to do things this way */
659
    SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
660 661 662
		 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
		 SWP_NOZORDER | SWP_NOACTIVATE);

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

665
    return clrTemp;
Alexandre Julliard's avatar
Alexandre Julliard committed
666 667 668
}


669 670
static INT
PAGER_SetBorder (PAGER_INFO* infoPtr, INT iBorder)
Alexandre Julliard's avatar
Alexandre Julliard committed
671
{
672
    INT nTemp = infoPtr->nBorder;
Alexandre Julliard's avatar
Alexandre Julliard committed
673

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

677
    PAGER_RecalcSize(infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
678

679
    return nTemp;
Alexandre Julliard's avatar
Alexandre Julliard committed
680 681 682
}


683 684
static INT
PAGER_SetButtonSize (PAGER_INFO* infoPtr, INT iButtonSize)
Alexandre Julliard's avatar
Alexandre Julliard committed
685
{
686
    INT nTemp = infoPtr->nButtonSize;
Alexandre Julliard's avatar
Alexandre Julliard committed
687

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

691
    PAGER_RecalcSize(infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
692

693
    return nTemp;
Alexandre Julliard's avatar
Alexandre Julliard committed
694 695 696
}


697
static LRESULT
698
PAGER_SetChild (PAGER_INFO* infoPtr, HWND hwndChild)
Alexandre Julliard's avatar
Alexandre Julliard committed
699
{
700
    INT hw;
Alexandre Julliard's avatar
Alexandre Julliard committed
701

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

704 705
    if (infoPtr->hwndChild)
    {
706
        TRACE("[%p] hwndChild=%p\n", infoPtr->hwndSelf, infoPtr->hwndChild);
707

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

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

728
        infoPtr->nPos = -1;
729
        PAGER_SetPos(infoPtr, 0, FALSE);
730
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
731 732 733 734

    return 0;
}

735
static void
736
PAGER_Scroll(PAGER_INFO* infoPtr, INT dir)
Alexandre Julliard's avatar
Alexandre Julliard committed
737
{
738
    NMPGSCROLL nmpgScroll;
739
    RECT rcWnd;
Alexandre Julliard's avatar
Alexandre Julliard committed
740

741 742 743
    if (infoPtr->hwndChild)
    {
        ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL));
744 745
        nmpgScroll.hdr.hwndFrom = infoPtr->hwndSelf;
        nmpgScroll.hdr.idFrom   = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
746 747
        nmpgScroll.hdr.code = PGN_SCROLL;

748 749
        GetWindowRect(infoPtr->hwndSelf, &rcWnd);
        GetClientRect(infoPtr->hwndSelf, &nmpgScroll.rcParent);
750 751 752
        nmpgScroll.iXpos = nmpgScroll.iYpos = 0;
        nmpgScroll.iDir = dir;

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

765
        SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
766
                    (WPARAM)nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll);
767

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

770
        if (nmpgScroll.iScroll > 0)
771
        {
772 773
            infoPtr->direction = dir;

774
            if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP)
775
                PAGER_SetPos(infoPtr, infoPtr->nPos - nmpgScroll.iScroll, TRUE);
776
            else
777
                PAGER_SetPos(infoPtr, infoPtr->nPos + nmpgScroll.iScroll, TRUE);
778
        }
779 780
        else
            infoPtr->direction = -1;
781
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
782 783
}

784
static LRESULT
785
PAGER_FmtLines(PAGER_INFO *infoPtr)
786 787
{
    /* initiate NCCalcSize to resize client wnd and get size */
788
    SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
789 790 791
		 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
		 SWP_NOZORDER | SWP_NOACTIVATE);

792 793
    SetWindowPos(infoPtr->hwndChild, 0,
		 0,0,infoPtr->nWidth,infoPtr->nHeight,
794 795
		 0);

796
    return DefWindowProcW (infoPtr->hwndSelf, EM_FMTLINES, 0, 0);
797 798
}

Alexandre Julliard's avatar
Alexandre Julliard committed
799
static LRESULT
800
PAGER_Create (HWND hwnd, LPCREATESTRUCTW lpcs)
Alexandre Julliard's avatar
Alexandre Julliard committed
801 802
{
    PAGER_INFO *infoPtr;
803

Alexandre Julliard's avatar
Alexandre Julliard committed
804
    /* allocate memory for info structure */
805
    infoPtr = (PAGER_INFO *)Alloc (sizeof(PAGER_INFO));
806
    if (!infoPtr) return -1;
807
    SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
808 809

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
829 830 831 832 833
    return 0;
}


static LRESULT
834
PAGER_Destroy (PAGER_INFO *infoPtr)
Alexandre Julliard's avatar
Alexandre Julliard committed
835
{
836 837
    SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
    Free (infoPtr);  /* free pager info data */
838 839
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
840

841
static LRESULT
842
PAGER_NCCalcSize(PAGER_INFO* infoPtr, WPARAM wParam, LPRECT lpRect)
843
{
844
    RECT rcChild, rcWindow;
845
    INT scrollRange;
846

847
    /*
848
     * lpRect points to a RECT struct.  On entry, the struct
849
     * contains the proposed wnd rectangle for the window.
850 851 852
     * On exit, the struct should contain the screen
     * coordinates of the corresponding window's client area.
     */
853

854
    DefWindowProcW (infoPtr->hwndSelf, WM_NCCALCSIZE, wParam, (LPARAM)lpRect);
855

856 857 858
    TRACE("orig rect=%s\n", wine_dbgstr_rect(lpRect));

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

862
    if (infoPtr->dwStyle & PGS_HORZ)
863
    {
864
	infoPtr->nWidth = lpRect->right - lpRect->left;
865
	PAGER_CalcSize (infoPtr, &infoPtr->nWidth, TRUE);
866 867 868

	scrollRange = infoPtr->nWidth - (rcWindow.right - rcWindow.left);

869
	if (infoPtr->TLbtnState && (lpRect->left + infoPtr->nButtonSize < lpRect->right))
870
	    lpRect->left += infoPtr->nButtonSize;
871
	if (infoPtr->BRbtnState && (lpRect->right - infoPtr->nButtonSize > lpRect->left))
872
	    lpRect->right -= infoPtr->nButtonSize;
873 874 875
    }
    else
    {
876
	infoPtr->nHeight = lpRect->bottom - lpRect->top;
877
	PAGER_CalcSize (infoPtr, &infoPtr->nHeight, FALSE);
878 879 880

	scrollRange = infoPtr->nHeight - (rcWindow.bottom - rcWindow.top);

881
	if (infoPtr->TLbtnState && (lpRect->top + infoPtr->nButtonSize < lpRect->bottom))
882
	    lpRect->top += infoPtr->nButtonSize;
883
	if (infoPtr->BRbtnState && (lpRect->bottom - infoPtr->nButtonSize > lpRect->top))
884
	    lpRect->bottom -= infoPtr->nButtonSize;
885
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
886

887 888 889 890
    TRACE("nPos=%d, nHeigth=%d, window=%s\n",
          infoPtr->nPos, infoPtr->nHeight,
          wine_dbgstr_rect(&rcWindow));

891
    TRACE("[%p] client rect set to %ldx%ld at (%ld,%ld) BtnState[%d,%d]\n",
892
	  infoPtr->hwndSelf, lpRect->right-lpRect->left, lpRect->bottom-lpRect->top,
893 894
	  lpRect->left, lpRect->top,
	  infoPtr->TLbtnState, infoPtr->BRbtnState);
Alexandre Julliard's avatar
Alexandre Julliard committed
895

896 897
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
898

899
static LRESULT
900
PAGER_NCPaint (PAGER_INFO* infoPtr, HRGN hRgn)
901
{
902
    RECT rcBottomRight, rcTopLeft;
903 904
    HDC hdc;

905
    if (infoPtr->dwStyle & WS_MINIMIZE)
906 907
        return 0;

908
    DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)hRgn, 0);
909

910
    if (!(hdc = GetDCEx (infoPtr->hwndSelf, 0, DCX_USESTYLE | DCX_WINDOW)))
911 912
        return 0;

913
    PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, FALSE);
914 915

    PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft,
916
                     infoPtr->dwStyle & PGS_HORZ, TRUE, infoPtr->TLbtnState);
917
    PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight,
918
                     infoPtr->dwStyle & PGS_HORZ, FALSE, infoPtr->BRbtnState);
919

920
    ReleaseDC( infoPtr->hwndSelf, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
921 922 923
    return 0;
}

924
static INT
925
PAGER_HitTest (PAGER_INFO* infoPtr, const POINT * pt)
926
{
927 928
    RECT clientRect, rcTopLeft, rcBottomRight;
    POINT ptWindow;
929

930
    GetClientRect (infoPtr->hwndSelf, &clientRect);
931 932 933

    if (PtInRect(&clientRect, *pt))
    {
934 935
        TRACE("child\n");
        return -1;
936 937
    }

938
    ptWindow = *pt;
939
    PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, TRUE);
940 941

    if ((infoPtr->TLbtnState != PGF_INVISIBLE) && PtInRect(&rcTopLeft, ptWindow))
942
    {
943 944
        TRACE("PGB_TOPORLEFT\n");
        return PGB_TOPORLEFT;
945
    }
946
    else if ((infoPtr->BRbtnState != PGF_INVISIBLE) && PtInRect(&rcBottomRight, ptWindow))
947
    {
948 949
        TRACE("PGB_BOTTOMORRIGHT\n");
        return PGB_BOTTOMORRIGHT;
950 951
    }

952 953
    TRACE("nowhere\n");
    return -1;
954 955 956
}

static LRESULT
957
PAGER_NCHitTest (PAGER_INFO* infoPtr, INT x, INT y)
958
{
959
    POINT pt;
960
    INT nHit;
961

962 963
    pt.x = x;
    pt.y = y;
964

965 966 967 968
    ScreenToClient (infoPtr->hwndSelf, &pt);
    nHit = PAGER_HitTest(infoPtr, &pt);

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

971
static LRESULT
972
PAGER_MouseMove (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
973
{
974
    POINT clpt, pt;
975
    RECT wnrect, *btnrect = NULL;
976 977 978 979 980
    BOOL topLeft = FALSE;
    INT btnstate = 0;
    INT hit;
    HDC hdc;

981 982
    pt.x = x;
    pt.y = y;
983

984 985 986
    TRACE("[%p] to (%d,%d)\n", infoPtr->hwndSelf, x, y);
    ClientToScreen(infoPtr->hwndSelf, &pt);
    GetWindowRect(infoPtr->hwndSelf, &wnrect);
987
    if (PtInRect(&wnrect, pt)) {
988
        RECT TLbtnrect, BRbtnrect;
989
        PAGER_GetButtonRects(infoPtr, &TLbtnrect, &BRbtnrect, FALSE);
990 991

	clpt = pt;
992 993
	MapWindowPoints(0, infoPtr->hwndSelf, &clpt, 1);
	hit = PAGER_HitTest(infoPtr, &clpt);
994 995
	if ((hit == PGB_TOPORLEFT) && (infoPtr->TLbtnState == PGF_NORMAL))
	{
996 997
	    topLeft = TRUE;
	    btnrect = &TLbtnrect;
998
	    infoPtr->TLbtnState = PGF_HOT;
999 1000
	    btnstate = infoPtr->TLbtnState;
	}
1001 1002
	else if ((hit == PGB_BOTTOMORRIGHT) && (infoPtr->BRbtnState == PGF_NORMAL))
	{
1003 1004
	    topLeft = FALSE;
	    btnrect = &BRbtnrect;
1005
	    infoPtr->BRbtnState = PGF_HOT;
1006 1007 1008 1009
	    btnstate = infoPtr->BRbtnState;
	}

	/* If in one of the buttons the capture and draw buttons */
1010 1011
	if (btnrect)
	{
1012
	    TRACE("[%p] draw btn (%ld,%ld)-(%ld,%ld), Capture %s, style %08lx\n",
1013
		  infoPtr->hwndSelf, btnrect->left, btnrect->top,
1014 1015
		  btnrect->right, btnrect->bottom,
		  (infoPtr->bCapture) ? "TRUE" : "FALSE",
1016
		  infoPtr->dwStyle);
1017
	    if (!infoPtr->bCapture)
1018
	    {
1019 1020
	        TRACE("[%p] SetCapture\n", infoPtr->hwndSelf);
	        SetCapture(infoPtr->hwndSelf);
1021 1022
	        infoPtr->bCapture = TRUE;
	    }
1023 1024 1025
	    if (infoPtr->dwStyle & PGS_AUTOSCROLL)
		SetTimer(infoPtr->hwndSelf, TIMERID1, 0x3e, 0);
	    hdc = GetWindowDC(infoPtr->hwndSelf);
1026 1027
	    /* OffsetRect(wnrect, 0 | 1, 0 | 1) */
	    PAGER_DrawButton(hdc, infoPtr->clrBk, *btnrect,
1028 1029
			     infoPtr->dwStyle & PGS_HORZ, topLeft, btnstate);
	    ReleaseDC(infoPtr->hwndSelf, hdc);
1030
	    return 0;
1031 1032 1033 1034
	}
    }

    /* If we think we are captured, then do release */
1035
    if (infoPtr->bCapture && (WindowFromPoint(pt) != infoPtr->hwndSelf))
1036 1037
    {
    	NMHDR nmhdr;
1038

1039 1040
        infoPtr->bCapture = FALSE;

1041
        if (GetCapture() == infoPtr->hwndSelf)
1042 1043 1044 1045 1046 1047
        {
            ReleaseCapture();

            if (infoPtr->TLbtnState == PGF_GRAYED)
            {
                infoPtr->TLbtnState = PGF_INVISIBLE;
1048
                SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
1049 1050 1051 1052 1053 1054 1055
                             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 */
1056
                RedrawWindow(infoPtr->hwndSelf, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
1057 1058 1059 1060 1061
            }

            if (infoPtr->BRbtnState == PGF_GRAYED)
            {
                infoPtr->BRbtnState = PGF_INVISIBLE;
1062
                SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
1063 1064 1065 1066 1067 1068 1069
                             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 */
1070
                RedrawWindow(infoPtr->hwndSelf, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
1071 1072 1073 1074
            }

            /* Notify parent of released mouse capture */
        	memset(&nmhdr, 0, sizeof(NMHDR));
1075 1076
        	nmhdr.hwndFrom = infoPtr->hwndSelf;
        	nmhdr.idFrom   = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1077 1078 1079 1080
        	nmhdr.code = NM_RELEASEDCAPTURE;
        	SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
                         (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
        }
1081 1082
        if (IsWindow(infoPtr->hwndSelf))
            KillTimer(infoPtr->hwndSelf, TIMERID1);
1083
    }
1084
    return 0;
1085 1086
}

Eric Kohl's avatar
Eric Kohl committed
1087
static LRESULT
1088
PAGER_LButtonDown (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
Eric Kohl's avatar
Eric Kohl committed
1089
{
1090
    BOOL repaintBtns = FALSE;
1091
    POINT pt;
1092
    INT hit;
Eric Kohl's avatar
Eric Kohl committed
1093

1094 1095
    pt.x = x;
    pt.y = y;
1096

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

1099
    hit = PAGER_HitTest(infoPtr, &pt);
1100 1101

    /* put btn in DEPRESSED state */
1102
    if (hit == PGB_TOPORLEFT)
1103 1104 1105
    {
        repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED;
        infoPtr->TLbtnState = PGF_DEPRESSED;
1106
        SetTimer(infoPtr->hwndSelf, TIMERID1, INITIAL_DELAY, 0);
1107
    }
1108
    else if (hit == PGB_BOTTOMORRIGHT)
1109 1110 1111
    {
        repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED;
        infoPtr->BRbtnState = PGF_DEPRESSED;
1112
        SetTimer(infoPtr->hwndSelf, TIMERID1, INITIAL_DELAY, 0);
1113 1114 1115
    }

    if (repaintBtns)
1116
        SendMessageW(infoPtr->hwndSelf, WM_NCPAINT, 0, 0);
1117 1118 1119

    switch(hit)
    {
1120
    case PGB_TOPORLEFT:
1121
        if (infoPtr->dwStyle & PGS_HORZ)
1122
        {
1123 1124
            TRACE("[%p] PGF_SCROLLLEFT\n", infoPtr->hwndSelf);
            PAGER_Scroll(infoPtr, PGF_SCROLLLEFT);
1125 1126 1127
        }
        else
        {
1128 1129
            TRACE("[%p] PGF_SCROLLUP\n", infoPtr->hwndSelf);
            PAGER_Scroll(infoPtr, PGF_SCROLLUP);
1130
        }
1131
        break;
1132
    case PGB_BOTTOMORRIGHT:
1133
        if (infoPtr->dwStyle & PGS_HORZ)
1134
        {
1135 1136
            TRACE("[%p] PGF_SCROLLRIGHT\n", infoPtr->hwndSelf);
            PAGER_Scroll(infoPtr, PGF_SCROLLRIGHT);
1137 1138 1139
        }
        else
        {
1140 1141
            TRACE("[%p] PGF_SCROLLDOWN\n", infoPtr->hwndSelf);
            PAGER_Scroll(infoPtr, PGF_SCROLLDOWN);
1142
        }
1143 1144 1145 1146 1147
        break;
    default:
        break;
    }

1148
    return 0;
Eric Kohl's avatar
Eric Kohl committed
1149 1150
}

1151
static LRESULT
1152
PAGER_LButtonUp (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
1153
{
1154
    TRACE("[%p]\n", infoPtr->hwndSelf);
Eric Kohl's avatar
Eric Kohl committed
1155

1156 1157
    KillTimer (infoPtr->hwndSelf, TIMERID1);
    KillTimer (infoPtr->hwndSelf, TIMERID2);
1158

1159
    /* make PRESSED btns NORMAL but don't hide gray btns */
1160 1161 1162 1163
    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
1164

1165 1166
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1167

1168
static LRESULT
1169
PAGER_Timer (PAGER_INFO* infoPtr, INT nTimerId)
1170 1171 1172 1173
{
    INT dir;

    /* if initial timer, kill it and start the repeat timer */
1174
    if (nTimerId == TIMERID1) {
1175
	if (infoPtr->TLbtnState == PGF_HOT)
1176
	    dir = (infoPtr->dwStyle & PGS_HORZ) ?
1177 1178
		PGF_SCROLLLEFT : PGF_SCROLLUP;
	else
1179
	    dir = (infoPtr->dwStyle & PGS_HORZ) ?
1180
		PGF_SCROLLRIGHT : PGF_SCROLLDOWN;
1181 1182 1183 1184 1185 1186 1187
	TRACE("[%p] TIMERID1: style=%08lx, dir=%d\n", 
              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,
1188 1189 1190 1191 1192 1193 1194
			 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
			 SWP_NOZORDER | SWP_NOACTIVATE);
	}
	return 0;

    }

1195 1196
    TRACE("[%p] TIMERID2: dir=%d\n", infoPtr->hwndSelf, infoPtr->direction);
    KillTimer(infoPtr->hwndSelf, TIMERID2);
1197
    if (infoPtr->direction > 0) {
1198 1199
	PAGER_Scroll(infoPtr, infoPtr->direction);
	SetTimer(infoPtr->hwndSelf, TIMERID2, REPEAT_DELAY, 0);
1200 1201 1202 1203
    }
    return 0;
}

1204
static LRESULT
1205
PAGER_EraseBackground (PAGER_INFO* infoPtr, HDC hdc)
1206
{
1207 1208 1209 1210 1211
    POINT pt, ptorig;
    HWND parent;

    pt.x = 0;
    pt.y = 0;
1212 1213
    parent = GetParent(infoPtr->hwndSelf);
    MapWindowPoints(infoPtr->hwndSelf, parent, &pt, 1);
1214
    OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig);
1215
    SendMessageW (parent, WM_ERASEBKGND, (WPARAM)hdc, 0);
1216 1217
    SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0);

1218
    return 0;
1219 1220 1221
}


1222
static LRESULT
1223
PAGER_Size (PAGER_INFO* infoPtr, INT type, INT x, INT y)
1224 1225 1226
{
    /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */

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

1229
    if (infoPtr->dwStyle & PGS_HORZ)
1230
        infoPtr->nHeight = y;
1231
    else
1232
        infoPtr->nWidth = x;
1233

1234
    return PAGER_RecalcSize(infoPtr);
1235 1236
}

1237

1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257
static LRESULT 
PAGER_StyleChanged(PAGER_INFO *infoPtr, WPARAM wStyleType, LPSTYLESTRUCT lpss)
{
    DWORD oldStyle = infoPtr->dwStyle;

    TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
          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;
}

1258
static LRESULT WINAPI
1259
PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
1260
{
1261
    PAGER_INFO *infoPtr = (PAGER_INFO *)GetWindowLongPtrW(hwnd, 0);
1262 1263

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1266 1267
    switch (uMsg)
    {
1268
        case EM_FMTLINES:
1269
	    return PAGER_FmtLines(infoPtr);
1270

1271
        case PGM_FORWARDMOUSE:
1272
            return PAGER_ForwardMouse (infoPtr, (BOOL)wParam);
1273 1274

        case PGM_GETBKCOLOR:
1275
            return PAGER_GetBkColor(infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1276

1277
        case PGM_GETBORDER:
1278
            return PAGER_GetBorder(infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1279

1280
        case PGM_GETBUTTONSIZE:
1281
            return PAGER_GetButtonSize(infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1282

1283
        case PGM_GETPOS:
1284
            return PAGER_GetPos(infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1285

1286
        case PGM_GETBUTTONSTATE:
1287
            return PAGER_GetButtonState (infoPtr, (INT)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1288

1289
/*      case PGM_GETDROPTARGET: */
Alexandre Julliard's avatar
Alexandre Julliard committed
1290

1291
        case PGM_RECALCSIZE:
1292
            return PAGER_RecalcSize(infoPtr);
1293

1294
        case PGM_SETBKCOLOR:
1295
            return PAGER_SetBkColor (infoPtr, (COLORREF)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1296

1297
        case PGM_SETBORDER:
1298
            return PAGER_SetBorder (infoPtr, (INT)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1299

1300
        case PGM_SETBUTTONSIZE:
1301
            return PAGER_SetButtonSize (infoPtr, (INT)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1302

1303
        case PGM_SETCHILD:
1304
            return PAGER_SetChild (infoPtr, (HWND)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1305

1306
        case PGM_SETPOS:
1307
            return PAGER_SetPos(infoPtr, (INT)lParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1308

1309
        case WM_CREATE:
1310
            return PAGER_Create (hwnd, (LPCREATESTRUCTW)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1311

1312
        case WM_DESTROY:
1313
            return PAGER_Destroy (infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
1314

1315
        case WM_SIZE:
1316
            return PAGER_Size (infoPtr, (INT)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
Alexandre Julliard's avatar
Alexandre Julliard committed
1317

1318
        case WM_NCPAINT:
1319
            return PAGER_NCPaint (infoPtr, (HRGN)wParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1320

1321
        case WM_WINDOWPOSCHANGING:
1322 1323 1324 1325
            return PAGER_WindowPosChanging (infoPtr, (WINDOWPOS*)lParam);

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

1327
        case WM_NCCALCSIZE:
1328
            return PAGER_NCCalcSize (infoPtr, wParam, (LPRECT)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1329

1330
        case WM_NCHITTEST:
1331
            return PAGER_NCHitTest (infoPtr, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
Alexandre Julliard's avatar
Alexandre Julliard committed
1332

1333 1334
        case WM_MOUSEMOVE:
            if (infoPtr->bForward && infoPtr->hwndChild)
1335
                PostMessageW(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam);
1336
            return PAGER_MouseMove (infoPtr, (INT)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
Alexandre Julliard's avatar
Alexandre Julliard committed
1337

1338
        case WM_LBUTTONDOWN:
1339
            return PAGER_LButtonDown (infoPtr, (INT)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
1340 1341

        case WM_LBUTTONUP:
1342
            return PAGER_LButtonUp (infoPtr, (INT)wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
1343 1344

        case WM_ERASEBKGND:
1345
            return PAGER_EraseBackground (infoPtr, (HDC)wParam);
1346

1347
        case WM_TIMER:
1348
            return PAGER_Timer (infoPtr, (INT)wParam);
1349

1350 1351
        case WM_NOTIFY:
        case WM_COMMAND:
1352
            return SendMessageW (infoPtr->hwndNotify, uMsg, wParam, lParam);
1353 1354

        default:
1355
            return DefWindowProcW (hwnd, uMsg, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1356 1357 1358 1359
    }
}


1360
VOID
1361
PAGER_Register (void)
Alexandre Julliard's avatar
Alexandre Julliard committed
1362
{
1363
    WNDCLASSW wndClass;
Alexandre Julliard's avatar
Alexandre Julliard committed
1364

1365 1366 1367
    ZeroMemory (&wndClass, sizeof(WNDCLASSW));
    wndClass.style         = CS_GLOBALCLASS;
    wndClass.lpfnWndProc   = PAGER_WindowProc;
Alexandre Julliard's avatar
Alexandre Julliard committed
1368 1369
    wndClass.cbClsExtra    = 0;
    wndClass.cbWndExtra    = sizeof(PAGER_INFO *);
1370 1371 1372
    wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
    wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
    wndClass.lpszClassName = WC_PAGESCROLLERW;
1373

1374
    RegisterClassW (&wndClass);
Alexandre Julliard's avatar
Alexandre Julliard committed
1375 1376
}

1377 1378

VOID
1379
PAGER_Unregister (void)
1380
{
1381
    UnregisterClassW (WC_PAGESCROLLERW, NULL);
1382
}