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