Update the address of the Free Software Foundation.
[wine] / dlls / comctl32 / pager.c
1 /*
2  * Pager control
3  *
4  * Copyright 1998, 1999 Eric Kohl
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * NOTES
21  *
22  * This code was audited for completeness against the documented features
23  * of Comctl32.dll version 6.0 on Sep. 18, 2004, by Robert Shearman.
24  * 
25  * Unless otherwise noted, we believe this code to be complete, as per
26  * the specification mentioned above.
27  * If you discover missing features or bugs please note them below.
28  *
29  * TODO:
30  *    Implement repetitive button press.
31  *    Adjust arrow size relative to size of button.
32  *    Allow border size changes.
33  *    Styles:
34  *      PGS_DRAGNDROP
35  *    Notifications:
36  *      PGN_HOTITEMCHANGE
37  *    Messages:
38  *      WM_PRINT and/or WM_PRINTCLIENT
39  *
40  * TESTING:
41  *    Tested primarily with the controlspy Pager application.
42  *       Susan Farley (susan@codeweavers.com)
43  *
44  * IMPLEMENTATION NOTES:
45  *    This control uses WM_NCPAINT instead of WM_PAINT to paint itself
46  *    as we need to scroll a child window. In order to do this we move 
47  *    the child window in the control's client area, using the clipping
48  *    region that is automatically set around the client area. As the 
49  *    entire client area now consists of the child window, we must 
50  *    allocate space (WM_NCCALCSIZE) for the buttons and draw them as 
51  *    a non-client area (WM_NCPAINT).
52  *       Robert Shearman <rob@codeweavers.com>
53  */
54
55 #include <stdarg.h>
56 #include <string.h>
57 #include "windef.h"
58 #include "winbase.h"
59 #include "wingdi.h"
60 #include "winuser.h"
61 #include "winnls.h"
62 #include "commctrl.h"
63 #include "comctl32.h"
64 #include "wine/debug.h"
65
66 WINE_DEFAULT_DEBUG_CHANNEL(pager);
67
68 typedef struct
69 {
70     HWND   hwndSelf;   /* handle of the control wnd */
71     HWND   hwndChild;  /* handle of the contained wnd */
72     HWND   hwndNotify; /* handle of the parent wnd */
73     DWORD  dwStyle;    /* styles for this control */
74     COLORREF clrBk;    /* background color */
75     INT    nBorder;    /* border size for the control */
76     INT    nButtonSize;/* size of the pager btns */
77     INT    nPos;       /* scroll position */
78     INT    nWidth;     /* from child wnd's response to PGN_CALCSIZE */
79     INT    nHeight;    /* from child wnd's response to PGN_CALCSIZE */
80     BOOL   bForward;   /* forward WM_MOUSEMOVE msgs to the contained wnd */
81     BOOL   bCapture;   /* we have captured the mouse  */
82     INT    TLbtnState; /* state of top or left btn */
83     INT    BRbtnState; /* state of bottom or right btn */
84     INT    direction;  /* direction of the scroll, (e.g. PGF_SCROLLUP) */
85 } PAGER_INFO;
86
87 #define MIN_ARROW_WIDTH  8
88 #define MIN_ARROW_HEIGHT 5
89
90 #define TIMERID1         1
91 #define TIMERID2         2
92 #define INITIAL_DELAY    500
93 #define REPEAT_DELAY     50
94
95 static void
96 PAGER_GetButtonRects(PAGER_INFO* infoPtr, RECT* prcTopLeft, RECT* prcBottomRight, BOOL bClientCoords)
97 {
98     RECT rcWindow;
99     GetWindowRect (infoPtr->hwndSelf, &rcWindow);
100
101     if (bClientCoords)
102     {
103         POINT pt = {rcWindow.left, rcWindow.top};
104         ScreenToClient(infoPtr->hwndSelf, &pt);
105         OffsetRect(&rcWindow, -(rcWindow.left-pt.x), -(rcWindow.top-pt.y));
106     }
107     else
108         OffsetRect(&rcWindow, -rcWindow.left, -rcWindow.top);
109
110     *prcTopLeft = *prcBottomRight = rcWindow;
111     if (infoPtr->dwStyle & PGS_HORZ)
112     {
113         prcTopLeft->right = prcTopLeft->left + infoPtr->nButtonSize;
114         prcBottomRight->left = prcBottomRight->right - infoPtr->nButtonSize;
115     }
116     else
117     {
118         prcTopLeft->bottom = prcTopLeft->top + infoPtr->nButtonSize;
119         prcBottomRight->top = prcBottomRight->bottom - infoPtr->nButtonSize;
120     }
121 }
122
123 /* the horizontal arrows are:
124  *
125  * 01234    01234
126  * 1  *      *
127  * 2 **      **
128  * 3***      ***
129  * 4***      ***
130  * 5 **      **
131  * 6  *      *
132  * 7
133  *
134  */
135 static void
136 PAGER_DrawHorzArrow (HDC hdc, RECT r, INT colorRef, BOOL left)
137 {
138     INT x, y, w, h;
139     HPEN hPen, hOldPen;
140
141     w = r.right - r.left + 1;
142     h = r.bottom - r.top + 1;
143     if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
144         return;  /* refuse to draw partial arrow */
145
146     if (!(hPen = CreatePen( PS_SOLID, 1, GetSysColor( colorRef )))) return;
147     hOldPen = SelectObject ( hdc, hPen );
148     if (left)
149     {
150         x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 3;
151         y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
152         MoveToEx (hdc, x, y, NULL);
153         LineTo (hdc, x--, y+5); y++;
154         MoveToEx (hdc, x, y, NULL);
155         LineTo (hdc, x--, y+3); y++;
156         MoveToEx (hdc, x, y, NULL);
157         LineTo (hdc, x, y+1);
158     }
159     else
160     {
161         x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
162         y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
163         MoveToEx (hdc, x, y, NULL);
164         LineTo (hdc, x++, y+5); y++;
165         MoveToEx (hdc, x, y, NULL);
166         LineTo (hdc, x++, y+3); y++;
167         MoveToEx (hdc, x, y, NULL);
168         LineTo (hdc, x, y+1);
169     }
170
171     SelectObject( hdc, hOldPen );
172     DeleteObject( hPen );
173 }
174
175 /* the vertical arrows are:
176  *
177  * 01234567    01234567
178  * 1******        **
179  * 2 ****        ****
180  * 3  **        ******
181  * 4
182  *
183  */
184 static void
185 PAGER_DrawVertArrow (HDC hdc, RECT r, INT colorRef, BOOL up)
186 {
187     INT x, y, w, h;
188     HPEN hPen, hOldPen;
189
190     w = r.right - r.left + 1;
191     h = r.bottom - r.top + 1;
192     if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
193         return;  /* refuse to draw partial arrow */
194
195     if (!(hPen = CreatePen( PS_SOLID, 1, GetSysColor( colorRef )))) return;
196     hOldPen = SelectObject ( hdc, hPen );
197     if (up)
198     {
199         x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
200         y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 3;
201         MoveToEx (hdc, x, y, NULL);
202         LineTo (hdc, x+5, y--); x++;
203         MoveToEx (hdc, x, y, NULL);
204         LineTo (hdc, x+3, y--); x++;
205         MoveToEx (hdc, x, y, NULL);
206         LineTo (hdc, x+1, y);
207     }
208     else
209     {
210         x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
211         y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
212         MoveToEx (hdc, x, y, NULL);
213         LineTo (hdc, x+5, y++); x++;
214         MoveToEx (hdc, x, y, NULL);
215         LineTo (hdc, x+3, y++); x++;
216         MoveToEx (hdc, x, y, NULL);
217         LineTo (hdc, x+1, y);
218     }
219
220     SelectObject( hdc, hOldPen );
221     DeleteObject( hPen );
222 }
223
224 static void
225 PAGER_DrawButton(HDC hdc, COLORREF clrBk, RECT arrowRect,
226                  BOOL horz, BOOL topLeft, INT btnState)
227 {
228     HBRUSH   hBrush, hOldBrush;
229     RECT     rc = arrowRect;
230
231     TRACE("arrowRect = %s, btnState = %d\n", wine_dbgstr_rect(&arrowRect), btnState);
232
233     if (btnState == PGF_INVISIBLE)
234         return;
235
236     if ((rc.right - rc.left <= 0) || (rc.bottom - rc.top <= 0))
237         return;
238
239     hBrush = CreateSolidBrush(clrBk);
240     hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
241
242     FillRect(hdc, &rc, hBrush);
243
244     if (btnState == PGF_HOT)
245     {
246        DrawEdge( hdc, &rc, BDR_RAISEDINNER, BF_RECT);
247        if (horz)
248            PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
249        else
250            PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
251     }
252     else if (btnState == PGF_NORMAL)
253     {
254        DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
255        if (horz)
256            PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
257        else
258            PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
259     }
260     else if (btnState == PGF_DEPRESSED)
261     {
262        DrawEdge( hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
263        if (horz)
264            PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
265        else
266            PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
267     }
268     else if (btnState == PGF_GRAYED)
269     {
270        DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
271        if (horz)
272        {
273            PAGER_DrawHorzArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
274            rc.left++, rc.top++; rc.right++, rc.bottom++;
275            PAGER_DrawHorzArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
276        }
277        else
278        {
279            PAGER_DrawVertArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
280            rc.left++, rc.top++; rc.right++, rc.bottom++;
281            PAGER_DrawVertArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
282        }
283     }
284
285     SelectObject( hdc, hOldBrush );
286     DeleteObject(hBrush);
287 }
288
289 /* << PAGER_GetDropTarget >> */
290
291 static inline LRESULT
292 PAGER_ForwardMouse (PAGER_INFO* infoPtr, BOOL bFwd)
293 {
294     TRACE("[%p]\n", infoPtr->hwndSelf);
295
296     infoPtr->bForward = bFwd;
297
298     return 0;
299 }
300
301 static inline LRESULT
302 PAGER_GetButtonState (PAGER_INFO* infoPtr, INT btn)
303 {
304     LRESULT btnState = PGF_INVISIBLE;
305     TRACE("[%p]\n", infoPtr->hwndSelf);
306
307     if (btn == PGB_TOPORLEFT)
308         btnState = infoPtr->TLbtnState;
309     else if (btn == PGB_BOTTOMORRIGHT)
310         btnState = infoPtr->BRbtnState;
311
312     return btnState;
313 }
314
315
316 static inline INT
317 PAGER_GetPos(PAGER_INFO *infoPtr)
318 {
319     TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nPos);
320     return infoPtr->nPos;
321 }
322
323 static inline INT
324 PAGER_GetButtonSize(PAGER_INFO *infoPtr)
325 {
326     TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nButtonSize);
327     return infoPtr->nButtonSize;
328 }
329
330 static inline INT
331 PAGER_GetBorder(PAGER_INFO *infoPtr)
332 {
333     TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nBorder);
334     return infoPtr->nBorder;
335 }
336
337 static inline COLORREF
338 PAGER_GetBkColor(PAGER_INFO *infoPtr)
339 {
340     TRACE("[%p] returns %06lx\n", infoPtr->hwndSelf, infoPtr->clrBk);
341     return infoPtr->clrBk;
342 }
343
344 static void
345 PAGER_CalcSize (PAGER_INFO *infoPtr, INT* size, BOOL getWidth)
346 {
347     NMPGCALCSIZE nmpgcs;
348     ZeroMemory (&nmpgcs, sizeof (NMPGCALCSIZE));
349     nmpgcs.hdr.hwndFrom = infoPtr->hwndSelf;
350     nmpgcs.hdr.idFrom   = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
351     nmpgcs.hdr.code = PGN_CALCSIZE;
352     nmpgcs.dwFlag = getWidth ? PGF_CALCWIDTH : PGF_CALCHEIGHT;
353     nmpgcs.iWidth = getWidth ? *size : 0;
354     nmpgcs.iHeight = getWidth ? 0 : *size;
355     SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
356                   (WPARAM)nmpgcs.hdr.idFrom, (LPARAM)&nmpgcs);
357
358     *size = getWidth ? nmpgcs.iWidth : nmpgcs.iHeight;
359
360     TRACE("[%p] PGN_CALCSIZE returns %s=%d\n", infoPtr->hwndSelf,
361                   getWidth ? "width" : "height", *size);
362 }
363
364 static void
365 PAGER_PositionChildWnd(PAGER_INFO* infoPtr)
366 {
367     if (infoPtr->hwndChild)
368     {
369         RECT rcClient;
370         int nPos = infoPtr->nPos;
371
372         /* compensate for a grayed btn, which will soon become invisible */
373         if (infoPtr->TLbtnState == PGF_GRAYED)
374             nPos += infoPtr->nButtonSize;
375
376         GetClientRect(infoPtr->hwndSelf, &rcClient);
377
378         if (infoPtr->dwStyle & PGS_HORZ)
379         {
380             int wndSize = max(0, rcClient.right - rcClient.left);
381             if (infoPtr->nWidth < wndSize)
382                 infoPtr->nWidth = wndSize;
383
384             TRACE("[%p] SWP %dx%d at (%d,%d)\n", infoPtr->hwndSelf,
385                          infoPtr->nWidth, infoPtr->nHeight,
386                          -nPos, 0);
387             SetWindowPos(infoPtr->hwndChild, 0,
388                          -nPos, 0,
389                          infoPtr->nWidth, infoPtr->nHeight,
390                          SWP_NOZORDER);
391         }
392         else
393         {
394             int wndSize = max(0, rcClient.bottom - rcClient.top);
395             if (infoPtr->nHeight < wndSize)
396                 infoPtr->nHeight = wndSize;
397
398             TRACE("[%p] SWP %dx%d at (%d,%d)\n", infoPtr->hwndSelf,
399                          infoPtr->nWidth, infoPtr->nHeight,
400                          0, -nPos);
401             SetWindowPos(infoPtr->hwndChild, 0,
402                          0, -nPos,
403                          infoPtr->nWidth, infoPtr->nHeight,
404                          SWP_NOZORDER);
405         }
406
407         InvalidateRect(infoPtr->hwndChild, NULL, TRUE);
408     }
409 }
410
411 static INT
412 PAGER_GetScrollRange(PAGER_INFO* infoPtr)
413 {
414     INT scrollRange = 0;
415
416     if (infoPtr->hwndChild)
417     {
418         INT wndSize, childSize;
419         RECT wndRect;
420         GetWindowRect(infoPtr->hwndSelf, &wndRect);
421
422         if (infoPtr->dwStyle & PGS_HORZ)
423         {
424             wndSize = wndRect.right - wndRect.left;
425             PAGER_CalcSize(infoPtr, &infoPtr->nWidth, TRUE);
426             childSize = infoPtr->nWidth;
427         }
428         else
429         {
430             wndSize = wndRect.bottom - wndRect.top;
431             PAGER_CalcSize(infoPtr, &infoPtr->nHeight, FALSE);
432             childSize = infoPtr->nHeight;
433         }
434
435         TRACE("childSize = %d,  wndSize = %d\n", childSize, wndSize);
436         if (childSize > wndSize)
437             scrollRange = childSize - wndSize + infoPtr->nButtonSize;
438     }
439
440     TRACE("[%p] returns %d\n", infoPtr->hwndSelf, scrollRange);
441     return scrollRange;
442 }
443
444 static void
445 PAGER_UpdateBtns(PAGER_INFO *infoPtr, INT scrollRange, BOOL hideGrayBtns)
446 {
447     BOOL resizeClient;
448     BOOL repaintBtns;
449     INT oldTLbtnState = infoPtr->TLbtnState;
450     INT oldBRbtnState = infoPtr->BRbtnState;
451     POINT pt;
452     RECT rcTopLeft, rcBottomRight;
453
454     /* get button rects */
455     PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, FALSE);
456
457     GetCursorPos(&pt);
458
459     /* update states based on scroll position */
460     if (infoPtr->nPos > 0)
461     {
462         if (infoPtr->TLbtnState == PGF_INVISIBLE || infoPtr->TLbtnState == PGF_GRAYED)
463             infoPtr->TLbtnState = PGF_NORMAL;
464     }
465     else if (PtInRect(&rcTopLeft, pt))
466         infoPtr->TLbtnState = PGF_GRAYED;
467     else
468         infoPtr->TLbtnState = PGF_INVISIBLE;
469
470     if (scrollRange <= 0)
471     {
472         infoPtr->TLbtnState = PGF_INVISIBLE;
473         infoPtr->BRbtnState = PGF_INVISIBLE;
474     }
475     else if (infoPtr->nPos < scrollRange)
476     {
477         if (infoPtr->BRbtnState == PGF_INVISIBLE || infoPtr->BRbtnState == PGF_GRAYED)
478             infoPtr->BRbtnState = PGF_NORMAL;
479     }
480     else if (PtInRect(&rcBottomRight, pt))
481         infoPtr->BRbtnState = PGF_GRAYED;
482     else
483         infoPtr->BRbtnState = PGF_INVISIBLE;
484
485     /* only need to resize when entering or leaving PGF_INVISIBLE state */
486     resizeClient =
487         ((oldTLbtnState == PGF_INVISIBLE) != (infoPtr->TLbtnState == PGF_INVISIBLE)) ||
488         ((oldBRbtnState == PGF_INVISIBLE) != (infoPtr->BRbtnState == PGF_INVISIBLE));
489     /* initiate NCCalcSize to resize client wnd if necessary */
490     if (resizeClient)
491         SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
492                      SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
493                      SWP_NOZORDER | SWP_NOACTIVATE);
494
495     /* repaint when changing any state */
496     repaintBtns = (oldTLbtnState != infoPtr->TLbtnState) || 
497                   (oldBRbtnState != infoPtr->BRbtnState);
498     if (repaintBtns)
499         SendMessageW(infoPtr->hwndSelf, WM_NCPAINT, 0, 0);
500 }
501
502 static LRESULT
503 PAGER_SetPos(PAGER_INFO* infoPtr, INT newPos, BOOL fromBtnPress)
504 {
505     INT scrollRange = PAGER_GetScrollRange(infoPtr);
506     INT oldPos = infoPtr->nPos;
507
508     if ((scrollRange <= 0) || (newPos < 0))
509         infoPtr->nPos = 0;
510     else if (newPos > scrollRange)
511         infoPtr->nPos = scrollRange;
512     else
513         infoPtr->nPos = newPos;
514
515     TRACE("[%p] pos=%d, oldpos=%d\n", infoPtr->hwndSelf, infoPtr->nPos, oldPos);
516
517     if (infoPtr->nPos != oldPos)
518     {
519         /* gray and restore btns, and if from WM_SETPOS, hide the gray btns */
520         PAGER_UpdateBtns(infoPtr, scrollRange, !fromBtnPress);
521         PAGER_PositionChildWnd(infoPtr);
522     }
523
524     return 0;
525 }
526
527 static LRESULT
528 PAGER_WindowPosChanging(PAGER_INFO* infoPtr, WINDOWPOS *winpos)
529 {
530     if ((infoPtr->dwStyle & CCS_NORESIZE) && !(winpos->flags & SWP_NOSIZE))
531     {
532         /* don't let the app resize the nonscrollable dimension of a control
533          * that was created with CCS_NORESIZE style
534          * (i.e. height for a horizontal pager, or width for a vertical one) */
535
536         /* except if the current dimension is 0 and app is setting for
537          * first time, then save amount as dimension. - GA 8/01 */
538
539         if (infoPtr->dwStyle & PGS_HORZ)
540             if (!infoPtr->nHeight && winpos->cy)
541                 infoPtr->nHeight = winpos->cy;
542             else
543                 winpos->cy = infoPtr->nHeight;
544         else
545             if (!infoPtr->nWidth && winpos->cx)
546                 infoPtr->nWidth = winpos->cx;
547             else
548                 winpos->cx = infoPtr->nWidth;
549         return 0;
550     }
551
552     return DefWindowProcW (infoPtr->hwndSelf, WM_WINDOWPOSCHANGING, 0, (LPARAM)winpos);
553 }
554
555 static INT
556 PAGER_SetFixedWidth(PAGER_INFO* infoPtr)
557 {
558   /* Must set the non-scrollable dimension to be less than the full height/width
559    * so that NCCalcSize is called.  The Msoft docs mention 3/4 factor for button
560    * size, and experimentation shows that affect is almost right. */
561
562     RECT wndRect;
563     INT delta, h;
564     GetWindowRect(infoPtr->hwndSelf, &wndRect);
565
566     /* see what the app says for btn width */
567     PAGER_CalcSize(infoPtr, &infoPtr->nWidth, TRUE);
568
569     if (infoPtr->dwStyle & CCS_NORESIZE)
570     {
571         delta = wndRect.right - wndRect.left - infoPtr->nWidth;
572         if (delta > infoPtr->nButtonSize)
573             infoPtr->nWidth += 4 * infoPtr->nButtonSize / 3;
574         else if (delta > 0)
575             infoPtr->nWidth +=  infoPtr->nButtonSize / 3;
576     }
577
578     h = wndRect.bottom - wndRect.top + infoPtr->nButtonSize;
579
580     TRACE("[%p] infoPtr->nWidth set to %d\n",
581                infoPtr->hwndSelf, infoPtr->nWidth);
582
583     return h;
584 }
585
586 static INT
587 PAGER_SetFixedHeight(PAGER_INFO* infoPtr)
588 {
589   /* Must set the non-scrollable dimension to be less than the full height/width
590    * so that NCCalcSize is called.  The Msoft docs mention 3/4 factor for button
591    * size, and experimentation shows that affect is almost right. */
592
593     RECT wndRect;
594     INT delta, w;
595     GetWindowRect(infoPtr->hwndSelf, &wndRect);
596
597     /* see what the app says for btn height */
598     PAGER_CalcSize(infoPtr, &infoPtr->nHeight, FALSE);
599
600     if (infoPtr->dwStyle & CCS_NORESIZE)
601     {
602         delta = wndRect.bottom - wndRect.top - infoPtr->nHeight;
603         if (delta > infoPtr->nButtonSize)
604             infoPtr->nHeight += infoPtr->nButtonSize;
605         else if (delta > 0)
606             infoPtr->nHeight +=  infoPtr->nButtonSize / 3;
607     }
608
609     w = wndRect.right - wndRect.left + infoPtr->nButtonSize;
610
611     TRACE("[%p] infoPtr->nHeight set to %d\n",
612                infoPtr->hwndSelf, infoPtr->nHeight);
613
614     return w;
615 }
616
617 /******************************************************************
618  * For the PGM_RECALCSIZE message (but not the other uses in      *
619  * this module), the native control does only the following:      *
620  *                                                                *
621  *    if (some condition)                                         *
622  *          PostMessageW(hwnd, EM_FMTLINES, 0, 0);                *
623  *    return DefWindowProcW(hwnd, PGM_RECALCSIZE, 0, 0);          *
624  *                                                                *
625  * When we figure out what the "some condition" is we will        *
626  * implement that for the message processing.                     *
627  ******************************************************************/
628
629 static LRESULT
630 PAGER_RecalcSize(PAGER_INFO *infoPtr)
631 {
632     TRACE("[%p]\n", infoPtr->hwndSelf);
633
634     if (infoPtr->hwndChild)
635     {
636         INT scrollRange = PAGER_GetScrollRange(infoPtr);
637
638         if (scrollRange <= 0)
639         {
640             infoPtr->nPos = -1;
641             PAGER_SetPos(infoPtr, 0, FALSE);
642         }
643         else
644             PAGER_PositionChildWnd(infoPtr);
645     }
646
647     return 1;
648 }
649
650
651 static COLORREF
652 PAGER_SetBkColor (PAGER_INFO* infoPtr, COLORREF clrBk)
653 {
654     COLORREF clrTemp = infoPtr->clrBk;
655
656     infoPtr->clrBk = clrBk;
657     TRACE("[%p] %06lx\n", infoPtr->hwndSelf, infoPtr->clrBk);
658
659     /* the native control seems to do things this way */
660     SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
661                  SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
662                  SWP_NOZORDER | SWP_NOACTIVATE);
663
664     RedrawWindow(infoPtr->hwndSelf, 0, 0, RDW_ERASE | RDW_INVALIDATE);
665
666     return clrTemp;
667 }
668
669
670 static INT
671 PAGER_SetBorder (PAGER_INFO* infoPtr, INT iBorder)
672 {
673     INT nTemp = infoPtr->nBorder;
674
675     infoPtr->nBorder = iBorder;
676     TRACE("[%p] %d\n", infoPtr->hwndSelf, infoPtr->nBorder);
677
678     PAGER_RecalcSize(infoPtr);
679
680     return nTemp;
681 }
682
683
684 static INT
685 PAGER_SetButtonSize (PAGER_INFO* infoPtr, INT iButtonSize)
686 {
687     INT nTemp = infoPtr->nButtonSize;
688
689     infoPtr->nButtonSize = iButtonSize;
690     TRACE("[%p] %d\n", infoPtr->hwndSelf, infoPtr->nButtonSize);
691
692     PAGER_RecalcSize(infoPtr);
693
694     return nTemp;
695 }
696
697
698 static LRESULT
699 PAGER_SetChild (PAGER_INFO* infoPtr, HWND hwndChild)
700 {
701     INT hw;
702
703     infoPtr->hwndChild = IsWindow (hwndChild) ? hwndChild : 0;
704
705     if (infoPtr->hwndChild)
706     {
707         TRACE("[%p] hwndChild=%p\n", infoPtr->hwndSelf, infoPtr->hwndChild);
708
709         if (infoPtr->dwStyle & PGS_HORZ) {
710             hw = PAGER_SetFixedHeight(infoPtr);
711             /* adjust non-scrollable dimension to fit the child */
712             SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, hw, infoPtr->nHeight,
713                          SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
714                          SWP_NOSIZE | SWP_NOACTIVATE);
715         }
716         else {
717             hw = PAGER_SetFixedWidth(infoPtr);
718             /* adjust non-scrollable dimension to fit the child */
719             SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, infoPtr->nWidth, hw,
720                          SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
721                          SWP_NOSIZE | SWP_NOACTIVATE);
722         }
723
724         /* position child within the page scroller */
725         SetWindowPos(infoPtr->hwndChild, HWND_TOP,
726                      0,0,0,0,
727                      SWP_SHOWWINDOW | SWP_NOSIZE);  /* native is 0 */
728
729         infoPtr->nPos = -1;
730         PAGER_SetPos(infoPtr, 0, FALSE);
731     }
732
733     return 0;
734 }
735
736 static void
737 PAGER_Scroll(PAGER_INFO* infoPtr, INT dir)
738 {
739     NMPGSCROLL nmpgScroll;
740     RECT rcWnd;
741
742     if (infoPtr->hwndChild)
743     {
744         ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL));
745         nmpgScroll.hdr.hwndFrom = infoPtr->hwndSelf;
746         nmpgScroll.hdr.idFrom   = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
747         nmpgScroll.hdr.code = PGN_SCROLL;
748
749         GetWindowRect(infoPtr->hwndSelf, &rcWnd);
750         GetClientRect(infoPtr->hwndSelf, &nmpgScroll.rcParent);
751         nmpgScroll.iXpos = nmpgScroll.iYpos = 0;
752         nmpgScroll.iDir = dir;
753
754         if (infoPtr->dwStyle & PGS_HORZ)
755         {
756             nmpgScroll.iScroll = rcWnd.right - rcWnd.left;
757             nmpgScroll.iXpos = infoPtr->nPos;
758         }
759         else
760         {
761             nmpgScroll.iScroll = rcWnd.bottom - rcWnd.top;
762             nmpgScroll.iYpos = infoPtr->nPos;
763         }
764         nmpgScroll.iScroll -= 2*infoPtr->nButtonSize;
765
766         SendMessageW (infoPtr->hwndNotify, WM_NOTIFY,
767                     (WPARAM)nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll);
768
769         TRACE("[%p] PGN_SCROLL returns iScroll=%d\n", infoPtr->hwndSelf, nmpgScroll.iScroll);
770
771         if (nmpgScroll.iScroll > 0)
772         {
773             infoPtr->direction = dir;
774
775             if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP)
776                 PAGER_SetPos(infoPtr, infoPtr->nPos - nmpgScroll.iScroll, TRUE);
777             else
778                 PAGER_SetPos(infoPtr, infoPtr->nPos + nmpgScroll.iScroll, TRUE);
779         }
780         else
781             infoPtr->direction = -1;
782     }
783 }
784
785 static LRESULT
786 PAGER_FmtLines(PAGER_INFO *infoPtr)
787 {
788     /* initiate NCCalcSize to resize client wnd and get size */
789     SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
790                  SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
791                  SWP_NOZORDER | SWP_NOACTIVATE);
792
793     SetWindowPos(infoPtr->hwndChild, 0,
794                  0,0,infoPtr->nWidth,infoPtr->nHeight,
795                  0);
796
797     return DefWindowProcW (infoPtr->hwndSelf, EM_FMTLINES, 0, 0);
798 }
799
800 static LRESULT
801 PAGER_Create (HWND hwnd, LPCREATESTRUCTW lpcs)
802 {
803     PAGER_INFO *infoPtr;
804
805     /* allocate memory for info structure */
806     infoPtr = (PAGER_INFO *)Alloc (sizeof(PAGER_INFO));
807     if (!infoPtr) return -1;
808     SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
809
810     /* set default settings */
811     infoPtr->hwndSelf = hwnd;
812     infoPtr->hwndChild = NULL;
813     infoPtr->hwndNotify = lpcs->hwndParent;
814     infoPtr->dwStyle = lpcs->style;
815     infoPtr->clrBk = GetSysColor(COLOR_BTNFACE);
816     infoPtr->nBorder = 0;
817     infoPtr->nButtonSize = 12;
818     infoPtr->nPos = 0;
819     infoPtr->nWidth = 0;
820     infoPtr->nHeight = 0;
821     infoPtr->bForward = FALSE;
822     infoPtr->bCapture = FALSE;
823     infoPtr->TLbtnState = PGF_INVISIBLE;
824     infoPtr->BRbtnState = PGF_INVISIBLE;
825     infoPtr->direction = -1;
826
827     if (infoPtr->dwStyle & PGS_DRAGNDROP)
828         FIXME("[%p] Drag and Drop style is not implemented yet.\n", infoPtr->hwndSelf);
829
830     return 0;
831 }
832
833
834 static LRESULT
835 PAGER_Destroy (PAGER_INFO *infoPtr)
836 {
837     SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
838     Free (infoPtr);  /* free pager info data */
839     return 0;
840 }
841
842 static LRESULT
843 PAGER_NCCalcSize(PAGER_INFO* infoPtr, WPARAM wParam, LPRECT lpRect)
844 {
845     RECT rcChild, rcWindow;
846     INT scrollRange;
847
848     /*
849      * lpRect points to a RECT struct.  On entry, the struct
850      * contains the proposed wnd rectangle for the window.
851      * On exit, the struct should contain the screen
852      * coordinates of the corresponding window's client area.
853      */
854
855     DefWindowProcW (infoPtr->hwndSelf, WM_NCCALCSIZE, wParam, (LPARAM)lpRect);
856
857     TRACE("orig rect=%s\n", wine_dbgstr_rect(lpRect));
858
859     GetWindowRect (infoPtr->hwndChild, &rcChild);
860     MapWindowPoints (0, infoPtr->hwndSelf, (LPPOINT)&rcChild, 2); /* FIXME: RECT != 2 POINTS */
861     GetWindowRect (infoPtr->hwndSelf, &rcWindow);
862
863     if (infoPtr->dwStyle & PGS_HORZ)
864     {
865         infoPtr->nWidth = lpRect->right - lpRect->left;
866         PAGER_CalcSize (infoPtr, &infoPtr->nWidth, TRUE);
867
868         scrollRange = infoPtr->nWidth - (rcWindow.right - rcWindow.left);
869
870         if (infoPtr->TLbtnState && (lpRect->left + infoPtr->nButtonSize < lpRect->right))
871             lpRect->left += infoPtr->nButtonSize;
872         if (infoPtr->BRbtnState && (lpRect->right - infoPtr->nButtonSize > lpRect->left))
873             lpRect->right -= infoPtr->nButtonSize;
874     }
875     else
876     {
877         infoPtr->nHeight = lpRect->bottom - lpRect->top;
878         PAGER_CalcSize (infoPtr, &infoPtr->nHeight, FALSE);
879
880         scrollRange = infoPtr->nHeight - (rcWindow.bottom - rcWindow.top);
881
882         if (infoPtr->TLbtnState && (lpRect->top + infoPtr->nButtonSize < lpRect->bottom))
883             lpRect->top += infoPtr->nButtonSize;
884         if (infoPtr->BRbtnState && (lpRect->bottom - infoPtr->nButtonSize > lpRect->top))
885             lpRect->bottom -= infoPtr->nButtonSize;
886     }
887
888     TRACE("nPos=%d, nHeigth=%d, window=%s\n",
889           infoPtr->nPos, infoPtr->nHeight,
890           wine_dbgstr_rect(&rcWindow));
891
892     TRACE("[%p] client rect set to %ldx%ld at (%ld,%ld) BtnState[%d,%d]\n",
893           infoPtr->hwndSelf, lpRect->right-lpRect->left, lpRect->bottom-lpRect->top,
894           lpRect->left, lpRect->top,
895           infoPtr->TLbtnState, infoPtr->BRbtnState);
896
897     return 0;
898 }
899
900 static LRESULT
901 PAGER_NCPaint (PAGER_INFO* infoPtr, HRGN hRgn)
902 {
903     RECT rcBottomRight, rcTopLeft;
904     HDC hdc;
905
906     if (infoPtr->dwStyle & WS_MINIMIZE)
907         return 0;
908
909     DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)hRgn, 0);
910
911     if (!(hdc = GetDCEx (infoPtr->hwndSelf, 0, DCX_USESTYLE | DCX_WINDOW)))
912         return 0;
913
914     PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, FALSE);
915
916     PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft,
917                      infoPtr->dwStyle & PGS_HORZ, TRUE, infoPtr->TLbtnState);
918     PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight,
919                      infoPtr->dwStyle & PGS_HORZ, FALSE, infoPtr->BRbtnState);
920
921     ReleaseDC( infoPtr->hwndSelf, hdc );
922     return 0;
923 }
924
925 static INT
926 PAGER_HitTest (PAGER_INFO* infoPtr, const POINT * pt)
927 {
928     RECT clientRect, rcTopLeft, rcBottomRight;
929     POINT ptWindow;
930
931     GetClientRect (infoPtr->hwndSelf, &clientRect);
932
933     if (PtInRect(&clientRect, *pt))
934     {
935         TRACE("child\n");
936         return -1;
937     }
938
939     ptWindow = *pt;
940     PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, TRUE);
941
942     if ((infoPtr->TLbtnState != PGF_INVISIBLE) && PtInRect(&rcTopLeft, ptWindow))
943     {
944         TRACE("PGB_TOPORLEFT\n");
945         return PGB_TOPORLEFT;
946     }
947     else if ((infoPtr->BRbtnState != PGF_INVISIBLE) && PtInRect(&rcBottomRight, ptWindow))
948     {
949         TRACE("PGB_BOTTOMORRIGHT\n");
950         return PGB_BOTTOMORRIGHT;
951     }
952
953     TRACE("nowhere\n");
954     return -1;
955 }
956
957 static LRESULT
958 PAGER_NCHitTest (PAGER_INFO* infoPtr, INT x, INT y)
959 {
960     POINT pt;
961     INT nHit;
962
963     pt.x = x;
964     pt.y = y;
965
966     ScreenToClient (infoPtr->hwndSelf, &pt);
967     nHit = PAGER_HitTest(infoPtr, &pt);
968
969     return (nHit < 0) ? HTTRANSPARENT : HTCLIENT;
970 }
971
972 static LRESULT
973 PAGER_MouseMove (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
974 {
975     POINT clpt, pt;
976     RECT wnrect, *btnrect = NULL;
977     BOOL topLeft = FALSE;
978     INT btnstate = 0;
979     INT hit;
980     HDC hdc;
981
982     pt.x = x;
983     pt.y = y;
984
985     TRACE("[%p] to (%d,%d)\n", infoPtr->hwndSelf, x, y);
986     ClientToScreen(infoPtr->hwndSelf, &pt);
987     GetWindowRect(infoPtr->hwndSelf, &wnrect);
988     if (PtInRect(&wnrect, pt)) {
989         RECT TLbtnrect, BRbtnrect;
990         PAGER_GetButtonRects(infoPtr, &TLbtnrect, &BRbtnrect, FALSE);
991
992         clpt = pt;
993         MapWindowPoints(0, infoPtr->hwndSelf, &clpt, 1);
994         hit = PAGER_HitTest(infoPtr, &clpt);
995         if ((hit == PGB_TOPORLEFT) && (infoPtr->TLbtnState == PGF_NORMAL))
996         {
997             topLeft = TRUE;
998             btnrect = &TLbtnrect;
999             infoPtr->TLbtnState = PGF_HOT;
1000             btnstate = infoPtr->TLbtnState;
1001         }
1002         else if ((hit == PGB_BOTTOMORRIGHT) && (infoPtr->BRbtnState == PGF_NORMAL))
1003         {
1004             topLeft = FALSE;
1005             btnrect = &BRbtnrect;
1006             infoPtr->BRbtnState = PGF_HOT;
1007             btnstate = infoPtr->BRbtnState;
1008         }
1009
1010         /* If in one of the buttons the capture and draw buttons */
1011         if (btnrect)
1012         {
1013             TRACE("[%p] draw btn (%ld,%ld)-(%ld,%ld), Capture %s, style %08lx\n",
1014                   infoPtr->hwndSelf, btnrect->left, btnrect->top,
1015                   btnrect->right, btnrect->bottom,
1016                   (infoPtr->bCapture) ? "TRUE" : "FALSE",
1017                   infoPtr->dwStyle);
1018             if (!infoPtr->bCapture)
1019             {
1020                 TRACE("[%p] SetCapture\n", infoPtr->hwndSelf);
1021                 SetCapture(infoPtr->hwndSelf);
1022                 infoPtr->bCapture = TRUE;
1023             }
1024             if (infoPtr->dwStyle & PGS_AUTOSCROLL)
1025                 SetTimer(infoPtr->hwndSelf, TIMERID1, 0x3e, 0);
1026             hdc = GetWindowDC(infoPtr->hwndSelf);
1027             /* OffsetRect(wnrect, 0 | 1, 0 | 1) */
1028             PAGER_DrawButton(hdc, infoPtr->clrBk, *btnrect,
1029                              infoPtr->dwStyle & PGS_HORZ, topLeft, btnstate);
1030             ReleaseDC(infoPtr->hwndSelf, hdc);
1031             return 0;
1032         }
1033     }
1034
1035     /* If we think we are captured, then do release */
1036     if (infoPtr->bCapture && (WindowFromPoint(pt) != infoPtr->hwndSelf))
1037     {
1038         NMHDR nmhdr;
1039
1040         infoPtr->bCapture = FALSE;
1041
1042         if (GetCapture() == infoPtr->hwndSelf)
1043         {
1044             ReleaseCapture();
1045
1046             if (infoPtr->TLbtnState == PGF_GRAYED)
1047             {
1048                 infoPtr->TLbtnState = PGF_INVISIBLE;
1049                 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
1050                              SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
1051                              SWP_NOZORDER | SWP_NOACTIVATE);
1052             }
1053             else if (infoPtr->TLbtnState == PGF_HOT)
1054             {
1055                 infoPtr->TLbtnState = PGF_NORMAL;
1056                 /* FIXME: just invalidate button rect */
1057                 RedrawWindow(infoPtr->hwndSelf, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
1058             }
1059
1060             if (infoPtr->BRbtnState == PGF_GRAYED)
1061             {
1062                 infoPtr->BRbtnState = PGF_INVISIBLE;
1063                 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
1064                              SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
1065                              SWP_NOZORDER | SWP_NOACTIVATE);
1066             }
1067             else if (infoPtr->BRbtnState == PGF_HOT)
1068             {
1069                 infoPtr->BRbtnState = PGF_NORMAL;
1070                 /* FIXME: just invalidate button rect */
1071                 RedrawWindow(infoPtr->hwndSelf, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
1072             }
1073
1074             /* Notify parent of released mouse capture */
1075                 memset(&nmhdr, 0, sizeof(NMHDR));
1076                 nmhdr.hwndFrom = infoPtr->hwndSelf;
1077                 nmhdr.idFrom   = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1078                 nmhdr.code = NM_RELEASEDCAPTURE;
1079                 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
1080                          (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1081         }
1082         if (IsWindow(infoPtr->hwndSelf))
1083             KillTimer(infoPtr->hwndSelf, TIMERID1);
1084     }
1085     return 0;
1086 }
1087
1088 static LRESULT
1089 PAGER_LButtonDown (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
1090 {
1091     BOOL repaintBtns = FALSE;
1092     POINT pt;
1093     INT hit;
1094
1095     pt.x = x;
1096     pt.y = y;
1097
1098     TRACE("[%p] at (%d,%d)\n", infoPtr->hwndSelf, x, y);
1099
1100     hit = PAGER_HitTest(infoPtr, &pt);
1101
1102     /* put btn in DEPRESSED state */
1103     if (hit == PGB_TOPORLEFT)
1104     {
1105         repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED;
1106         infoPtr->TLbtnState = PGF_DEPRESSED;
1107         SetTimer(infoPtr->hwndSelf, TIMERID1, INITIAL_DELAY, 0);
1108     }
1109     else if (hit == PGB_BOTTOMORRIGHT)
1110     {
1111         repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED;
1112         infoPtr->BRbtnState = PGF_DEPRESSED;
1113         SetTimer(infoPtr->hwndSelf, TIMERID1, INITIAL_DELAY, 0);
1114     }
1115
1116     if (repaintBtns)
1117         SendMessageW(infoPtr->hwndSelf, WM_NCPAINT, 0, 0);
1118
1119     switch(hit)
1120     {
1121     case PGB_TOPORLEFT:
1122         if (infoPtr->dwStyle & PGS_HORZ)
1123         {
1124             TRACE("[%p] PGF_SCROLLLEFT\n", infoPtr->hwndSelf);
1125             PAGER_Scroll(infoPtr, PGF_SCROLLLEFT);
1126         }
1127         else
1128         {
1129             TRACE("[%p] PGF_SCROLLUP\n", infoPtr->hwndSelf);
1130             PAGER_Scroll(infoPtr, PGF_SCROLLUP);
1131         }
1132         break;
1133     case PGB_BOTTOMORRIGHT:
1134         if (infoPtr->dwStyle & PGS_HORZ)
1135         {
1136             TRACE("[%p] PGF_SCROLLRIGHT\n", infoPtr->hwndSelf);
1137             PAGER_Scroll(infoPtr, PGF_SCROLLRIGHT);
1138         }
1139         else
1140         {
1141             TRACE("[%p] PGF_SCROLLDOWN\n", infoPtr->hwndSelf);
1142             PAGER_Scroll(infoPtr, PGF_SCROLLDOWN);
1143         }
1144         break;
1145     default:
1146         break;
1147     }
1148
1149     return 0;
1150 }
1151
1152 static LRESULT
1153 PAGER_LButtonUp (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
1154 {
1155     TRACE("[%p]\n", infoPtr->hwndSelf);
1156
1157     KillTimer (infoPtr->hwndSelf, TIMERID1);
1158     KillTimer (infoPtr->hwndSelf, TIMERID2);
1159
1160     /* make PRESSED btns NORMAL but don't hide gray btns */
1161     if (infoPtr->TLbtnState & (PGF_HOT | PGF_DEPRESSED))
1162         infoPtr->TLbtnState = PGF_NORMAL;
1163     if (infoPtr->BRbtnState & (PGF_HOT | PGF_DEPRESSED))
1164         infoPtr->BRbtnState = PGF_NORMAL;
1165
1166     return 0;
1167 }
1168
1169 static LRESULT
1170 PAGER_Timer (PAGER_INFO* infoPtr, INT nTimerId)
1171 {
1172     INT dir;
1173
1174     /* if initial timer, kill it and start the repeat timer */
1175     if (nTimerId == TIMERID1) {
1176         if (infoPtr->TLbtnState == PGF_HOT)
1177             dir = (infoPtr->dwStyle & PGS_HORZ) ?
1178                 PGF_SCROLLLEFT : PGF_SCROLLUP;
1179         else
1180             dir = (infoPtr->dwStyle & PGS_HORZ) ?
1181                 PGF_SCROLLRIGHT : PGF_SCROLLDOWN;
1182         TRACE("[%p] TIMERID1: style=%08lx, dir=%d\n", 
1183               infoPtr->hwndSelf, infoPtr->dwStyle, dir);
1184         KillTimer(infoPtr->hwndSelf, TIMERID1);
1185         SetTimer(infoPtr->hwndSelf, TIMERID1, REPEAT_DELAY, 0);
1186         if (infoPtr->dwStyle & PGS_AUTOSCROLL) {
1187             PAGER_Scroll(infoPtr, dir);
1188             SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
1189                          SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
1190                          SWP_NOZORDER | SWP_NOACTIVATE);
1191         }
1192         return 0;
1193
1194     }
1195
1196     TRACE("[%p] TIMERID2: dir=%d\n", infoPtr->hwndSelf, infoPtr->direction);
1197     KillTimer(infoPtr->hwndSelf, TIMERID2);
1198     if (infoPtr->direction > 0) {
1199         PAGER_Scroll(infoPtr, infoPtr->direction);
1200         SetTimer(infoPtr->hwndSelf, TIMERID2, REPEAT_DELAY, 0);
1201     }
1202     return 0;
1203 }
1204
1205 static LRESULT
1206 PAGER_EraseBackground (PAGER_INFO* infoPtr, HDC hdc)
1207 {
1208     POINT pt, ptorig;
1209     HWND parent;
1210
1211     pt.x = 0;
1212     pt.y = 0;
1213     parent = GetParent(infoPtr->hwndSelf);
1214     MapWindowPoints(infoPtr->hwndSelf, parent, &pt, 1);
1215     OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig);
1216     SendMessageW (parent, WM_ERASEBKGND, (WPARAM)hdc, 0);
1217     SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0);
1218
1219     return 0;
1220 }
1221
1222
1223 static LRESULT
1224 PAGER_Size (PAGER_INFO* infoPtr, INT type, INT x, INT y)
1225 {
1226     /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */
1227
1228     TRACE("[%p] %d,%d\n", infoPtr->hwndSelf, x, y);
1229
1230     if (infoPtr->dwStyle & PGS_HORZ)
1231         infoPtr->nHeight = y;
1232     else
1233         infoPtr->nWidth = x;
1234
1235     return PAGER_RecalcSize(infoPtr);
1236 }
1237
1238
1239 static LRESULT 
1240 PAGER_StyleChanged(PAGER_INFO *infoPtr, WPARAM wStyleType, LPSTYLESTRUCT lpss)
1241 {
1242     DWORD oldStyle = infoPtr->dwStyle;
1243
1244     TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
1245           wStyleType, lpss->styleOld, lpss->styleNew);
1246
1247     if (wStyleType != GWL_STYLE) return 0;
1248   
1249     infoPtr->dwStyle = lpss->styleNew;
1250
1251     if ((oldStyle ^ lpss->styleNew) & (PGS_HORZ | PGS_VERT))
1252     {
1253         PAGER_RecalcSize(infoPtr);
1254     }
1255
1256     return 0;
1257 }
1258
1259 static LRESULT WINAPI
1260 PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1261 {
1262     PAGER_INFO *infoPtr = (PAGER_INFO *)GetWindowLongPtrW(hwnd, 0);
1263
1264     if (!infoPtr && (uMsg != WM_CREATE))
1265         return DefWindowProcW (hwnd, uMsg, wParam, lParam);
1266
1267     switch (uMsg)
1268     {
1269         case EM_FMTLINES:
1270             return PAGER_FmtLines(infoPtr);
1271
1272         case PGM_FORWARDMOUSE:
1273             return PAGER_ForwardMouse (infoPtr, (BOOL)wParam);
1274
1275         case PGM_GETBKCOLOR:
1276             return PAGER_GetBkColor(infoPtr);
1277
1278         case PGM_GETBORDER:
1279             return PAGER_GetBorder(infoPtr);
1280
1281         case PGM_GETBUTTONSIZE:
1282             return PAGER_GetButtonSize(infoPtr);
1283
1284         case PGM_GETPOS:
1285             return PAGER_GetPos(infoPtr);
1286
1287         case PGM_GETBUTTONSTATE:
1288             return PAGER_GetButtonState (infoPtr, (INT)lParam);
1289
1290 /*      case PGM_GETDROPTARGET: */
1291
1292         case PGM_RECALCSIZE:
1293             return PAGER_RecalcSize(infoPtr);
1294
1295         case PGM_SETBKCOLOR:
1296             return PAGER_SetBkColor (infoPtr, (COLORREF)lParam);
1297
1298         case PGM_SETBORDER:
1299             return PAGER_SetBorder (infoPtr, (INT)lParam);
1300
1301         case PGM_SETBUTTONSIZE:
1302             return PAGER_SetButtonSize (infoPtr, (INT)lParam);
1303
1304         case PGM_SETCHILD:
1305             return PAGER_SetChild (infoPtr, (HWND)lParam);
1306
1307         case PGM_SETPOS:
1308             return PAGER_SetPos(infoPtr, (INT)lParam, FALSE);
1309
1310         case WM_CREATE:
1311             return PAGER_Create (hwnd, (LPCREATESTRUCTW)lParam);
1312
1313         case WM_DESTROY:
1314             return PAGER_Destroy (infoPtr);
1315
1316         case WM_SIZE:
1317             return PAGER_Size (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
1318
1319         case WM_NCPAINT:
1320             return PAGER_NCPaint (infoPtr, (HRGN)wParam);
1321
1322         case WM_WINDOWPOSCHANGING:
1323             return PAGER_WindowPosChanging (infoPtr, (WINDOWPOS*)lParam);
1324
1325         case WM_STYLECHANGED:
1326             return PAGER_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
1327
1328         case WM_NCCALCSIZE:
1329             return PAGER_NCCalcSize (infoPtr, wParam, (LPRECT)lParam);
1330
1331         case WM_NCHITTEST:
1332             return PAGER_NCHitTest (infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
1333
1334         case WM_MOUSEMOVE:
1335             if (infoPtr->bForward && infoPtr->hwndChild)
1336                 PostMessageW(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam);
1337             return PAGER_MouseMove (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
1338
1339         case WM_LBUTTONDOWN:
1340             return PAGER_LButtonDown (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
1341
1342         case WM_LBUTTONUP:
1343             return PAGER_LButtonUp (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
1344
1345         case WM_ERASEBKGND:
1346             return PAGER_EraseBackground (infoPtr, (HDC)wParam);
1347
1348         case WM_TIMER:
1349             return PAGER_Timer (infoPtr, (INT)wParam);
1350
1351         case WM_NOTIFY:
1352         case WM_COMMAND:
1353             return SendMessageW (infoPtr->hwndNotify, uMsg, wParam, lParam);
1354
1355         default:
1356             return DefWindowProcW (hwnd, uMsg, wParam, lParam);
1357     }
1358 }
1359
1360
1361 VOID
1362 PAGER_Register (void)
1363 {
1364     WNDCLASSW wndClass;
1365
1366     ZeroMemory (&wndClass, sizeof(WNDCLASSW));
1367     wndClass.style         = CS_GLOBALCLASS;
1368     wndClass.lpfnWndProc   = PAGER_WindowProc;
1369     wndClass.cbClsExtra    = 0;
1370     wndClass.cbWndExtra    = sizeof(PAGER_INFO *);
1371     wndClass.hCursor       = LoadCursorW (0, (LPWSTR)IDC_ARROW);
1372     wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
1373     wndClass.lpszClassName = WC_PAGESCROLLERW;
1374
1375     RegisterClassW (&wndClass);
1376 }
1377
1378
1379 VOID
1380 PAGER_Unregister (void)
1381 {
1382     UnregisterClassW (WC_PAGESCROLLERW, NULL);
1383 }