shlwapi: Implement URL_APPLY_GUESSFILE in UrlApplyScheme.
[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(const PAGER_INFO* infoPtr, RECT* prcTopLeft, RECT* prcBottomRight, BOOL bClientCoords)
97 {
98     RECT rcWindow;
99     GetWindowRect (infoPtr->hwndSelf, &rcWindow);
100
101     if (bClientCoords)
102         MapWindowPoints( 0, infoPtr->hwndSelf, (POINT *)&rcWindow, 2 );
103     else
104         OffsetRect(&rcWindow, -rcWindow.left, -rcWindow.top);
105
106     *prcTopLeft = *prcBottomRight = rcWindow;
107     if (infoPtr->dwStyle & PGS_HORZ)
108     {
109         prcTopLeft->right = prcTopLeft->left + infoPtr->nButtonSize;
110         prcBottomRight->left = prcBottomRight->right - infoPtr->nButtonSize;
111     }
112     else
113     {
114         prcTopLeft->bottom = prcTopLeft->top + infoPtr->nButtonSize;
115         prcBottomRight->top = prcBottomRight->bottom - infoPtr->nButtonSize;
116     }
117 }
118
119 /* the horizontal arrows are:
120  *
121  * 01234    01234
122  * 1  *      *
123  * 2 **      **
124  * 3***      ***
125  * 4***      ***
126  * 5 **      **
127  * 6  *      *
128  * 7
129  *
130  */
131 static void
132 PAGER_DrawHorzArrow (HDC hdc, RECT r, INT colorRef, BOOL left)
133 {
134     INT x, y, w, h;
135     HPEN hPen, hOldPen;
136
137     w = r.right - r.left + 1;
138     h = r.bottom - r.top + 1;
139     if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
140         return;  /* refuse to draw partial arrow */
141
142     if (!(hPen = CreatePen( PS_SOLID, 1, GetSysColor( colorRef )))) return;
143     hOldPen = SelectObject ( hdc, hPen );
144     if (left)
145     {
146         x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 3;
147         y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
148         MoveToEx (hdc, x, y, NULL);
149         LineTo (hdc, x--, y+5); y++;
150         MoveToEx (hdc, x, y, NULL);
151         LineTo (hdc, x--, y+3); y++;
152         MoveToEx (hdc, x, y, NULL);
153         LineTo (hdc, x, y+1);
154     }
155     else
156     {
157         x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
158         y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
159         MoveToEx (hdc, x, y, NULL);
160         LineTo (hdc, x++, y+5); y++;
161         MoveToEx (hdc, x, y, NULL);
162         LineTo (hdc, x++, y+3); y++;
163         MoveToEx (hdc, x, y, NULL);
164         LineTo (hdc, x, y+1);
165     }
166
167     SelectObject( hdc, hOldPen );
168     DeleteObject( hPen );
169 }
170
171 /* the vertical arrows are:
172  *
173  * 01234567    01234567
174  * 1******        **
175  * 2 ****        ****
176  * 3  **        ******
177  * 4
178  *
179  */
180 static void
181 PAGER_DrawVertArrow (HDC hdc, RECT r, INT colorRef, BOOL up)
182 {
183     INT x, y, w, h;
184     HPEN hPen, hOldPen;
185
186     w = r.right - r.left + 1;
187     h = r.bottom - r.top + 1;
188     if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
189         return;  /* refuse to draw partial arrow */
190
191     if (!(hPen = CreatePen( PS_SOLID, 1, GetSysColor( colorRef )))) return;
192     hOldPen = SelectObject ( hdc, hPen );
193     if (up)
194     {
195         x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
196         y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 3;
197         MoveToEx (hdc, x, y, NULL);
198         LineTo (hdc, x+5, y--); x++;
199         MoveToEx (hdc, x, y, NULL);
200         LineTo (hdc, x+3, y--); x++;
201         MoveToEx (hdc, x, y, NULL);
202         LineTo (hdc, x+1, y);
203     }
204     else
205     {
206         x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
207         y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
208         MoveToEx (hdc, x, y, NULL);
209         LineTo (hdc, x+5, y++); x++;
210         MoveToEx (hdc, x, y, NULL);
211         LineTo (hdc, x+3, y++); x++;
212         MoveToEx (hdc, x, y, NULL);
213         LineTo (hdc, x+1, y);
214     }
215
216     SelectObject( hdc, hOldPen );
217     DeleteObject( hPen );
218 }
219
220 static void
221 PAGER_DrawButton(HDC hdc, COLORREF clrBk, RECT arrowRect,
222                  BOOL horz, BOOL topLeft, INT btnState)
223 {
224     HBRUSH   hBrush, hOldBrush;
225     RECT     rc = arrowRect;
226
227     TRACE("arrowRect = %s, btnState = %d\n", wine_dbgstr_rect(&arrowRect), btnState);
228
229     if (btnState == PGF_INVISIBLE)
230         return;
231
232     if ((rc.right - rc.left <= 0) || (rc.bottom - rc.top <= 0))
233         return;
234
235     hBrush = CreateSolidBrush(clrBk);
236     hOldBrush = SelectObject(hdc, hBrush);
237
238     FillRect(hdc, &rc, hBrush);
239
240     if (btnState == PGF_HOT)
241     {
242        DrawEdge( hdc, &rc, BDR_RAISEDINNER, BF_RECT);
243        if (horz)
244            PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
245        else
246            PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
247     }
248     else if (btnState == PGF_NORMAL)
249     {
250        DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
251        if (horz)
252            PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
253        else
254            PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
255     }
256     else if (btnState == PGF_DEPRESSED)
257     {
258        DrawEdge( hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
259        if (horz)
260            PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
261        else
262            PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
263     }
264     else if (btnState == PGF_GRAYED)
265     {
266        DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
267        if (horz)
268        {
269            PAGER_DrawHorzArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
270            rc.left++, rc.top++; rc.right++, rc.bottom++;
271            PAGER_DrawHorzArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
272        }
273        else
274        {
275            PAGER_DrawVertArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
276            rc.left++, rc.top++; rc.right++, rc.bottom++;
277            PAGER_DrawVertArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
278        }
279     }
280
281     SelectObject( hdc, hOldBrush );
282     DeleteObject(hBrush);
283 }
284
285 /* << PAGER_GetDropTarget >> */
286
287 static inline LRESULT
288 PAGER_ForwardMouse (PAGER_INFO* infoPtr, BOOL bFwd)
289 {
290     TRACE("[%p]\n", infoPtr->hwndSelf);
291
292     infoPtr->bForward = bFwd;
293
294     return 0;
295 }
296
297 static inline LRESULT
298 PAGER_GetButtonState (const PAGER_INFO* infoPtr, INT btn)
299 {
300     LRESULT btnState = PGF_INVISIBLE;
301     TRACE("[%p]\n", infoPtr->hwndSelf);
302
303     if (btn == PGB_TOPORLEFT)
304         btnState = infoPtr->TLbtnState;
305     else if (btn == PGB_BOTTOMORRIGHT)
306         btnState = infoPtr->BRbtnState;
307
308     return btnState;
309 }
310
311
312 static inline INT
313 PAGER_GetPos(const PAGER_INFO *infoPtr)
314 {
315     TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nPos);
316     return infoPtr->nPos;
317 }
318
319 static inline INT
320 PAGER_GetButtonSize(const PAGER_INFO *infoPtr)
321 {
322     TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nButtonSize);
323     return infoPtr->nButtonSize;
324 }
325
326 static inline INT
327 PAGER_GetBorder(const PAGER_INFO *infoPtr)
328 {
329     TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nBorder);
330     return infoPtr->nBorder;
331 }
332
333 static inline COLORREF
334 PAGER_GetBkColor(const PAGER_INFO *infoPtr)
335 {
336     TRACE("[%p] returns %06x\n", infoPtr->hwndSelf, infoPtr->clrBk);
337     return infoPtr->clrBk;
338 }
339
340 static void
341 PAGER_CalcSize (const PAGER_INFO *infoPtr, INT* size, BOOL getWidth)
342 {
343     NMPGCALCSIZE nmpgcs;
344     ZeroMemory (&nmpgcs, sizeof (NMPGCALCSIZE));
345     nmpgcs.hdr.hwndFrom = infoPtr->hwndSelf;
346     nmpgcs.hdr.idFrom   = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
347     nmpgcs.hdr.code = PGN_CALCSIZE;
348     nmpgcs.dwFlag = getWidth ? PGF_CALCWIDTH : PGF_CALCHEIGHT;
349     nmpgcs.iWidth = getWidth ? *size : 0;
350     nmpgcs.iHeight = getWidth ? 0 : *size;
351     SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, nmpgcs.hdr.idFrom, (LPARAM)&nmpgcs);
352
353     *size = getWidth ? nmpgcs.iWidth : nmpgcs.iHeight;
354
355     TRACE("[%p] PGN_CALCSIZE returns %s=%d\n", infoPtr->hwndSelf,
356                   getWidth ? "width" : "height", *size);
357 }
358
359 static void
360 PAGER_PositionChildWnd(PAGER_INFO* infoPtr)
361 {
362     if (infoPtr->hwndChild)
363     {
364         RECT rcClient;
365         int nPos = infoPtr->nPos;
366
367         /* compensate for a grayed btn, which will soon become invisible */
368         if (infoPtr->TLbtnState == PGF_GRAYED)
369             nPos += infoPtr->nButtonSize;
370
371         GetClientRect(infoPtr->hwndSelf, &rcClient);
372
373         if (infoPtr->dwStyle & PGS_HORZ)
374         {
375             int wndSize = max(0, rcClient.right - rcClient.left);
376             if (infoPtr->nWidth < wndSize)
377                 infoPtr->nWidth = wndSize;
378
379             TRACE("[%p] SWP %dx%d at (%d,%d)\n", infoPtr->hwndSelf,
380                          infoPtr->nWidth, infoPtr->nHeight,
381                          -nPos, 0);
382             SetWindowPos(infoPtr->hwndChild, 0,
383                          -nPos, 0,
384                          infoPtr->nWidth, infoPtr->nHeight,
385                          SWP_NOZORDER);
386         }
387         else
388         {
389             int wndSize = max(0, rcClient.bottom - rcClient.top);
390             if (infoPtr->nHeight < wndSize)
391                 infoPtr->nHeight = wndSize;
392
393             TRACE("[%p] SWP %dx%d at (%d,%d)\n", infoPtr->hwndSelf,
394                          infoPtr->nWidth, infoPtr->nHeight,
395                          0, -nPos);
396             SetWindowPos(infoPtr->hwndChild, 0,
397                          0, -nPos,
398                          infoPtr->nWidth, infoPtr->nHeight,
399                          SWP_NOZORDER);
400         }
401
402         InvalidateRect(infoPtr->hwndChild, NULL, TRUE);
403     }
404 }
405
406 static INT
407 PAGER_GetScrollRange(PAGER_INFO* infoPtr)
408 {
409     INT scrollRange = 0;
410
411     if (infoPtr->hwndChild)
412     {
413         INT wndSize, childSize;
414         RECT wndRect;
415         GetWindowRect(infoPtr->hwndSelf, &wndRect);
416
417         if (infoPtr->dwStyle & PGS_HORZ)
418         {
419             wndSize = wndRect.right - wndRect.left;
420             PAGER_CalcSize(infoPtr, &infoPtr->nWidth, TRUE);
421             childSize = infoPtr->nWidth;
422         }
423         else
424         {
425             wndSize = wndRect.bottom - wndRect.top;
426             PAGER_CalcSize(infoPtr, &infoPtr->nHeight, FALSE);
427             childSize = infoPtr->nHeight;
428         }
429
430         TRACE("childSize = %d,  wndSize = %d\n", childSize, wndSize);
431         if (childSize > wndSize)
432             scrollRange = childSize - wndSize + infoPtr->nButtonSize;
433     }
434
435     TRACE("[%p] returns %d\n", infoPtr->hwndSelf, scrollRange);
436     return scrollRange;
437 }
438
439 static void
440 PAGER_UpdateBtns(PAGER_INFO *infoPtr, INT scrollRange, BOOL hideGrayBtns)
441 {
442     BOOL resizeClient;
443     BOOL repaintBtns;
444     INT oldTLbtnState = infoPtr->TLbtnState;
445     INT oldBRbtnState = infoPtr->BRbtnState;
446     POINT pt;
447     RECT rcTopLeft, rcBottomRight;
448
449     /* get button rects */
450     PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, FALSE);
451
452     GetCursorPos(&pt);
453
454     /* update states based on scroll position */
455     if (infoPtr->nPos > 0)
456     {
457         if (infoPtr->TLbtnState == PGF_INVISIBLE || infoPtr->TLbtnState == PGF_GRAYED)
458             infoPtr->TLbtnState = PGF_NORMAL;
459     }
460     else if (PtInRect(&rcTopLeft, pt))
461         infoPtr->TLbtnState = PGF_GRAYED;
462     else
463         infoPtr->TLbtnState = PGF_INVISIBLE;
464
465     if (scrollRange <= 0)
466     {
467         infoPtr->TLbtnState = PGF_INVISIBLE;
468         infoPtr->BRbtnState = PGF_INVISIBLE;
469     }
470     else if (infoPtr->nPos < scrollRange)
471     {
472         if (infoPtr->BRbtnState == PGF_INVISIBLE || infoPtr->BRbtnState == PGF_GRAYED)
473             infoPtr->BRbtnState = PGF_NORMAL;
474     }
475     else if (PtInRect(&rcBottomRight, pt))
476         infoPtr->BRbtnState = PGF_GRAYED;
477     else
478         infoPtr->BRbtnState = PGF_INVISIBLE;
479
480     /* only need to resize when entering or leaving PGF_INVISIBLE state */
481     resizeClient =
482         ((oldTLbtnState == PGF_INVISIBLE) != (infoPtr->TLbtnState == PGF_INVISIBLE)) ||
483         ((oldBRbtnState == PGF_INVISIBLE) != (infoPtr->BRbtnState == PGF_INVISIBLE));
484     /* initiate NCCalcSize to resize client wnd if necessary */
485     if (resizeClient)
486         SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
487                      SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
488                      SWP_NOZORDER | SWP_NOACTIVATE);
489
490     /* repaint when changing any state */
491     repaintBtns = (oldTLbtnState != infoPtr->TLbtnState) || 
492                   (oldBRbtnState != infoPtr->BRbtnState);
493     if (repaintBtns)
494         SendMessageW(infoPtr->hwndSelf, WM_NCPAINT, 0, 0);
495 }
496
497 static LRESULT
498 PAGER_SetPos(PAGER_INFO* infoPtr, INT newPos, BOOL fromBtnPress)
499 {
500     INT scrollRange = PAGER_GetScrollRange(infoPtr);
501     INT oldPos = infoPtr->nPos;
502
503     if ((scrollRange <= 0) || (newPos < 0))
504         infoPtr->nPos = 0;
505     else if (newPos > scrollRange)
506         infoPtr->nPos = scrollRange;
507     else
508         infoPtr->nPos = newPos;
509
510     TRACE("[%p] pos=%d, oldpos=%d\n", infoPtr->hwndSelf, infoPtr->nPos, oldPos);
511
512     if (infoPtr->nPos != oldPos)
513     {
514         /* gray and restore btns, and if from WM_SETPOS, hide the gray btns */
515         PAGER_UpdateBtns(infoPtr, scrollRange, !fromBtnPress);
516         PAGER_PositionChildWnd(infoPtr);
517     }
518
519     return 0;
520 }
521
522 static LRESULT
523 PAGER_WindowPosChanging(PAGER_INFO* infoPtr, WINDOWPOS *winpos)
524 {
525     if ((infoPtr->dwStyle & CCS_NORESIZE) && !(winpos->flags & SWP_NOSIZE))
526     {
527         /* don't let the app resize the nonscrollable dimension of a control
528          * that was created with CCS_NORESIZE style
529          * (i.e. height for a horizontal pager, or width for a vertical one) */
530
531         /* except if the current dimension is 0 and app is setting for
532          * first time, then save amount as dimension. - GA 8/01 */
533
534         if (infoPtr->dwStyle & PGS_HORZ)
535             if (!infoPtr->nHeight && winpos->cy)
536                 infoPtr->nHeight = winpos->cy;
537             else
538                 winpos->cy = infoPtr->nHeight;
539         else
540             if (!infoPtr->nWidth && winpos->cx)
541                 infoPtr->nWidth = winpos->cx;
542             else
543                 winpos->cx = infoPtr->nWidth;
544         return 0;
545     }
546
547     return DefWindowProcW (infoPtr->hwndSelf, WM_WINDOWPOSCHANGING, 0, (LPARAM)winpos);
548 }
549
550 static INT
551 PAGER_SetFixedWidth(PAGER_INFO* infoPtr)
552 {
553   /* Must set the non-scrollable dimension to be less than the full height/width
554    * so that NCCalcSize is called.  The Microsoft docs mention 3/4 factor for button
555    * size, and experimentation shows that the effect is almost right. */
556
557     RECT wndRect;
558     INT delta, h;
559     GetWindowRect(infoPtr->hwndSelf, &wndRect);
560
561     /* see what the app says for btn width */
562     PAGER_CalcSize(infoPtr, &infoPtr->nWidth, TRUE);
563
564     if (infoPtr->dwStyle & CCS_NORESIZE)
565     {
566         delta = wndRect.right - wndRect.left - infoPtr->nWidth;
567         if (delta > infoPtr->nButtonSize)
568             infoPtr->nWidth += 4 * infoPtr->nButtonSize / 3;
569         else if (delta > 0)
570             infoPtr->nWidth +=  infoPtr->nButtonSize / 3;
571     }
572
573     h = wndRect.bottom - wndRect.top + infoPtr->nButtonSize;
574
575     TRACE("[%p] infoPtr->nWidth set to %d\n",
576                infoPtr->hwndSelf, infoPtr->nWidth);
577
578     return h;
579 }
580
581 static INT
582 PAGER_SetFixedHeight(PAGER_INFO* infoPtr)
583 {
584   /* Must set the non-scrollable dimension to be less than the full height/width
585    * so that NCCalcSize is called.  The Microsoft docs mention 3/4 factor for button
586    * size, and experimentation shows that the effect is almost right. */
587
588     RECT wndRect;
589     INT delta, w;
590     GetWindowRect(infoPtr->hwndSelf, &wndRect);
591
592     /* see what the app says for btn height */
593     PAGER_CalcSize(infoPtr, &infoPtr->nHeight, FALSE);
594
595     if (infoPtr->dwStyle & CCS_NORESIZE)
596     {
597         delta = wndRect.bottom - wndRect.top - infoPtr->nHeight;
598         if (delta > infoPtr->nButtonSize)
599             infoPtr->nHeight += infoPtr->nButtonSize;
600         else if (delta > 0)
601             infoPtr->nHeight +=  infoPtr->nButtonSize / 3;
602     }
603
604     w = wndRect.right - wndRect.left + infoPtr->nButtonSize;
605
606     TRACE("[%p] infoPtr->nHeight set to %d\n",
607                infoPtr->hwndSelf, infoPtr->nHeight);
608
609     return w;
610 }
611
612 /******************************************************************
613  * For the PGM_RECALCSIZE message (but not the other uses in      *
614  * this module), the native control does only the following:      *
615  *                                                                *
616  *    if (some condition)                                         *
617  *          PostMessageW(hwnd, EM_FMTLINES, 0, 0);                *
618  *    return DefWindowProcW(hwnd, PGM_RECALCSIZE, 0, 0);          *
619  *                                                                *
620  * When we figure out what the "some condition" is we will        *
621  * implement that for the message processing.                     *
622  ******************************************************************/
623
624 static LRESULT
625 PAGER_RecalcSize(PAGER_INFO *infoPtr)
626 {
627     TRACE("[%p]\n", infoPtr->hwndSelf);
628
629     if (infoPtr->hwndChild)
630     {
631         INT scrollRange = PAGER_GetScrollRange(infoPtr);
632
633         if (scrollRange <= 0)
634         {
635             infoPtr->nPos = -1;
636             PAGER_SetPos(infoPtr, 0, FALSE);
637         }
638         else
639             PAGER_PositionChildWnd(infoPtr);
640     }
641
642     return 1;
643 }
644
645
646 static COLORREF
647 PAGER_SetBkColor (PAGER_INFO* infoPtr, COLORREF clrBk)
648 {
649     COLORREF clrTemp = infoPtr->clrBk;
650
651     infoPtr->clrBk = clrBk;
652     TRACE("[%p] %06x\n", infoPtr->hwndSelf, infoPtr->clrBk);
653
654     /* the native control seems to do things this way */
655     SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
656                  SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
657                  SWP_NOZORDER | SWP_NOACTIVATE);
658
659     RedrawWindow(infoPtr->hwndSelf, 0, 0, RDW_ERASE | RDW_INVALIDATE);
660
661     return clrTemp;
662 }
663
664
665 static INT
666 PAGER_SetBorder (PAGER_INFO* infoPtr, INT iBorder)
667 {
668     INT nTemp = infoPtr->nBorder;
669
670     infoPtr->nBorder = iBorder;
671     TRACE("[%p] %d\n", infoPtr->hwndSelf, infoPtr->nBorder);
672
673     PAGER_RecalcSize(infoPtr);
674
675     return nTemp;
676 }
677
678
679 static INT
680 PAGER_SetButtonSize (PAGER_INFO* infoPtr, INT iButtonSize)
681 {
682     INT nTemp = infoPtr->nButtonSize;
683
684     infoPtr->nButtonSize = iButtonSize;
685     TRACE("[%p] %d\n", infoPtr->hwndSelf, infoPtr->nButtonSize);
686
687     PAGER_RecalcSize(infoPtr);
688
689     return nTemp;
690 }
691
692
693 static LRESULT
694 PAGER_SetChild (PAGER_INFO* infoPtr, HWND hwndChild)
695 {
696     INT hw;
697
698     infoPtr->hwndChild = IsWindow (hwndChild) ? hwndChild : 0;
699
700     if (infoPtr->hwndChild)
701     {
702         TRACE("[%p] hwndChild=%p\n", infoPtr->hwndSelf, infoPtr->hwndChild);
703
704         if (infoPtr->dwStyle & PGS_HORZ) {
705             hw = PAGER_SetFixedHeight(infoPtr);
706             /* adjust non-scrollable dimension to fit the child */
707             SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, hw, infoPtr->nHeight,
708                          SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
709                          SWP_NOSIZE | SWP_NOACTIVATE);
710         }
711         else {
712             hw = PAGER_SetFixedWidth(infoPtr);
713             /* adjust non-scrollable dimension to fit the child */
714             SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, infoPtr->nWidth, hw,
715                          SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
716                          SWP_NOSIZE | SWP_NOACTIVATE);
717         }
718
719         /* position child within the page scroller */
720         SetWindowPos(infoPtr->hwndChild, HWND_TOP,
721                      0,0,0,0,
722                      SWP_SHOWWINDOW | SWP_NOSIZE);  /* native is 0 */
723
724         infoPtr->nPos = -1;
725         PAGER_SetPos(infoPtr, 0, FALSE);
726     }
727
728     return 0;
729 }
730
731 static void
732 PAGER_Scroll(PAGER_INFO* infoPtr, INT dir)
733 {
734     NMPGSCROLL nmpgScroll;
735     RECT rcWnd;
736
737     if (infoPtr->hwndChild)
738     {
739         ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL));
740         nmpgScroll.hdr.hwndFrom = infoPtr->hwndSelf;
741         nmpgScroll.hdr.idFrom   = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
742         nmpgScroll.hdr.code = PGN_SCROLL;
743
744         GetWindowRect(infoPtr->hwndSelf, &rcWnd);
745         GetClientRect(infoPtr->hwndSelf, &nmpgScroll.rcParent);
746         nmpgScroll.iXpos = nmpgScroll.iYpos = 0;
747         nmpgScroll.iDir = dir;
748
749         if (infoPtr->dwStyle & PGS_HORZ)
750         {
751             nmpgScroll.iScroll = rcWnd.right - rcWnd.left;
752             nmpgScroll.iXpos = infoPtr->nPos;
753         }
754         else
755         {
756             nmpgScroll.iScroll = rcWnd.bottom - rcWnd.top;
757             nmpgScroll.iYpos = infoPtr->nPos;
758         }
759         nmpgScroll.iScroll -= 2*infoPtr->nButtonSize;
760
761         SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll);
762
763         TRACE("[%p] PGN_SCROLL returns iScroll=%d\n", infoPtr->hwndSelf, nmpgScroll.iScroll);
764
765         if (nmpgScroll.iScroll > 0)
766         {
767             infoPtr->direction = dir;
768
769             if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP)
770                 PAGER_SetPos(infoPtr, infoPtr->nPos - nmpgScroll.iScroll, TRUE);
771             else
772                 PAGER_SetPos(infoPtr, infoPtr->nPos + nmpgScroll.iScroll, TRUE);
773         }
774         else
775             infoPtr->direction = -1;
776     }
777 }
778
779 static LRESULT
780 PAGER_FmtLines(const PAGER_INFO *infoPtr)
781 {
782     /* initiate NCCalcSize to resize client wnd and get size */
783     SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
784                  SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
785                  SWP_NOZORDER | SWP_NOACTIVATE);
786
787     SetWindowPos(infoPtr->hwndChild, 0,
788                  0,0,infoPtr->nWidth,infoPtr->nHeight,
789                  0);
790
791     return DefWindowProcW (infoPtr->hwndSelf, EM_FMTLINES, 0, 0);
792 }
793
794 static LRESULT
795 PAGER_Create (HWND hwnd, const CREATESTRUCTW *lpcs)
796 {
797     PAGER_INFO *infoPtr;
798
799     /* allocate memory for info structure */
800     infoPtr = Alloc (sizeof(PAGER_INFO));
801     if (!infoPtr) return -1;
802     SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
803
804     /* set default settings */
805     infoPtr->hwndSelf = hwnd;
806     infoPtr->hwndChild = NULL;
807     infoPtr->hwndNotify = lpcs->hwndParent;
808     infoPtr->dwStyle = lpcs->style;
809     infoPtr->clrBk = GetSysColor(COLOR_BTNFACE);
810     infoPtr->nBorder = 0;
811     infoPtr->nButtonSize = 12;
812     infoPtr->nPos = 0;
813     infoPtr->nWidth = 0;
814     infoPtr->nHeight = 0;
815     infoPtr->bForward = FALSE;
816     infoPtr->bCapture = FALSE;
817     infoPtr->TLbtnState = PGF_INVISIBLE;
818     infoPtr->BRbtnState = PGF_INVISIBLE;
819     infoPtr->direction = -1;
820
821     if (infoPtr->dwStyle & PGS_DRAGNDROP)
822         FIXME("[%p] Drag and Drop style is not implemented yet.\n", infoPtr->hwndSelf);
823
824     return 0;
825 }
826
827
828 static LRESULT
829 PAGER_Destroy (PAGER_INFO *infoPtr)
830 {
831     SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
832     Free (infoPtr);  /* free pager info data */
833     return 0;
834 }
835
836 static LRESULT
837 PAGER_NCCalcSize(PAGER_INFO* infoPtr, WPARAM wParam, LPRECT lpRect)
838 {
839     RECT rcChild, rcWindow;
840
841     /*
842      * lpRect points to a RECT struct.  On entry, the struct
843      * contains the proposed wnd rectangle for the window.
844      * On exit, the struct should contain the screen
845      * coordinates of the corresponding window's client area.
846      */
847
848     DefWindowProcW (infoPtr->hwndSelf, WM_NCCALCSIZE, wParam, (LPARAM)lpRect);
849
850     TRACE("orig rect=%s\n", wine_dbgstr_rect(lpRect));
851
852     GetWindowRect (infoPtr->hwndChild, &rcChild);
853     MapWindowPoints (0, infoPtr->hwndSelf, (LPPOINT)&rcChild, 2); /* FIXME: RECT != 2 POINTS */
854     GetWindowRect (infoPtr->hwndSelf, &rcWindow);
855
856     if (infoPtr->dwStyle & PGS_HORZ)
857     {
858         infoPtr->nWidth = lpRect->right - lpRect->left;
859         PAGER_CalcSize (infoPtr, &infoPtr->nWidth, TRUE);
860
861         if (infoPtr->TLbtnState && (lpRect->left + infoPtr->nButtonSize < lpRect->right))
862             lpRect->left += infoPtr->nButtonSize;
863         if (infoPtr->BRbtnState && (lpRect->right - infoPtr->nButtonSize > lpRect->left))
864             lpRect->right -= infoPtr->nButtonSize;
865     }
866     else
867     {
868         infoPtr->nHeight = lpRect->bottom - lpRect->top;
869         PAGER_CalcSize (infoPtr, &infoPtr->nHeight, FALSE);
870
871         if (infoPtr->TLbtnState && (lpRect->top + infoPtr->nButtonSize < lpRect->bottom))
872             lpRect->top += infoPtr->nButtonSize;
873         if (infoPtr->BRbtnState && (lpRect->bottom - infoPtr->nButtonSize > lpRect->top))
874             lpRect->bottom -= infoPtr->nButtonSize;
875     }
876
877     TRACE("nPos=%d, nHeight=%d, window=%s\n",
878           infoPtr->nPos, infoPtr->nHeight,
879           wine_dbgstr_rect(&rcWindow));
880
881     TRACE("[%p] client rect set to %dx%d at (%d,%d) BtnState[%d,%d]\n",
882           infoPtr->hwndSelf, lpRect->right-lpRect->left, lpRect->bottom-lpRect->top,
883           lpRect->left, lpRect->top,
884           infoPtr->TLbtnState, infoPtr->BRbtnState);
885
886     return 0;
887 }
888
889 static LRESULT
890 PAGER_NCPaint (const PAGER_INFO* infoPtr, HRGN hRgn)
891 {
892     RECT rcBottomRight, rcTopLeft;
893     HDC hdc;
894
895     if (infoPtr->dwStyle & WS_MINIMIZE)
896         return 0;
897
898     DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)hRgn, 0);
899
900     if (!(hdc = GetDCEx (infoPtr->hwndSelf, 0, DCX_USESTYLE | DCX_WINDOW)))
901         return 0;
902
903     PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, FALSE);
904
905     PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft,
906                      infoPtr->dwStyle & PGS_HORZ, TRUE, infoPtr->TLbtnState);
907     PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight,
908                      infoPtr->dwStyle & PGS_HORZ, FALSE, infoPtr->BRbtnState);
909
910     ReleaseDC( infoPtr->hwndSelf, hdc );
911     return 0;
912 }
913
914 static INT
915 PAGER_HitTest (const PAGER_INFO* infoPtr, const POINT * pt)
916 {
917     RECT clientRect, rcTopLeft, rcBottomRight;
918     POINT ptWindow;
919
920     GetClientRect (infoPtr->hwndSelf, &clientRect);
921
922     if (PtInRect(&clientRect, *pt))
923     {
924         TRACE("child\n");
925         return -1;
926     }
927
928     ptWindow = *pt;
929     PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, TRUE);
930
931     if ((infoPtr->TLbtnState != PGF_INVISIBLE) && PtInRect(&rcTopLeft, ptWindow))
932     {
933         TRACE("PGB_TOPORLEFT\n");
934         return PGB_TOPORLEFT;
935     }
936     else if ((infoPtr->BRbtnState != PGF_INVISIBLE) && PtInRect(&rcBottomRight, ptWindow))
937     {
938         TRACE("PGB_BOTTOMORRIGHT\n");
939         return PGB_BOTTOMORRIGHT;
940     }
941
942     TRACE("nowhere\n");
943     return -1;
944 }
945
946 static LRESULT
947 PAGER_NCHitTest (const PAGER_INFO* infoPtr, INT x, INT y)
948 {
949     POINT pt;
950     INT nHit;
951
952     pt.x = x;
953     pt.y = y;
954
955     ScreenToClient (infoPtr->hwndSelf, &pt);
956     nHit = PAGER_HitTest(infoPtr, &pt);
957
958     return (nHit < 0) ? HTTRANSPARENT : HTCLIENT;
959 }
960
961 static LRESULT
962 PAGER_MouseMove (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
963 {
964     POINT clpt, pt;
965     RECT wnrect, *btnrect = NULL;
966     BOOL topLeft = FALSE;
967     INT btnstate = 0;
968     INT hit;
969     HDC hdc;
970
971     pt.x = x;
972     pt.y = y;
973
974     TRACE("[%p] to (%d,%d)\n", infoPtr->hwndSelf, x, y);
975     ClientToScreen(infoPtr->hwndSelf, &pt);
976     GetWindowRect(infoPtr->hwndSelf, &wnrect);
977     if (PtInRect(&wnrect, pt)) {
978         RECT TLbtnrect, BRbtnrect;
979         PAGER_GetButtonRects(infoPtr, &TLbtnrect, &BRbtnrect, FALSE);
980
981         clpt = pt;
982         MapWindowPoints(0, infoPtr->hwndSelf, &clpt, 1);
983         hit = PAGER_HitTest(infoPtr, &clpt);
984         if ((hit == PGB_TOPORLEFT) && (infoPtr->TLbtnState == PGF_NORMAL))
985         {
986             topLeft = TRUE;
987             btnrect = &TLbtnrect;
988             infoPtr->TLbtnState = PGF_HOT;
989             btnstate = infoPtr->TLbtnState;
990         }
991         else if ((hit == PGB_BOTTOMORRIGHT) && (infoPtr->BRbtnState == PGF_NORMAL))
992         {
993             topLeft = FALSE;
994             btnrect = &BRbtnrect;
995             infoPtr->BRbtnState = PGF_HOT;
996             btnstate = infoPtr->BRbtnState;
997         }
998
999         /* If in one of the buttons the capture and draw buttons */
1000         if (btnrect)
1001         {
1002             TRACE("[%p] draw btn (%s), Capture %s, style %08x\n",
1003                   infoPtr->hwndSelf, wine_dbgstr_rect(btnrect),
1004                   (infoPtr->bCapture) ? "TRUE" : "FALSE",
1005                   infoPtr->dwStyle);
1006             if (!infoPtr->bCapture)
1007             {
1008                 TRACE("[%p] SetCapture\n", infoPtr->hwndSelf);
1009                 SetCapture(infoPtr->hwndSelf);
1010                 infoPtr->bCapture = TRUE;
1011             }
1012             if (infoPtr->dwStyle & PGS_AUTOSCROLL)
1013                 SetTimer(infoPtr->hwndSelf, TIMERID1, 0x3e, 0);
1014             hdc = GetWindowDC(infoPtr->hwndSelf);
1015             /* OffsetRect(wnrect, 0 | 1, 0 | 1) */
1016             PAGER_DrawButton(hdc, infoPtr->clrBk, *btnrect,
1017                              infoPtr->dwStyle & PGS_HORZ, topLeft, btnstate);
1018             ReleaseDC(infoPtr->hwndSelf, hdc);
1019             return 0;
1020         }
1021     }
1022
1023     /* If we think we are captured, then do release */
1024     if (infoPtr->bCapture && (WindowFromPoint(pt) != infoPtr->hwndSelf))
1025     {
1026         NMHDR nmhdr;
1027
1028         infoPtr->bCapture = FALSE;
1029
1030         if (GetCapture() == infoPtr->hwndSelf)
1031         {
1032             ReleaseCapture();
1033
1034             if (infoPtr->TLbtnState == PGF_GRAYED)
1035             {
1036                 infoPtr->TLbtnState = PGF_INVISIBLE;
1037                 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
1038                              SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
1039                              SWP_NOZORDER | SWP_NOACTIVATE);
1040             }
1041             else if (infoPtr->TLbtnState == PGF_HOT)
1042             {
1043                 infoPtr->TLbtnState = PGF_NORMAL;
1044                 /* FIXME: just invalidate button rect */
1045                 RedrawWindow(infoPtr->hwndSelf, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
1046             }
1047
1048             if (infoPtr->BRbtnState == PGF_GRAYED)
1049             {
1050                 infoPtr->BRbtnState = PGF_INVISIBLE;
1051                 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
1052                              SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
1053                              SWP_NOZORDER | SWP_NOACTIVATE);
1054             }
1055             else if (infoPtr->BRbtnState == PGF_HOT)
1056             {
1057                 infoPtr->BRbtnState = PGF_NORMAL;
1058                 /* FIXME: just invalidate button rect */
1059                 RedrawWindow(infoPtr->hwndSelf, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
1060             }
1061
1062             /* Notify parent of released mouse capture */
1063                 memset(&nmhdr, 0, sizeof(NMHDR));
1064                 nmhdr.hwndFrom = infoPtr->hwndSelf;
1065                 nmhdr.idFrom   = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1066                 nmhdr.code = NM_RELEASEDCAPTURE;
1067                 SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, 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=%08x, 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 (const 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 = y;
1219     else
1220         infoPtr->nWidth = x;
1221
1222     return PAGER_RecalcSize(infoPtr);
1223 }
1224
1225
1226 static LRESULT 
1227 PAGER_StyleChanged(PAGER_INFO *infoPtr, WPARAM wStyleType, const STYLESTRUCT *lpss)
1228 {
1229     DWORD oldStyle = infoPtr->dwStyle;
1230
1231     TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\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, (short)LOWORD(lParam), (short)HIWORD(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, (short)LOWORD(lParam), (short)HIWORD(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, (short)LOWORD(lParam), (short)HIWORD(lParam));
1325
1326         case WM_LBUTTONDOWN:
1327             return PAGER_LButtonDown (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
1328
1329         case WM_LBUTTONUP:
1330             return PAGER_LButtonUp (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
1331
1332         case WM_ERASEBKGND:
1333             return PAGER_EraseBackground (infoPtr, (HDC)wParam);
1334
1335         case WM_TIMER:
1336             return PAGER_Timer (infoPtr, (INT)wParam);
1337
1338         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 }