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