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