pager.c 33.6 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
 *
 * NOTES
7 8
 *    Tested primarily with the controlspy Pager application.
 *       Susan Farley (susan@codeweavers.com)
Alexandre Julliard's avatar
Alexandre Julliard committed
9 10
 *
 * TODO:
11 12 13 14
 *    Implement repetitive button press.
 *    Adjust arrow size relative to size of button.
 *    Allow border size changes.
 *    Implement drag and drop style.
Alexandre Julliard's avatar
Alexandre Julliard committed
15 16
 */

17
#include <string.h>
18
#include "winbase.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
19
#include "commctrl.h"
20
#include "debugtools.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
21

22
DEFAULT_DEBUG_CHANNEL(pager);
23

24 25
typedef struct
{
26
    HWND   hwndChild;  /* handle of the contained wnd */
27
    BOOL   bNoResize;  /* set when created with CCS_NORESIZE */
28 29 30 31 32 33 34 35 36
    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 */
    INT    nHeight;    /* from child wnd's response to PGN_CALCSIZE */ 
    BOOL   bForward;   /* forward WM_MOUSEMOVE msgs to the contained wnd */
    INT    TLbtnState; /* state of top or left btn */
    INT    BRbtnState; /* state of bottom or right btn */
37
    INT    direction;  /* direction of the scroll, (e.g. PGF_SCROLLUP) */
38
} PAGER_INFO;
Alexandre Julliard's avatar
Alexandre Julliard committed
39

40
#define PAGER_GetInfoPtr(hwnd) ((PAGER_INFO *)GetWindowLongA(hwnd, 0))
41
#define PAGER_IsHorizontal(hwnd) ((GetWindowLongA (hwnd, GWL_STYLE) & PGS_HORZ))
Alexandre Julliard's avatar
Alexandre Julliard committed
42

43 44 45
#define MIN_ARROW_WIDTH  8
#define MIN_ARROW_HEIGHT 5

46 47 48 49
#define TIMERID1         1
#define TIMERID2         2
#define INITIAL_DELAY    500
#define REPEAT_DELAY     50
50

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
/* the horizontal arrows are: 
 *
 * 01234    01234
 * 1  *      *
 * 2 **      **
 * 3***      ***
 * 4***      ***
 * 5 **      **
 * 6  *      *
 * 7  
 *
 */
static void
PAGER_DrawHorzArrow (HDC hdc, RECT r, INT colorRef, BOOL left)
{
    INT x, y, w, h;
    HPEN hOldPen;
    
    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 */

    hOldPen = SelectObject ( hdc, GetSysColorPen (colorRef));
    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 );
}

/* the vertical arrows are: 
 *
 * 01234567    01234567
 * 1******        **  
 * 2 ****        ****
 * 3  **        ******
 * 4
 *
 */
static void
PAGER_DrawVertArrow (HDC hdc, RECT r, INT colorRef, BOOL up)
{
    INT x, y, w, h;
    HPEN hOldPen;
    
    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 */

    hOldPen = SelectObject ( hdc, GetSysColorPen (colorRef));
    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 );
}

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

    if (!btnState) /* PGF_INVISIBLE */
        return;

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

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

    FillRect(hdc, &rc, hBrush);

    if (btnState == PGF_HOT) 
    {
168
       DrawEdge( hdc, &rc, BDR_RAISEDINNER, BF_RECT);
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
       if (horz)
           PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
       else
           PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
    }
    else if (btnState == PGF_NORMAL) 
    {
       DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
       if (horz)
           PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
       else
           PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
    }
    else if (btnState == PGF_DEPRESSED) 
    {
       DrawEdge( hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
       if (horz)
           PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
       else
           PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
    }
    else if (btnState == PGF_GRAYED) 
    {
       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
212

Patrik Stridvall's avatar
Patrik Stridvall committed
213
static inline LRESULT
214
PAGER_ForwardMouse (HWND hwnd, WPARAM wParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
215
{
216
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
217
    TRACE("[%04x]\n", hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
218

219
    infoPtr->bForward = (BOOL)wParam;
Alexandre Julliard's avatar
Alexandre Julliard committed
220 221 222 223

    return 0;
}

Patrik Stridvall's avatar
Patrik Stridvall committed
224
static inline LRESULT
225
PAGER_GetButtonState (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
226
{
227 228 229 230
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); 
    LRESULT btnState = PGF_INVISIBLE;
    INT btn = (INT)lParam;
    TRACE("[%04x]\n", hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
231

232 233 234 235 236 237
    if (btn == PGB_TOPORLEFT)
        btnState = infoPtr->TLbtnState;
    else if (btn == PGB_BOTTOMORRIGHT)
        btnState = infoPtr->BRbtnState;

    return btnState;
Alexandre Julliard's avatar
Alexandre Julliard committed
238 239 240
}


Patrik Stridvall's avatar
Patrik Stridvall committed
241
static inline LRESULT
242
PAGER_GetPos(HWND hwnd)
Alexandre Julliard's avatar
Alexandre Julliard committed
243
{
244 245 246 247
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); 
    TRACE("[%04x] returns %d\n", hwnd, infoPtr->nPos);
    return (LRESULT)infoPtr->nPos;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
248

249 250 251 252 253 254
static inline LRESULT
PAGER_GetButtonSize(HWND hwnd)
{
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); 
    TRACE("[%04x] returns %d\n", hwnd, infoPtr->nButtonSize);
    return (LRESULT)infoPtr->nButtonSize;
Alexandre Julliard's avatar
Alexandre Julliard committed
255 256
}

257 258 259 260 261 262 263
static inline LRESULT
PAGER_GetBorder(HWND hwnd)
{
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); 
    TRACE("[%04x] returns %d\n", hwnd, infoPtr->nBorder);
    return (LRESULT)infoPtr->nBorder;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
264

Patrik Stridvall's avatar
Patrik Stridvall committed
265
static inline LRESULT
266
PAGER_GetBkColor(HWND hwnd)
Alexandre Julliard's avatar
Alexandre Julliard committed
267
{
268 269 270 271
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); 
    TRACE("[%04x] returns %06lx\n", hwnd, infoPtr->clrBk);
    return (LRESULT)infoPtr->clrBk;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
272

273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
static void 
PAGER_CalcSize (HWND hwnd, INT* size, BOOL getWidth) 
{
    NMPGCALCSIZE nmpgcs;
    ZeroMemory (&nmpgcs, sizeof (NMPGCALCSIZE));
    nmpgcs.hdr.hwndFrom = hwnd;
    nmpgcs.hdr.idFrom   = GetWindowLongA (hwnd, GWL_ID);
    nmpgcs.hdr.code = PGN_CALCSIZE;
    nmpgcs.dwFlag = getWidth ? PGF_CALCWIDTH : PGF_CALCHEIGHT;
    nmpgcs.iWidth = getWidth ? *size : 0;
    nmpgcs.iHeight = getWidth ? 0 : *size;
    SendMessageA (hwnd, WM_NOTIFY,
                  (WPARAM)nmpgcs.hdr.idFrom, (LPARAM)&nmpgcs);

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

    TRACE("[%04x] PGN_CALCSIZE returns %s=%d\n", hwnd,
                  getWidth ? "width" : "height", *size);
Alexandre Julliard's avatar
Alexandre Julliard committed
291 292
}

293 294 295 296 297
static void
PAGER_PositionChildWnd(HWND hwnd, PAGER_INFO* infoPtr)
{
    if (infoPtr->hwndChild)
    {
298
        RECT rcClient;
299 300 301 302 303 304
        int nPos = infoPtr->nPos;

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

305 306 307
        GetClientRect(hwnd, &rcClient);

        if (PAGER_IsHorizontal(hwnd))
308
        {
309 310 311 312
            int wndSize = max(0, rcClient.right - rcClient.left);
            if (infoPtr->nWidth < wndSize)
                infoPtr->nWidth = wndSize;

313 314 315 316 317 318
            TRACE("[%04x] SWP %dx%d at (%d,%d)\n", hwnd,
                         infoPtr->nWidth, infoPtr->nHeight,
                         -nPos, 0);
            SetWindowPos(infoPtr->hwndChild, 0,
                         -nPos, 0,
                         infoPtr->nWidth, infoPtr->nHeight,
319
                         SWP_NOZORDER);
320 321 322
        }
        else
        {
323 324 325 326
            int wndSize = max(0, rcClient.bottom - rcClient.top);
            if (infoPtr->nHeight < wndSize)
                infoPtr->nHeight = wndSize;

327
            TRACE("[%04x] SWP %dx%d at (%d,%d)\n", hwnd, 
328
                         infoPtr->nWidth, infoPtr->nHeight,
329 330 331 332 333 334 335
                         0, -nPos);
            SetWindowPos(infoPtr->hwndChild, 0,
                         0, -nPos,
                         infoPtr->nWidth, infoPtr->nHeight,
                         SWP_NOZORDER);
        }

336
        InvalidateRect(infoPtr->hwndChild, NULL, TRUE);
337 338
    }
}
Alexandre Julliard's avatar
Alexandre Julliard committed
339

340 341
static INT
PAGER_GetScrollRange(HWND hwnd, PAGER_INFO* infoPtr)
Alexandre Julliard's avatar
Alexandre Julliard committed
342
{
343
    INT scrollRange = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
344

345 346 347 348 349 350
    if (infoPtr->hwndChild)
    {
        INT wndSize, childSize;
        RECT wndRect;
        GetWindowRect(hwnd, &wndRect);

351
        if (PAGER_IsHorizontal(hwnd))
352 353 354 355 356 357 358 359 360 361 362 363
        {
            wndSize = wndRect.right - wndRect.left;
            PAGER_CalcSize(hwnd, &infoPtr->nWidth, TRUE);
            childSize = infoPtr->nWidth;
        }
        else
        {
            wndSize = wndRect.bottom - wndRect.top;
            PAGER_CalcSize(hwnd, &infoPtr->nHeight, FALSE);
            childSize = infoPtr->nHeight;
        }

364
        TRACE("childSize = %d,  wndSize = %d\n", childSize, wndSize);
365 366 367
        if (childSize > wndSize)
            scrollRange = childSize - wndSize + infoPtr->nButtonSize;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
368

369 370
    TRACE("[%04x] returns %d\n", hwnd, scrollRange);
    return scrollRange;
Alexandre Julliard's avatar
Alexandre Julliard committed
371 372
}

373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
static void 
PAGER_GrayAndRestoreBtns(PAGER_INFO* infoPtr, INT scrollRange,
                         BOOL* needsResize, BOOL* needsRepaint)
{
    if (infoPtr->nPos > 0)
    {
        *needsResize |= !infoPtr->TLbtnState; /* PGF_INVISIBLE */
        if (infoPtr->TLbtnState != PGF_DEPRESSED)
            infoPtr->TLbtnState = PGF_NORMAL;
    }
    else
    {
        *needsRepaint |= (infoPtr->TLbtnState != PGF_GRAYED);
        infoPtr->TLbtnState = PGF_GRAYED;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
388

389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
    if (scrollRange <= 0)
    {
        *needsRepaint |= (infoPtr->TLbtnState != PGF_GRAYED);
        infoPtr->TLbtnState = PGF_GRAYED;
        *needsRepaint |= (infoPtr->BRbtnState != PGF_GRAYED);
        infoPtr->BRbtnState = PGF_GRAYED;
    }
    else if (infoPtr->nPos < scrollRange)
    {
        *needsResize |= !infoPtr->BRbtnState; /* PGF_INVISIBLE */
        if (infoPtr->BRbtnState != PGF_DEPRESSED)
            infoPtr->BRbtnState = PGF_NORMAL;
    }
    else
    {
        *needsRepaint |= (infoPtr->BRbtnState != PGF_GRAYED);
        infoPtr->BRbtnState = PGF_GRAYED;
    }
}
Alexandre Julliard's avatar
Alexandre Julliard committed
408 409


410 411
static void 
PAGER_NormalizeBtns(PAGER_INFO* infoPtr, BOOL* needsRepaint)
Alexandre Julliard's avatar
Alexandre Julliard committed
412
{
413 414 415 416 417
    if (infoPtr->TLbtnState & (PGF_HOT | PGF_DEPRESSED))
    {
        infoPtr->TLbtnState = PGF_NORMAL;
        *needsRepaint = TRUE;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
418

419 420 421 422 423
    if (infoPtr->BRbtnState & (PGF_HOT | PGF_DEPRESSED))
    {
        infoPtr->BRbtnState = PGF_NORMAL;
        *needsRepaint = TRUE;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
424 425
}

426 427 428 429 430 431 432 433
static void 
PAGER_HideGrayBtns(PAGER_INFO* infoPtr, BOOL* needsResize)
{
    if (infoPtr->TLbtnState == PGF_GRAYED)
    {
        infoPtr->TLbtnState = PGF_INVISIBLE;
        *needsResize = TRUE;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
434

435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
    if (infoPtr->BRbtnState == PGF_GRAYED)
    {
        infoPtr->BRbtnState = PGF_INVISIBLE;
        *needsResize = TRUE;
    }
}

static void
PAGER_UpdateBtns(HWND hwnd, PAGER_INFO *infoPtr,
                 INT scrollRange, BOOL hideGrayBtns)
{
    BOOL resizeClient = FALSE;
    BOOL repaintBtns = FALSE;

    if (scrollRange < 0)
        PAGER_NormalizeBtns(infoPtr, &repaintBtns);
    else
        PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);

    if (hideGrayBtns)
        PAGER_HideGrayBtns(infoPtr, &resizeClient);

    if (resizeClient) /* initiate NCCalcSize to resize client wnd */
        SetWindowPos(hwnd, 0,0,0,0,0, 
                     SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
                     SWP_NOZORDER | SWP_NOACTIVATE);

    if (repaintBtns)
        SendMessageA(hwnd, WM_NCPAINT, 0, 0); 
}

static LRESULT  
PAGER_SetPos(HWND hwnd, INT newPos, BOOL fromBtnPress)
Alexandre Julliard's avatar
Alexandre Julliard committed
468
{
469
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
470
    INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr);
471
    INT oldPos = infoPtr->nPos;
472

473 474
    if ((scrollRange <= 0) || (newPos < 0))
        infoPtr->nPos = 0;
475
    else if (newPos > scrollRange)
476 477
        infoPtr->nPos = scrollRange;
    else
478
        infoPtr->nPos = newPos;
Alexandre Julliard's avatar
Alexandre Julliard committed
479

480 481
    TRACE("[%04x] pos=%d\n", hwnd, infoPtr->nPos);

482 483 484 485 486 487
    if (infoPtr->nPos != oldPos)
    {
        /* gray and restore btns, and if from WM_SETPOS, hide the gray btns */
        PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, !fromBtnPress);
        PAGER_PositionChildWnd(hwnd, infoPtr);
    }
488 489 490

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

492 493 494 495 496 497 498 499 500 501 502
static LRESULT
PAGER_HandleWindowPosChanging(HWND hwnd, WINDOWPOS *winpos)
{
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);

    if (infoPtr->bNoResize && !(winpos->flags & SWP_NOSIZE))
    {
        /* 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) */

503
        if (PAGER_IsHorizontal(hwnd))
504 505 506
            winpos->cy = infoPtr->nHeight;
        else
            winpos->cx = infoPtr->nWidth;
507
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
508

509 510
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
511

512 513 514 515 516 517 518 519
static void 
PAGER_SetFixedWidth(HWND hwnd, PAGER_INFO* infoPtr)
{
  /* 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;
520
    INT delta, h;
521 522 523 524 525 526 527 528 529 530 531 532 533 534
    GetWindowRect(hwnd, &wndRect);

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

    if (infoPtr->bNoResize)
    {
        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;
    }

535
    h = wndRect.bottom - wndRect.top + infoPtr->nButtonSize;
536 537 538 539 540

    /* adjust non-scrollable dimension to fit the child */
    SetWindowPos(hwnd, 0, 0,0, infoPtr->nWidth, h, 
                 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER);

541 542 543 544 545 546 547 548 549 550 551 552 553

    TRACE("[%04x] infoPtr->nWidth set to %d\n",
	       hwnd, infoPtr->nWidth);
}

static void 
PAGER_SetFixedHeight(HWND hwnd, PAGER_INFO* infoPtr)
{
  /* 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;
554
    INT delta, w;
555 556 557 558 559 560 561 562 563
    GetWindowRect(hwnd, &wndRect);

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

    if (infoPtr->bNoResize)
    {
        delta = wndRect.bottom - wndRect.top - infoPtr->nHeight;
        if (delta > infoPtr->nButtonSize)
564
            infoPtr->nHeight += infoPtr->nButtonSize;
565 566 567 568
        else if (delta > 0)
            infoPtr->nHeight +=  infoPtr->nButtonSize / 3;
    }

569
    w = wndRect.right - wndRect.left + infoPtr->nButtonSize;
570 571 572 573

    /* adjust non-scrollable dimension to fit the child */
    SetWindowPos(hwnd, 0, 0,0, w, infoPtr->nHeight, 
                 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER);
574 575 576 577 578

    TRACE("[%04x] infoPtr->nHeight set to %d\n",
	       hwnd, infoPtr->nHeight);
}

579 580 581 582
static LRESULT
PAGER_RecalcSize(HWND hwnd)
{
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
583

584
    TRACE("[%04x]\n", hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
585

586
    if (infoPtr->hwndChild)
587
    {
588 589 590
        INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr);

        if (scrollRange <= 0)
591 592
        {
            infoPtr->nPos = -1;
593
            PAGER_SetPos(hwnd, 0, FALSE);
594
        }
595 596 597 598 599
        else 
        {
            PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, TRUE);
            PAGER_PositionChildWnd(hwnd, infoPtr);
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
600
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
601

602
    return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
603
}
Alexandre Julliard's avatar
Alexandre Julliard committed
604 605


606
static LRESULT
607
PAGER_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
608
{
609
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
610 611 612
    COLORREF clrTemp = infoPtr->clrBk;

    infoPtr->clrBk = (COLORREF)lParam;
613
    TRACE("[%04x] %06lx\n", hwnd, infoPtr->clrBk);
Alexandre Julliard's avatar
Alexandre Julliard committed
614

615
    PAGER_RecalcSize(hwnd);
616
    SendMessageA(hwnd, WM_NCPAINT, (WPARAM)0, (LPARAM)0);
Alexandre Julliard's avatar
Alexandre Julliard committed
617 618 619 620 621

    return (LRESULT)clrTemp;
}


622
static LRESULT
623
PAGER_SetBorder (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
624
{
625
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
626
    INT nTemp = infoPtr->nBorder;
Alexandre Julliard's avatar
Alexandre Julliard committed
627

628
    infoPtr->nBorder = (INT)lParam;
629
    TRACE("[%04x] %d\n", hwnd, infoPtr->nBorder);
Alexandre Julliard's avatar
Alexandre Julliard committed
630

631
    PAGER_RecalcSize(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
632 633 634 635 636

    return (LRESULT)nTemp;
}


637
static LRESULT
638
PAGER_SetButtonSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
639
{
640
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
641
    INT nTemp = infoPtr->nButtonSize;
Alexandre Julliard's avatar
Alexandre Julliard committed
642

643
    infoPtr->nButtonSize = (INT)lParam;
644
    TRACE("[%04x] %d\n", hwnd, infoPtr->nButtonSize);
Alexandre Julliard's avatar
Alexandre Julliard committed
645

646
    PAGER_RecalcSize(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
647 648 649 650 651

    return (LRESULT)nTemp;
}


652
static LRESULT
653
PAGER_SetChild (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
654
{
655
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
656

657
    infoPtr->hwndChild = IsWindow ((HWND)lParam) ? (HWND)lParam : 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
658

659 660 661 662
    if (infoPtr->hwndChild)
    {
        TRACE("[%04x] hwndChild=%04x\n", hwnd, infoPtr->hwndChild);

663
        if (PAGER_IsHorizontal(hwnd))
664 665
            PAGER_SetFixedHeight(hwnd, infoPtr);
        else
666
            PAGER_SetFixedWidth(hwnd, infoPtr);
667 668 669 670 671 672

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

673
        infoPtr->nPos = -1;
674
        PAGER_SetPos(hwnd, 0, FALSE);
675
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
676 677 678 679

    return 0;
}

680 681
static void
PAGER_Scroll(HWND hwnd, INT dir)
Alexandre Julliard's avatar
Alexandre Julliard committed
682
{
683
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
684
    NMPGSCROLL nmpgScroll;
685
    RECT rcWnd;
Alexandre Julliard's avatar
Alexandre Julliard committed
686

687 688 689 690 691 692 693
    if (infoPtr->hwndChild)
    {
        ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL));
        nmpgScroll.hdr.hwndFrom = hwnd;
        nmpgScroll.hdr.idFrom   = GetWindowLongA (hwnd, GWL_ID);
        nmpgScroll.hdr.code = PGN_SCROLL;

694
        GetWindowRect(hwnd, &rcWnd);  
695 696 697 698
        GetClientRect(hwnd, &nmpgScroll.rcParent);  
        nmpgScroll.iXpos = nmpgScroll.iYpos = 0;
        nmpgScroll.iDir = dir;

699 700
        if (PAGER_IsHorizontal(hwnd))
        {
701
            nmpgScroll.iScroll = rcWnd.right - rcWnd.left;
702
            nmpgScroll.iXpos = infoPtr->nPos;
703
        }
704
        else
705
        {
706
            nmpgScroll.iScroll = rcWnd.bottom - rcWnd.top;
707
            nmpgScroll.iYpos = infoPtr->nPos;
708
        }
709
        nmpgScroll.iScroll -= 2*infoPtr->nButtonSize;
710 711
  
        SendMessageA (hwnd, WM_NOTIFY,
712
                    (WPARAM)nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll);
713 714
  
        TRACE("[%04x] PGN_SCROLL returns iScroll=%d\n", hwnd, nmpgScroll.iScroll);
715

716
        if (nmpgScroll.iScroll > 0)
717
        {
718 719
            infoPtr->direction = dir;

720 721 722 723
            if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP)
                PAGER_SetPos(hwnd, infoPtr->nPos - nmpgScroll.iScroll, TRUE);
            else
                PAGER_SetPos(hwnd, infoPtr->nPos + nmpgScroll.iScroll, TRUE);
724
        }
725 726
        else
            infoPtr->direction = -1;
727
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
728 729
}

Alexandre Julliard's avatar
Alexandre Julliard committed
730
static LRESULT
731
PAGER_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
732 733
{
    PAGER_INFO *infoPtr;
734
    DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
735

Alexandre Julliard's avatar
Alexandre Julliard committed
736
    /* allocate memory for info structure */
Alexandre Julliard's avatar
Alexandre Julliard committed
737
    infoPtr = (PAGER_INFO *)COMCTL32_Alloc (sizeof(PAGER_INFO));
738
    SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
Alexandre Julliard's avatar
Alexandre Julliard committed
739 740

    /* set default settings */
741
    infoPtr->hwndChild = (HWND)NULL;
742
    infoPtr->bNoResize = dwStyle & CCS_NORESIZE;
743
    infoPtr->clrBk = GetSysColor(COLOR_BTNFACE);
Alexandre Julliard's avatar
Alexandre Julliard committed
744
    infoPtr->nBorder = 0;
745
    infoPtr->nButtonSize = 12;
Alexandre Julliard's avatar
Alexandre Julliard committed
746
    infoPtr->nPos = 0;
747 748 749 750 751
    infoPtr->nWidth = 0;
    infoPtr->nHeight = 0;
    infoPtr->bForward = FALSE;
    infoPtr->TLbtnState = PGF_INVISIBLE;
    infoPtr->BRbtnState = PGF_INVISIBLE;
752
    infoPtr->direction = -1;
753 754 755 756 757

    if (dwStyle & PGS_AUTOSCROLL)
        FIXME("[%04x] Autoscroll style is not implemented yet.\n", hwnd);
    if (dwStyle & PGS_DRAGNDROP)
        FIXME("[%04x] Drag and Drop style is not implemented yet.\n", hwnd);
758 759 760 761 762 763
    /*
	 * If neither horizontal nor vertical style specified, default to vertical.
	 * This is probably not necessary, since the style may be set later on as
	 * the control is initialized, but just in case it isn't, set it here.
	 */
    if (!(dwStyle & PGS_HORZ) && !(dwStyle & PGS_VERT))
764
    {
765
        dwStyle |= PGS_VERT;
766 767
        SetWindowLongA(hwnd, GWL_STYLE, dwStyle);
    }
768

Alexandre Julliard's avatar
Alexandre Julliard committed
769 770 771 772 773
    return 0;
}


static LRESULT
774
PAGER_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
775
{
776
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
777 778 779 780 781
    /* free pager info data */
    COMCTL32_Free (infoPtr);
    SetWindowLongA (hwnd, 0, 0);
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
782

783 784 785
static LRESULT
PAGER_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
786 787
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
    LPRECT lpRect = (LPRECT)lParam;
788 789 790 791 792 793
    /*
     * lParam points to a RECT struct.  On entry, the struct
     * contains the proposed wnd rectangle for the window. 
     * On exit, the struct should contain the screen
     * coordinates of the corresponding window's client area.
     */
794
	
795
    if (PAGER_IsHorizontal(hwnd))
796 797 798 799 800 801 802 803 804 805 806 807 808
    {
        if (infoPtr->TLbtnState) /* != PGF_INVISIBLE */
            lpRect->left += infoPtr->nButtonSize;
        if (infoPtr->BRbtnState) 
            lpRect->right -= infoPtr->nButtonSize;
    }
    else
    {
        if (infoPtr->TLbtnState)
            lpRect->top += infoPtr->nButtonSize;
        if (infoPtr->BRbtnState)
            lpRect->bottom -= infoPtr->nButtonSize;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
809

810 811 812 813
    TRACE("[%04x] client rect set to %dx%d at (%d,%d)\n", hwnd,
                lpRect->right-lpRect->left,
                lpRect->bottom-lpRect->top,
                lpRect->left, lpRect->top);
Alexandre Julliard's avatar
Alexandre Julliard committed
814

815 816
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
817

818 819 820 821 822 823 824
static LRESULT
PAGER_NCPaint (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    PAGER_INFO* infoPtr = PAGER_GetInfoPtr(hwnd);
    DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
    RECT rcWindow, rcBottomRight, rcTopLeft;
    HDC hdc;
825
    BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
826 827 828 829 830 831 832 833 834 835 836 837 838

    if (dwStyle & WS_MINIMIZE)
        return 0;

    DefWindowProcA (hwnd, WM_NCPAINT, wParam, lParam);

    if (!(hdc = GetDCEx (hwnd, 0, DCX_USESTYLE | DCX_WINDOW)))
        return 0;

    GetWindowRect (hwnd, &rcWindow);
    OffsetRect (&rcWindow, -rcWindow.left, -rcWindow.top);

    rcTopLeft = rcBottomRight = rcWindow;
839
    if (bHorizontal)
840 841 842 843 844 845 846 847 848 849 850
    {
        rcTopLeft.right = rcTopLeft.left + infoPtr->nButtonSize;
        rcBottomRight.left = rcBottomRight.right - infoPtr->nButtonSize;
    }
    else
    {
        rcTopLeft.bottom = rcTopLeft.top + infoPtr->nButtonSize;
        rcBottomRight.top = rcBottomRight.bottom - infoPtr->nButtonSize;
    }

    PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft,
851
                     bHorizontal, TRUE, infoPtr->TLbtnState);
852
    PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight,
853
                     bHorizontal, FALSE, infoPtr->BRbtnState); 
854 855

    ReleaseDC( hwnd, hdc );
Alexandre Julliard's avatar
Alexandre Julliard committed
856 857 858
    return 0;
}

859 860 861 862 863
static INT 
PAGER_HitTest (HWND hwnd, LPPOINT pt)
{
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
    RECT clientRect;
864
    BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
865 866 867 868 869 870 871 872 873 874 875

    GetClientRect (hwnd, &clientRect);

    if (PtInRect(&clientRect, *pt))
    {
       /* TRACE("HTCLIENT\n"); */
        return HTCLIENT;
    }

    if (infoPtr->TLbtnState && infoPtr->TLbtnState != PGF_GRAYED)
    {
876
        if (bHorizontal)
877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
        {
            if (pt->x < clientRect.left)
            {
                /* TRACE("HTLEFT\n"); */
                return HTLEFT;
            }
        }
        else
        {
            if (pt->y < clientRect.top)
            {
                /* TRACE("HTTOP\n"); */
                return HTTOP;
            }
        }
    }

    if (infoPtr->BRbtnState && infoPtr->BRbtnState != PGF_GRAYED)
    {
896
        if (bHorizontal)
897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924
        {
            if (pt->x > clientRect.right)
            {
                /* TRACE("HTRIGHT\n"); */
                return HTRIGHT;
            }
        }
        else
        {
            if (pt->y > clientRect.bottom)
            {
               /* TRACE("HTBOTTOM\n"); */
                return HTBOTTOM;
            }
        }
    }

    /* TRACE("HTNOWHERE\n"); */
    return HTNOWHERE;
}

static LRESULT
PAGER_NCHitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
    ScreenToClient (hwnd, &pt);
    return PAGER_HitTest(hwnd, &pt);
}
Alexandre Julliard's avatar
Alexandre Julliard committed
925 926

static LRESULT
927
PAGER_SetCursor( HWND hwnd, WPARAM wParam, LPARAM lParam )
Alexandre Julliard's avatar
Alexandre Julliard committed
928
{
929
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
930
    BOOL notCaptured = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
931

932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953
    switch(LOWORD(lParam))
    {
        case HTLEFT:
        case HTTOP:
            if ((notCaptured = infoPtr->TLbtnState != PGF_HOT))
                infoPtr->TLbtnState = PGF_HOT;
            break;
        case HTRIGHT:
        case HTBOTTOM:
            if ((notCaptured = infoPtr->BRbtnState != PGF_HOT))
               infoPtr->BRbtnState = PGF_HOT;
            break;
        default:
            return FALSE;
    }

    if (notCaptured)
    {
        TRACKMOUSEEVENT trackinfo;

        TRACE("[%04x] SetCapture\n", hwnd);
        SetCapture(hwnd);
954

955 956 957 958 959 960 961 962
        trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
        trackinfo.dwFlags = TME_QUERY;
        trackinfo.hwndTrack = hwnd;
        trackinfo.dwHoverTime = HOVER_DEFAULT;

        /* call _TrackMouseEvent to see if we are currently tracking for this hwnd */
        _TrackMouseEvent(&trackinfo);

963
        /* Make sure tracking is enabled so we receive a WM_MOUSELEAVE message */
964 965 966
        if(!(trackinfo.dwFlags & TME_LEAVE)) {
            trackinfo.dwFlags = TME_LEAVE; /* notify upon leaving */
 
967
           /* call TRACKMOUSEEVENT so we receive a WM_MOUSELEAVE message */
968 969 970 971 972 973 974 975
           /* and can properly deactivate the hot button */
           _TrackMouseEvent(&trackinfo);
        }

        SendMessageA(hwnd, WM_NCPAINT, 0, 0); 
    }

    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
976 977
}

978 979 980 981 982
static LRESULT
PAGER_MouseLeave (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);

983 984 985
    KillTimer (hwnd, TIMERID1);
    KillTimer (hwnd, TIMERID2);

986 987 988 989 990 991 992 993 994 995
    TRACE("[%04x] ReleaseCapture\n", hwnd);
    ReleaseCapture();

    /* Notify parent of released mouse capture */
    {
        NMHDR nmhdr;
        ZeroMemory (&nmhdr, sizeof (NMHDR));
        nmhdr.hwndFrom = hwnd;
        nmhdr.idFrom   = GetWindowLongA (hwnd, GWL_ID);
        nmhdr.code = NM_RELEASEDCAPTURE;
996
        SendMessageA (GetParent(hwnd), WM_NOTIFY,
997 998 999 1000 1001 1002 1003 1004
                        (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
    }

    /* make HOT btns NORMAL and hide gray btns */
    PAGER_UpdateBtns(hwnd, infoPtr, -1, TRUE);

    return TRUE;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1005

Eric Kohl's avatar
Eric Kohl committed
1006
static LRESULT
1007
PAGER_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
Eric Kohl's avatar
Eric Kohl committed
1008
{
1009 1010 1011 1012
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
    BOOL repaintBtns = FALSE;
    POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
    INT hit;
Eric Kohl's avatar
Eric Kohl committed
1013

1014 1015 1016 1017 1018 1019 1020 1021 1022
    TRACE("[%04x]\n", hwnd);
	
    hit = PAGER_HitTest(hwnd, &pt);

    /* put btn in DEPRESSED state */
    if (hit == HTLEFT || hit == HTTOP)
    {
        repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED;
        infoPtr->TLbtnState = PGF_DEPRESSED;
1023
        SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0); 
1024 1025 1026 1027 1028
    }
    else if (hit == HTRIGHT || hit == HTBOTTOM)
    {
        repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED;
        infoPtr->BRbtnState = PGF_DEPRESSED;
1029
        SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0); 
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
    }

    if (repaintBtns)
        SendMessageA(hwnd, WM_NCPAINT, 0, 0); 

    switch(hit)
    {
    case HTLEFT:
        TRACE("[%04x] PGF_SCROLLLEFT\n", hwnd);
        PAGER_Scroll(hwnd, PGF_SCROLLLEFT);
        break;
    case HTTOP:
        TRACE("[%04x] PGF_SCROLLUP\n", hwnd);
        PAGER_Scroll(hwnd, PGF_SCROLLUP);
        break;
    case HTRIGHT:
        TRACE("[%04x] PGF_SCROLLRIGHT\n", hwnd);
        PAGER_Scroll(hwnd, PGF_SCROLLRIGHT);
        break;
    case HTBOTTOM:
        TRACE("[%04x] PGF_SCROLLDOWN\n", hwnd);
        PAGER_Scroll(hwnd, PGF_SCROLLDOWN);
        break;
    default:
        break;
    }

    return TRUE;
Eric Kohl's avatar
Eric Kohl committed
1058 1059
}

1060 1061 1062 1063 1064
static LRESULT
PAGER_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
{
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
    TRACE("[%04x]\n", hwnd);
Eric Kohl's avatar
Eric Kohl committed
1065

1066 1067 1068
    KillTimer (hwnd, TIMERID1);
    KillTimer (hwnd, TIMERID2);

1069 1070
    /* make PRESSED btns NORMAL but don't hide gray btns */
    PAGER_UpdateBtns(hwnd, infoPtr, -1, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1071

1072 1073
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1074

1075
static LRESULT
1076
PAGER_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
1077
{
1078
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1079
    HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
1080
    RECT rect;
1081

1082
    GetClientRect (hwnd, &rect);
1083
    FillRect ((HDC)wParam, &rect, hBrush);
1084 1085 1086 1087 1088 1089 1090 1091

    /* background color of the child should be the same as the pager */
    if (infoPtr->hwndChild)
    {
        GetClientRect (infoPtr->hwndChild, &rect);
        FillRect ((HDC)wParam, &rect, hBrush);
    }

1092
    DeleteObject (hBrush);
1093 1094 1095 1096
    return TRUE;
}


1097
static LRESULT
1098
PAGER_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1099 1100 1101 1102
{
    /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */

    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1103
    TRACE("[%04x] %dx%d\n", hwnd, LOWORD(lParam), HIWORD(lParam));
1104

1105
    if (PAGER_IsHorizontal(hwnd))
1106 1107 1108
        infoPtr->nHeight = HIWORD(lParam);
    else
        infoPtr->nWidth = LOWORD(lParam);
1109

1110
    return PAGER_RecalcSize(hwnd);
1111 1112
}

1113

1114
static LRESULT WINAPI
1115
PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
1116
{
1117 1118 1119
    PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);

    if (!infoPtr && (uMsg != WM_CREATE))
1120
	return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1121

Alexandre Julliard's avatar
Alexandre Julliard committed
1122 1123
    switch (uMsg)
    {
1124 1125 1126 1127 1128
        case PGM_FORWARDMOUSE:
            return PAGER_ForwardMouse (hwnd, wParam);

        case PGM_GETBKCOLOR:
            return PAGER_GetBkColor(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
1129

1130 1131
        case PGM_GETBORDER:
            return PAGER_GetBorder(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
1132

1133 1134
        case PGM_GETBUTTONSIZE:
            return PAGER_GetButtonSize(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
1135

1136 1137
        case PGM_GETPOS:
            return PAGER_GetPos(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
1138

1139 1140
        case PGM_GETBUTTONSTATE:
            return PAGER_GetButtonState (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1141

1142
/*      case PGM_GETDROPTARGET: */
Alexandre Julliard's avatar
Alexandre Julliard committed
1143

1144 1145 1146 1147 1148
        case PGM_RECALCSIZE:
            return PAGER_RecalcSize(hwnd);
    
        case PGM_SETBKCOLOR:
            return PAGER_SetBkColor (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1149

1150 1151
        case PGM_SETBORDER:
            return PAGER_SetBorder (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1152

1153 1154
        case PGM_SETBUTTONSIZE:
            return PAGER_SetButtonSize (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1155

1156 1157
        case PGM_SETCHILD:
            return PAGER_SetChild (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1158

1159 1160
        case PGM_SETPOS:
            return PAGER_SetPos(hwnd, (INT)lParam, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1161

1162 1163
        case WM_CREATE:
            return PAGER_Create (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1164

1165 1166
        case WM_DESTROY:
            return PAGER_Destroy (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1167

1168
        case WM_SIZE:
1169
            return PAGER_Size (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1170

1171 1172
        case WM_NCPAINT:
            return PAGER_NCPaint (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1173

1174 1175 1176
        case WM_WINDOWPOSCHANGING:
            return PAGER_HandleWindowPosChanging (hwnd, (WINDOWPOS*)lParam);

1177 1178
        case WM_NCCALCSIZE:
            return PAGER_NCCalcSize (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1179

1180 1181
        case WM_NCHITTEST:
            return PAGER_NCHitTest (hwnd, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1182

1183 1184 1185 1186 1187 1188 1189
        case WM_SETCURSOR:
        {
            if (hwnd == (HWND)wParam)
                return PAGER_SetCursor(hwnd, wParam, lParam);
            else /* its for the child */
                return 0;
        }
1190

1191 1192 1193 1194
        case WM_MOUSEMOVE:
            if (infoPtr->bForward && infoPtr->hwndChild)
                PostMessageA(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam);
            return TRUE;			
Alexandre Julliard's avatar
Alexandre Julliard committed
1195

1196 1197
        case WM_MOUSELEAVE:
            return PAGER_MouseLeave (hwnd, wParam, lParam);	
Alexandre Julliard's avatar
Alexandre Julliard committed
1198

1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210
        case WM_LBUTTONDOWN:
            return PAGER_LButtonDown (hwnd, wParam, lParam);

        case WM_LBUTTONUP:
            return PAGER_LButtonUp (hwnd, wParam, lParam);

        case WM_ERASEBKGND:
            return PAGER_EraseBackground (hwnd, wParam, lParam);
/*
        case WM_PAINT:
            return PAGER_Paint (hwnd, wParam); 
*/
1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226
        case WM_TIMER:
            /* if initial timer, kill it and start the repeat timer */
            if (wParam == TIMERID1)
            {
                KillTimer(hwnd, TIMERID1);
                SetTimer(hwnd, TIMERID2, REPEAT_DELAY, 0);
            }

            KillTimer(hwnd, TIMERID2);
            if (infoPtr->direction > 0)
            {
                PAGER_Scroll(hwnd, infoPtr->direction);
                SetTimer(hwnd, TIMERID2, REPEAT_DELAY, 0);	    
            }
            break;

1227 1228 1229 1230 1231 1232
        case WM_NOTIFY:
        case WM_COMMAND:
            return SendMessageA (GetParent (hwnd), uMsg, wParam, lParam);

        default:
            return DefWindowProcA (hwnd, uMsg, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1233
    }
1234

Alexandre Julliard's avatar
Alexandre Julliard committed
1235 1236 1237 1238
    return 0;
}


1239
VOID
1240
PAGER_Register (void)
Alexandre Julliard's avatar
Alexandre Julliard committed
1241
{
1242
    WNDCLASSA wndClass;
Alexandre Julliard's avatar
Alexandre Julliard committed
1243

1244
    ZeroMemory (&wndClass, sizeof(WNDCLASSA));
Alexandre Julliard's avatar
Alexandre Julliard committed
1245
    wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS;
1246
    wndClass.lpfnWndProc   = (WNDPROC)PAGER_WindowProc;
Alexandre Julliard's avatar
Alexandre Julliard committed
1247 1248
    wndClass.cbClsExtra    = 0;
    wndClass.cbWndExtra    = sizeof(PAGER_INFO *);
1249
    wndClass.hCursor       = LoadCursorA (0, IDC_ARROWA);
Alexandre Julliard's avatar
Alexandre Julliard committed
1250
    wndClass.hbrBackground = 0;
1251
    wndClass.lpszClassName = WC_PAGESCROLLERA;
Alexandre Julliard's avatar
Alexandre Julliard committed
1252
 
1253
    RegisterClassA (&wndClass);
Alexandre Julliard's avatar
Alexandre Julliard committed
1254 1255
}

1256 1257

VOID
1258
PAGER_Unregister (void)
1259
{
1260
    UnregisterClassA (WC_PAGESCROLLERA, (HINSTANCE)NULL);
1261 1262
}