Cleaned up a bit, removed todos for OpenThread, avoid TerminateThread
[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 static LRESULT
602 PAGER_RecalcSize(HWND hwnd)
603 {
604     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
605
606     TRACE("[%04x]\n", hwnd);
607
608     if (infoPtr->hwndChild)
609     {
610         INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr);
611
612         if (scrollRange <= 0)
613         {
614             infoPtr->nPos = -1;
615             PAGER_SetPos(hwnd, 0, FALSE);
616         }
617         else 
618         {
619             PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, TRUE);
620             PAGER_PositionChildWnd(hwnd, infoPtr);
621         }
622     }
623
624     return 1;
625 }
626
627
628 static LRESULT
629 PAGER_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
630 {
631     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
632     COLORREF clrTemp = infoPtr->clrBk;
633
634     infoPtr->clrBk = (COLORREF)lParam;
635     TRACE("[%04x] %06lx\n", hwnd, infoPtr->clrBk);
636
637     PAGER_RecalcSize(hwnd);
638     SendMessageA(hwnd, WM_NCPAINT, (WPARAM)0, (LPARAM)0);
639
640     return (LRESULT)clrTemp;
641 }
642
643
644 static LRESULT
645 PAGER_SetBorder (HWND hwnd, WPARAM wParam, LPARAM lParam)
646 {
647     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
648     INT nTemp = infoPtr->nBorder;
649
650     infoPtr->nBorder = (INT)lParam;
651     TRACE("[%04x] %d\n", hwnd, infoPtr->nBorder);
652
653     PAGER_RecalcSize(hwnd);
654
655     return (LRESULT)nTemp;
656 }
657
658
659 static LRESULT
660 PAGER_SetButtonSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
661 {
662     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
663     INT nTemp = infoPtr->nButtonSize;
664
665     infoPtr->nButtonSize = (INT)lParam;
666     TRACE("[%04x] %d\n", hwnd, infoPtr->nButtonSize);
667
668     PAGER_RecalcSize(hwnd);
669
670     return (LRESULT)nTemp;
671 }
672
673
674 static LRESULT
675 PAGER_SetChild (HWND hwnd, WPARAM wParam, LPARAM lParam)
676 {
677     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
678     INT hw;
679
680     infoPtr->hwndChild = IsWindow ((HWND)lParam) ? (HWND)lParam : 0;
681
682     if (infoPtr->hwndChild)
683     {
684         TRACE("[%04x] hwndChild=%04x\n", hwnd, infoPtr->hwndChild);
685
686         if (PAGER_IsHorizontal(hwnd)) {
687             hw = PAGER_SetFixedHeight(hwnd, infoPtr);
688             /* adjust non-scrollable dimension to fit the child */
689             SetWindowPos(hwnd, 0, 0,0, hw, infoPtr->nHeight, 
690                          SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
691                          SWP_NOSIZE | SWP_NOACTIVATE);
692         }
693         else {
694             hw = PAGER_SetFixedWidth(hwnd, infoPtr);
695             /* adjust non-scrollable dimension to fit the child */
696             SetWindowPos(hwnd, 0, 0,0, infoPtr->nWidth, hw, 
697                          SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
698                          SWP_NOSIZE | SWP_NOACTIVATE);
699         }
700
701         /* position child within the page scroller */
702         SetWindowPos(infoPtr->hwndChild, HWND_TOP,
703                      0,0,0,0,
704                      SWP_SHOWWINDOW | SWP_NOSIZE);  /* native is 0 */
705
706         infoPtr->nPos = -1;
707         PAGER_SetPos(hwnd, 0, FALSE);
708     }
709
710     return 0;
711 }
712
713 static void
714 PAGER_Scroll(HWND hwnd, INT dir)
715 {
716     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
717     NMPGSCROLL nmpgScroll;
718     RECT rcWnd;
719
720     if (infoPtr->hwndChild)
721     {
722         ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL));
723         nmpgScroll.hdr.hwndFrom = hwnd;
724         nmpgScroll.hdr.idFrom   = GetWindowLongA (hwnd, GWL_ID);
725         nmpgScroll.hdr.code = PGN_SCROLL;
726
727         GetWindowRect(hwnd, &rcWnd);  
728         GetClientRect(hwnd, &nmpgScroll.rcParent);  
729         nmpgScroll.iXpos = nmpgScroll.iYpos = 0;
730         nmpgScroll.iDir = dir;
731
732         if (PAGER_IsHorizontal(hwnd))
733         {
734             nmpgScroll.iScroll = rcWnd.right - rcWnd.left;
735             nmpgScroll.iXpos = infoPtr->nPos;
736         }
737         else
738         {
739             nmpgScroll.iScroll = rcWnd.bottom - rcWnd.top;
740             nmpgScroll.iYpos = infoPtr->nPos;
741         }
742         nmpgScroll.iScroll -= 2*infoPtr->nButtonSize;
743   
744         SendMessageA (hwnd, WM_NOTIFY,
745                     (WPARAM)nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll);
746   
747         TRACE("[%04x] PGN_SCROLL returns iScroll=%d\n", hwnd, nmpgScroll.iScroll);
748
749         if (nmpgScroll.iScroll > 0)
750         {
751             infoPtr->direction = dir;
752
753             if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP)
754                 PAGER_SetPos(hwnd, infoPtr->nPos - nmpgScroll.iScroll, TRUE);
755             else
756                 PAGER_SetPos(hwnd, infoPtr->nPos + nmpgScroll.iScroll, TRUE);
757         }
758         else
759             infoPtr->direction = -1;
760     }
761 }
762
763 static LRESULT
764 PAGER_FmtLines(HWND hwnd)
765 {
766     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
767
768     /* initiate NCCalcSize to resize client wnd and get size */
769     SetWindowPos(hwnd, 0, 0,0,0,0, 
770                  SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
771                  SWP_NOZORDER | SWP_NOACTIVATE);
772
773     SetWindowPos(infoPtr->hwndChild, 0, 
774                  0,0,infoPtr->nWidth,infoPtr->nHeight, 
775                  0);
776
777     return DefWindowProcA (hwnd, EM_FMTLINES, 0, 0);
778 }
779
780 static LRESULT
781 PAGER_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
782 {
783     PAGER_INFO *infoPtr;
784     DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
785
786     /* allocate memory for info structure */
787     infoPtr = (PAGER_INFO *)COMCTL32_Alloc (sizeof(PAGER_INFO));
788     SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
789
790     /* set default settings */
791     infoPtr->hwndChild = (HWND)NULL;
792     infoPtr->bNoResize = dwStyle & CCS_NORESIZE;
793     infoPtr->clrBk = GetSysColor(COLOR_BTNFACE);
794     infoPtr->nBorder = 0;
795     infoPtr->nButtonSize = 12;
796     infoPtr->nPos = 0;
797     infoPtr->nWidth = 0;
798     infoPtr->nHeight = 0;
799     infoPtr->bForward = FALSE;
800     infoPtr->TLbtnState = PGF_INVISIBLE;
801     infoPtr->BRbtnState = PGF_INVISIBLE;
802     infoPtr->direction = -1;
803
804     if (dwStyle & PGS_AUTOSCROLL)
805         FIXME("[%04x] Autoscroll style is not implemented yet.\n", hwnd);
806     if (dwStyle & PGS_DRAGNDROP)
807         FIXME("[%04x] Drag and Drop style is not implemented yet.\n", hwnd);
808     /*
809          * If neither horizontal nor vertical style specified, default to vertical.
810          * This is probably not necessary, since the style may be set later on as
811          * the control is initialized, but just in case it isn't, set it here.
812          */
813     if (!(dwStyle & PGS_HORZ) && !(dwStyle & PGS_VERT))
814     {
815         dwStyle |= PGS_VERT;
816         SetWindowLongA(hwnd, GWL_STYLE, dwStyle);
817     }
818
819     return 0;
820 }
821
822
823 static LRESULT
824 PAGER_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
825 {
826     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
827     /* free pager info data */
828     COMCTL32_Free (infoPtr);
829     SetWindowLongA (hwnd, 0, 0);
830     return 0;
831 }
832
833 static LRESULT
834 PAGER_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
835 {
836     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
837     LPRECT lpRect = (LPRECT)lParam;
838     RECT rcChildw, rcmyw, wnrc, lbrc, rbrc;
839     POINT cursor;
840     BOOL resizeClient = FALSE;
841     BOOL repaintBtns = FALSE;
842     INT scrollRange;
843
844     /*
845      * lParam points to a RECT struct.  On entry, the struct
846      * contains the proposed wnd rectangle for the window. 
847      * On exit, the struct should contain the screen
848      * coordinates of the corresponding window's client area.
849      */
850         
851     DefWindowProcA (hwnd, WM_NCCALCSIZE, wParam, lParam);
852
853     if (PAGER_IsHorizontal(hwnd))
854     {
855         infoPtr->nWidth = lpRect->right - lpRect->left;
856         PAGER_CalcSize (hwnd, &infoPtr->nWidth, TRUE);
857         GetWindowRect (infoPtr->hwndChild, &rcChildw);
858         MapWindowPoints (0, hwnd, (LPPOINT)&rcChildw, 2);
859         GetCursorPos (&cursor);
860         GetWindowRect (hwnd, &rcmyw);
861
862         /* Reset buttons and hide any grey ones */
863         scrollRange = infoPtr->nWidth - (rcmyw.right - rcmyw.bottom);
864
865         TRACE("nPos=%d, scrollrange=%d, nWidth=%d, myw=(%d,%d)-(%d,%d)\n",
866               infoPtr->nPos, scrollRange, infoPtr->nWidth,
867               rcmyw.left, rcmyw.top, rcmyw.right, rcmyw.bottom);
868         PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
869         PAGER_HideGrayBtns(infoPtr, &resizeClient);
870
871         if (PtInRect (&rcmyw, cursor)) {
872             GetWindowRect (hwnd, &wnrc);
873             lbrc = wnrc;
874             lbrc.right = lbrc.left + infoPtr->nButtonSize;
875             rbrc = wnrc;
876             rbrc.left = rbrc.right - infoPtr->nButtonSize;
877             TRACE("horz lb rect=(%d,%d)-(%d,%d), rb rect=(%d,%d)-(%d,%d)\n",
878                   lbrc.left, lbrc.top, lbrc.right, lbrc.bottom,
879                   rbrc.left, rbrc.top, rbrc.right, rbrc.bottom);
880             if (PtInRect (&lbrc, cursor) && infoPtr->TLbtnState)
881                 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
882             if (PtInRect (&rbrc, cursor) && infoPtr->BRbtnState)
883                 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
884         }
885         if (infoPtr->TLbtnState) /* != PGF_INVISIBLE */
886             lpRect->left += infoPtr->nButtonSize;
887         if (infoPtr->BRbtnState) 
888             lpRect->right -= infoPtr->nButtonSize;
889     }
890     else
891     {
892         /* native does: (from trace of IE4 opening "Favorites" frame) 
893          *        DefWindowProc
894          *        WM_NOITFY  PGN_CALCSIZE w/ dwFlag=2
895          *        GetWindowRect (child, &rc)
896          *        MapWindowPoints (0, syspager, &rc, 2)
897          *        GetCursorPos( &cur )
898          *        GetWindowRect (syspager, &rc2)
899          *        PtInRect (&rc2, cur.x, cur.y) rtns 0
900          *        returns with rect empty
901          */
902         infoPtr->nHeight = lpRect->bottom - lpRect->top;
903         PAGER_CalcSize (hwnd, &infoPtr->nHeight, FALSE);
904         GetWindowRect (infoPtr->hwndChild, &rcChildw);
905         MapWindowPoints (0, hwnd, (LPPOINT)&rcChildw, 2);
906         GetCursorPos (&cursor);
907         GetWindowRect (hwnd, &rcmyw);
908
909         /* Reset buttons and hide any grey ones */
910         scrollRange = infoPtr->nHeight - (rcmyw.bottom - rcmyw.top);
911
912         TRACE("nPos=%d, scrollrange=%d, nHeigth=%d, myw=(%d,%d)-(%d,%d)\n",
913               infoPtr->nPos, scrollRange, infoPtr->nHeight,
914               rcmyw.left, rcmyw.top,
915               rcmyw.right, rcmyw.bottom);
916         PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
917         PAGER_HideGrayBtns(infoPtr, &resizeClient);
918
919         if (PtInRect (&rcmyw, cursor)) {
920
921             /* native does:
922              *    GetWindowRect(pager, &rc)
923              *    PtInRect(btn-left????, cur.x, cur.y)
924              *    if true -> ???
925              *    PtInRect(btn-right????, cur.x, cur.y)
926              *    if true
927              *      RedrawWindow(pager, 0, 0, 5)
928              *      return TRUE
929              */
930
931             GetWindowRect (hwnd, &wnrc);
932             lbrc = wnrc;
933             lbrc.right = lbrc.left + infoPtr->nButtonSize;
934             rbrc = wnrc;
935             rbrc.left = rbrc.right - infoPtr->nButtonSize;
936             TRACE("vert lb rect=(%d,%d)-(%d,%d), rb rect=(%d,%d)-(%d,%d)\n",
937                   lbrc.left, lbrc.top, lbrc.right, lbrc.bottom,
938                   rbrc.left, rbrc.top, rbrc.right, rbrc.bottom);
939             if (PtInRect (&lbrc, cursor) && infoPtr->TLbtnState)
940                 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
941             if (PtInRect (&rbrc, cursor) && infoPtr->BRbtnState)
942                 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
943         }
944         if (infoPtr->TLbtnState)
945             lpRect->top += infoPtr->nButtonSize;
946         if (infoPtr->BRbtnState)
947             lpRect->bottom -= infoPtr->nButtonSize;
948         /* ???? */
949         if ((lpRect->bottom < 0) || (lpRect->bottom > infoPtr->nHeight))
950             lpRect->bottom = infoPtr->nHeight;
951     }
952
953     TRACE("[%04x] client rect set to %dx%d at (%d,%d) BtnState[%d,%d]\n", 
954           hwnd, lpRect->right-lpRect->left, lpRect->bottom-lpRect->top,
955           lpRect->left, lpRect->top,
956           infoPtr->TLbtnState, infoPtr->BRbtnState);
957
958     return 0;
959 }
960
961 static LRESULT
962 PAGER_NCPaint (HWND hwnd, WPARAM wParam, LPARAM lParam)
963 {
964     PAGER_INFO* infoPtr = PAGER_GetInfoPtr(hwnd);
965     DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
966     RECT rcWindow, rcBottomRight, rcTopLeft;
967     HDC hdc;
968     BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
969
970     if (dwStyle & WS_MINIMIZE)
971         return 0;
972
973     DefWindowProcA (hwnd, WM_NCPAINT, wParam, lParam);
974
975     if (!(hdc = GetDCEx (hwnd, 0, DCX_USESTYLE | DCX_WINDOW)))
976         return 0;
977
978     GetWindowRect (hwnd, &rcWindow);
979     OffsetRect (&rcWindow, -rcWindow.left, -rcWindow.top);
980
981     rcTopLeft = rcBottomRight = rcWindow;
982     if (bHorizontal)
983     {
984         rcTopLeft.right = rcTopLeft.left + infoPtr->nButtonSize;
985         rcBottomRight.left = rcBottomRight.right - infoPtr->nButtonSize;
986     }
987     else
988     {
989         rcTopLeft.bottom = rcTopLeft.top + infoPtr->nButtonSize;
990         rcBottomRight.top = rcBottomRight.bottom - infoPtr->nButtonSize;
991     }
992
993     PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft,
994                      bHorizontal, TRUE, infoPtr->TLbtnState);
995     PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight,
996                      bHorizontal, FALSE, infoPtr->BRbtnState); 
997
998     ReleaseDC( hwnd, hdc );
999     return 0;
1000 }
1001
1002 static INT 
1003 PAGER_HitTest (HWND hwnd, LPPOINT pt)
1004 {
1005     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1006     RECT clientRect;
1007     BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
1008
1009     GetClientRect (hwnd, &clientRect);
1010
1011     if (PtInRect(&clientRect, *pt))
1012     {
1013        /* TRACE("HTCLIENT\n"); */
1014         return HTCLIENT;
1015     }
1016
1017     if (infoPtr->TLbtnState && infoPtr->TLbtnState != PGF_GRAYED)
1018     {
1019         if (bHorizontal)
1020         {
1021             if (pt->x < clientRect.left)
1022             {
1023                 /* TRACE("HTLEFT\n"); */
1024                 return HTLEFT;
1025             }
1026         }
1027         else
1028         {
1029             if (pt->y < clientRect.top)
1030             {
1031                 /* TRACE("HTTOP\n"); */
1032                 return HTTOP;
1033             }
1034         }
1035     }
1036
1037     if (infoPtr->BRbtnState && infoPtr->BRbtnState != PGF_GRAYED)
1038     {
1039         if (bHorizontal)
1040         {
1041             if (pt->x > clientRect.right)
1042             {
1043                 /* TRACE("HTRIGHT\n"); */
1044                 return HTRIGHT;
1045             }
1046         }
1047         else
1048         {
1049             if (pt->y > clientRect.bottom)
1050             {
1051                /* TRACE("HTBOTTOM\n"); */
1052                 return HTBOTTOM;
1053             }
1054         }
1055     }
1056
1057     /* TRACE("HTNOWHERE\n"); */
1058     return HTNOWHERE;
1059 }
1060
1061 static LRESULT
1062 PAGER_NCHitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
1063 {
1064     POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
1065     ScreenToClient (hwnd, &pt);
1066     return PAGER_HitTest(hwnd, &pt);
1067 }
1068
1069 static LRESULT
1070 PAGER_SetCursor( HWND hwnd, WPARAM wParam, LPARAM lParam )
1071 {
1072     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1073     BOOL notCaptured = FALSE;
1074
1075     switch(LOWORD(lParam))
1076     {
1077         case HTLEFT:
1078         case HTTOP:
1079             if ((notCaptured = infoPtr->TLbtnState != PGF_HOT))
1080                 infoPtr->TLbtnState = PGF_HOT;
1081             break;
1082         case HTRIGHT:
1083         case HTBOTTOM:
1084             if ((notCaptured = infoPtr->BRbtnState != PGF_HOT))
1085                infoPtr->BRbtnState = PGF_HOT;
1086             break;
1087         default:
1088             return FALSE;
1089     }
1090
1091     if (notCaptured)
1092     {
1093         TRACKMOUSEEVENT trackinfo;
1094
1095         TRACE("[%04x] SetCapture\n", hwnd);
1096         SetCapture(hwnd);
1097
1098         trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
1099         trackinfo.dwFlags = TME_QUERY;
1100         trackinfo.hwndTrack = hwnd;
1101         trackinfo.dwHoverTime = HOVER_DEFAULT;
1102
1103         /* call _TrackMouseEvent to see if we are currently tracking for this hwnd */
1104         _TrackMouseEvent(&trackinfo);
1105
1106         /* Make sure tracking is enabled so we receive a WM_MOUSELEAVE message */
1107         if(!(trackinfo.dwFlags & TME_LEAVE)) {
1108             trackinfo.dwFlags = TME_LEAVE; /* notify upon leaving */
1109  
1110            /* call TRACKMOUSEEVENT so we receive a WM_MOUSELEAVE message */
1111            /* and can properly deactivate the hot button */
1112            _TrackMouseEvent(&trackinfo);
1113         }
1114
1115         SendMessageA(hwnd, WM_NCPAINT, 0, 0); 
1116     }
1117
1118     return TRUE;
1119 }
1120
1121 static LRESULT
1122 PAGER_MouseLeave (HWND hwnd, WPARAM wParam, LPARAM lParam)
1123 {
1124     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1125
1126     KillTimer (hwnd, TIMERID1);
1127     KillTimer (hwnd, TIMERID2);
1128
1129     TRACE("[%04x] ReleaseCapture\n", hwnd);
1130     ReleaseCapture();
1131
1132     /* Notify parent of released mouse capture */
1133     {
1134         NMHDR nmhdr;
1135         ZeroMemory (&nmhdr, sizeof (NMHDR));
1136         nmhdr.hwndFrom = hwnd;
1137         nmhdr.idFrom   = GetWindowLongA (hwnd, GWL_ID);
1138         nmhdr.code = NM_RELEASEDCAPTURE;
1139         SendMessageA (GetParent(hwnd), WM_NOTIFY,
1140                         (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1141     }
1142
1143     /* make HOT btns NORMAL and hide gray btns */
1144     PAGER_UpdateBtns(hwnd, infoPtr, -1, TRUE);
1145
1146     return TRUE;
1147 }
1148
1149 static LRESULT
1150 PAGER_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
1151 {
1152     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1153     BOOL repaintBtns = FALSE;
1154     POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
1155     INT hit;
1156
1157     TRACE("[%04x]\n", hwnd);
1158         
1159     hit = PAGER_HitTest(hwnd, &pt);
1160
1161     /* put btn in DEPRESSED state */
1162     if (hit == HTLEFT || hit == HTTOP)
1163     {
1164         repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED;
1165         infoPtr->TLbtnState = PGF_DEPRESSED;
1166         SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0); 
1167     }
1168     else if (hit == HTRIGHT || hit == HTBOTTOM)
1169     {
1170         repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED;
1171         infoPtr->BRbtnState = PGF_DEPRESSED;
1172         SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0); 
1173     }
1174
1175     if (repaintBtns)
1176         SendMessageA(hwnd, WM_NCPAINT, 0, 0); 
1177
1178     switch(hit)
1179     {
1180     case HTLEFT:
1181         TRACE("[%04x] PGF_SCROLLLEFT\n", hwnd);
1182         PAGER_Scroll(hwnd, PGF_SCROLLLEFT);
1183         break;
1184     case HTTOP:
1185         TRACE("[%04x] PGF_SCROLLUP\n", hwnd);
1186         PAGER_Scroll(hwnd, PGF_SCROLLUP);
1187         break;
1188     case HTRIGHT:
1189         TRACE("[%04x] PGF_SCROLLRIGHT\n", hwnd);
1190         PAGER_Scroll(hwnd, PGF_SCROLLRIGHT);
1191         break;
1192     case HTBOTTOM:
1193         TRACE("[%04x] PGF_SCROLLDOWN\n", hwnd);
1194         PAGER_Scroll(hwnd, PGF_SCROLLDOWN);
1195         break;
1196     default:
1197         break;
1198     }
1199
1200     return TRUE;
1201 }
1202
1203 static LRESULT
1204 PAGER_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
1205 {
1206     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1207     TRACE("[%04x]\n", hwnd);
1208
1209     KillTimer (hwnd, TIMERID1);
1210     KillTimer (hwnd, TIMERID2);
1211
1212     /* make PRESSED btns NORMAL but don't hide gray btns */
1213     PAGER_UpdateBtns(hwnd, infoPtr, -1, FALSE);
1214
1215     return 0;
1216 }
1217
1218 static LRESULT
1219 PAGER_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
1220 {
1221     POINT pt, ptorig;
1222     HDC hdc = (HDC)wParam;
1223     HWND parent;
1224
1225     /* native does:
1226      *   parent = GetParent(pager)
1227      *   pt.x=0; pt.y=0; ?????
1228      *   MapWindowPoints(pager, parent, &pt, 1)
1229      *   OffsetWindowOrgEx(hdc, pt.x, pt.y, &ptorg)
1230      *   SendMessageA(parent, WM_ERASEBKGND, hdc, 0)
1231      *   SetWindowOrgEx(hdc, 0, 0, 0)
1232      */
1233
1234     pt.x = 0;
1235     pt.y = 0;
1236     parent = GetParent(hwnd);
1237     MapWindowPoints(hwnd, parent, &pt, 1);
1238     OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig);
1239     SendMessageA (parent, WM_ERASEBKGND, wParam, lParam);
1240     SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0);
1241
1242
1243 #if 0
1244     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1245     HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
1246     RECT rect;
1247
1248     GetClientRect (hwnd, &rect);
1249     FillRect ((HDC)wParam, &rect, hBrush);
1250
1251     /* background color of the child should be the same as the pager */
1252     if (infoPtr->hwndChild)
1253     {
1254         GetClientRect (infoPtr->hwndChild, &rect);
1255         FillRect ((HDC)wParam, &rect, hBrush);
1256     }
1257
1258     DeleteObject (hBrush);
1259 #endif
1260
1261     return TRUE;
1262 }
1263
1264
1265 static LRESULT
1266 PAGER_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1267 {
1268     /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */
1269
1270     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1271     TRACE("[%04x] %dx%d\n", hwnd, LOWORD(lParam), HIWORD(lParam));
1272
1273     if (PAGER_IsHorizontal(hwnd))
1274         infoPtr->nHeight = HIWORD(lParam);
1275     else
1276         infoPtr->nWidth = LOWORD(lParam);
1277
1278     return PAGER_RecalcSize(hwnd);
1279 }
1280
1281
1282 static LRESULT WINAPI
1283 PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1284 {
1285     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1286
1287     if (!infoPtr && (uMsg != WM_CREATE))
1288         return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1289
1290     switch (uMsg)
1291     {
1292         case EM_FMTLINES:
1293             return PAGER_FmtLines(hwnd);
1294
1295         case PGM_FORWARDMOUSE:
1296             return PAGER_ForwardMouse (hwnd, wParam);
1297
1298         case PGM_GETBKCOLOR:
1299             return PAGER_GetBkColor(hwnd);
1300
1301         case PGM_GETBORDER:
1302             return PAGER_GetBorder(hwnd);
1303
1304         case PGM_GETBUTTONSIZE:
1305             return PAGER_GetButtonSize(hwnd);
1306
1307         case PGM_GETPOS:
1308             return PAGER_GetPos(hwnd);
1309
1310         case PGM_GETBUTTONSTATE:
1311             return PAGER_GetButtonState (hwnd, wParam, lParam);
1312
1313 /*      case PGM_GETDROPTARGET: */
1314
1315         case PGM_RECALCSIZE:
1316             return PAGER_RecalcSize(hwnd);
1317     
1318         case PGM_SETBKCOLOR:
1319             return PAGER_SetBkColor (hwnd, wParam, lParam);
1320
1321         case PGM_SETBORDER:
1322             return PAGER_SetBorder (hwnd, wParam, lParam);
1323
1324         case PGM_SETBUTTONSIZE:
1325             return PAGER_SetButtonSize (hwnd, wParam, lParam);
1326
1327         case PGM_SETCHILD:
1328             return PAGER_SetChild (hwnd, wParam, lParam);
1329
1330         case PGM_SETPOS:
1331             return PAGER_SetPos(hwnd, (INT)lParam, FALSE);
1332
1333         case WM_CREATE:
1334             return PAGER_Create (hwnd, wParam, lParam);
1335
1336         case WM_DESTROY:
1337             return PAGER_Destroy (hwnd, wParam, lParam);
1338
1339         case WM_SIZE:
1340             return PAGER_Size (hwnd, wParam, lParam);
1341
1342         case WM_NCPAINT:
1343             return PAGER_NCPaint (hwnd, wParam, lParam);
1344
1345         case WM_WINDOWPOSCHANGING:
1346             return PAGER_HandleWindowPosChanging (hwnd, wParam, (WINDOWPOS*)lParam);
1347
1348         case WM_NCCALCSIZE:
1349             return PAGER_NCCalcSize (hwnd, wParam, lParam);
1350
1351         case WM_NCHITTEST:
1352             return PAGER_NCHitTest (hwnd, wParam, lParam);
1353
1354         case WM_SETCURSOR:
1355         {
1356             if (hwnd == (HWND)wParam)
1357                 return PAGER_SetCursor(hwnd, wParam, lParam);
1358             else /* its for the child */
1359                 return 0;
1360         }
1361
1362         case WM_MOUSEMOVE:
1363             if (infoPtr->bForward && infoPtr->hwndChild)
1364                 PostMessageA(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam);
1365             return TRUE;                        
1366
1367         case WM_MOUSELEAVE:
1368             return PAGER_MouseLeave (hwnd, wParam, lParam);     
1369
1370         case WM_LBUTTONDOWN:
1371             return PAGER_LButtonDown (hwnd, wParam, lParam);
1372
1373         case WM_LBUTTONUP:
1374             return PAGER_LButtonUp (hwnd, wParam, lParam);
1375
1376         case WM_ERASEBKGND:
1377             return PAGER_EraseBackground (hwnd, wParam, lParam);
1378 /*
1379         case WM_PAINT:
1380             return PAGER_Paint (hwnd, wParam); 
1381 */
1382         case WM_TIMER:
1383             /* if initial timer, kill it and start the repeat timer */
1384             if (wParam == TIMERID1)
1385             {
1386                 KillTimer(hwnd, TIMERID1);
1387                 SetTimer(hwnd, TIMERID2, REPEAT_DELAY, 0);
1388             }
1389
1390             KillTimer(hwnd, TIMERID2);
1391             if (infoPtr->direction > 0)
1392             {
1393                 PAGER_Scroll(hwnd, infoPtr->direction);
1394                 SetTimer(hwnd, TIMERID2, REPEAT_DELAY, 0);          
1395             }
1396             break;
1397
1398         case WM_NOTIFY:
1399         case WM_COMMAND:
1400             return SendMessageA (GetParent (hwnd), uMsg, wParam, lParam);
1401
1402         default:
1403             return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1404     }
1405
1406     return 0;
1407 }
1408
1409
1410 VOID
1411 PAGER_Register (void)
1412 {
1413     WNDCLASSA wndClass;
1414
1415     ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1416     wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS;
1417     wndClass.lpfnWndProc   = (WNDPROC)PAGER_WindowProc;
1418     wndClass.cbClsExtra    = 0;
1419     wndClass.cbWndExtra    = sizeof(PAGER_INFO *);
1420     wndClass.hCursor       = LoadCursorA (0, IDC_ARROWA);
1421     wndClass.hbrBackground = 0;
1422     wndClass.lpszClassName = WC_PAGESCROLLERA;
1423  
1424     RegisterClassA (&wndClass);
1425 }
1426
1427
1428 VOID
1429 PAGER_Unregister (void)
1430 {
1431     UnregisterClassA (WC_PAGESCROLLERA, (HINSTANCE)NULL);
1432 }
1433