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