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