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