Got rid of GetSysColorPen.
[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("[%08x] 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("[%08x]\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("[%08x]\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("[%08x] 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("[%08x] 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("[%08x] 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("[%08x] 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("[%08x] 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("[%08x] 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("[%08x] 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("[%08x] 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("[%08x] 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("[%08x] 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("[%08x] 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("[%08x]\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("[%08x] %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("[%08x] %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("[%08x] %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("[%08x] hwndChild=%08x\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("[%08x] 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 = (HWND)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("[%08x] Drag and Drop style is not implemented yet.\n", hwnd);
855     /*
856          * If neither horizontal nor vertical style specified, default to vertical.
857          * This is probably not necessary, since the style may be set later on as
858          * the control is initialized, but just in case it isn't, set it here.
859          */
860     if (!(dwStyle & PGS_HORZ) && !(dwStyle & PGS_VERT))
861     {
862         dwStyle |= PGS_VERT;
863         SetWindowLongA(hwnd, GWL_STYLE, dwStyle);
864     }
865
866     return 0;
867 }
868
869
870 static LRESULT
871 PAGER_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
872 {
873     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
874     /* free pager info data */
875     COMCTL32_Free (infoPtr);
876     SetWindowLongA (hwnd, 0, 0);
877     return 0;
878 }
879
880 static LRESULT
881 PAGER_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
882 {
883     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
884     LPRECT lpRect = (LPRECT)lParam;
885     RECT rcChildw, rcmyw, wnrc, ltrc, rbrc;
886     POINT cursor;
887     BOOL resizeClient = FALSE;
888     BOOL repaintBtns = FALSE;
889     INT scrollRange;
890
891     /*
892      * lParam points to a RECT struct.  On entry, the struct
893      * contains the proposed wnd rectangle for the window. 
894      * On exit, the struct should contain the screen
895      * coordinates of the corresponding window's client area.
896      */
897         
898     DefWindowProcA (hwnd, WM_NCCALCSIZE, wParam, lParam);
899
900     TRACE("orig rect=(%d,%d)-(%d,%d)\n",
901           lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
902
903     if (PAGER_IsHorizontal(hwnd))
904     {
905         infoPtr->nWidth = lpRect->right - lpRect->left;
906         PAGER_CalcSize (hwnd, &infoPtr->nWidth, TRUE);
907         GetWindowRect (infoPtr->hwndChild, &rcChildw);
908         MapWindowPoints (0, hwnd, (LPPOINT)&rcChildw, 2);
909         GetCursorPos (&cursor);
910         GetWindowRect (hwnd, &rcmyw);
911
912         /* Reset buttons and hide any grey ones */
913         scrollRange = infoPtr->nWidth - (rcmyw.right - rcmyw.left);
914
915         TRACE("nPos=%d, scrollrange=%d, nHeigth=%d, myw=(%d,%d)-(%d,%d), cursor=(%ld,%ld)\n",
916               infoPtr->nPos, scrollRange, infoPtr->nHeight,
917               rcmyw.left, rcmyw.top,
918               rcmyw.right, rcmyw.bottom,
919               cursor.x, cursor.y);
920         PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
921         PAGER_HideGrayBtns(infoPtr, &resizeClient);
922
923         if (PtInRect (&rcmyw, cursor)) {
924             GetWindowRect (hwnd, &wnrc);
925             ltrc = wnrc;
926             ltrc.right = ltrc.left + infoPtr->nButtonSize;
927             rbrc = wnrc;
928             rbrc.left = rbrc.right - infoPtr->nButtonSize;
929             TRACE("horz lt rect=(%d,%d)-(%d,%d), rb rect=(%d,%d)-(%d,%d)\n",
930                   ltrc.left, ltrc.top, ltrc.right, ltrc.bottom,
931                   rbrc.left, rbrc.top, rbrc.right, rbrc.bottom);
932             if (PtInRect (&ltrc, cursor) && infoPtr->TLbtnState)
933                 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
934             if (PtInRect (&rbrc, cursor) && infoPtr->BRbtnState)
935                 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
936         }
937         if (infoPtr->TLbtnState && (lpRect->left + infoPtr->nButtonSize < lpRect->right))
938             lpRect->left += infoPtr->nButtonSize;
939         if (infoPtr->BRbtnState && (lpRect->right - infoPtr->nButtonSize > lpRect->left))
940             lpRect->right -= infoPtr->nButtonSize;
941     }
942     else
943     {
944         /* native does: (from trace of IE4 opening "Favorites" frame) 
945          *        DefWindowProc
946          *        WM_NOITFY  PGN_CALCSIZE w/ dwFlag=2
947          *        GetWindowRect (child, &rc)
948          *        MapWindowPoints (0, syspager, &rc, 2)
949          *        GetCursorPos( &cur )
950          *        GetWindowRect (syspager, &rc2)
951          *        PtInRect (&rc2, cur.x, cur.y) rtns 0
952          *        returns with rect empty
953          */
954         infoPtr->nHeight = lpRect->bottom - lpRect->top;
955         PAGER_CalcSize (hwnd, &infoPtr->nHeight, FALSE);
956         GetWindowRect (infoPtr->hwndChild, &rcChildw);
957         MapWindowPoints (0, hwnd, (LPPOINT)&rcChildw, 2);
958         GetCursorPos (&cursor);
959         GetWindowRect (hwnd, &rcmyw);
960
961         /* Reset buttons and hide any grey ones */
962         scrollRange = infoPtr->nHeight - (rcmyw.bottom - rcmyw.top);
963
964         TRACE("nPos=%d, scrollrange=%d, nHeigth=%d, myw=(%d,%d)-(%d,%d), cursor=(%ld,%ld)\n",
965               infoPtr->nPos, scrollRange, infoPtr->nHeight,
966               rcmyw.left, rcmyw.top,
967               rcmyw.right, rcmyw.bottom,
968               cursor.x, cursor.y);
969         PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
970         PAGER_HideGrayBtns(infoPtr, &resizeClient);
971
972         if (PtInRect (&rcmyw, cursor)) {
973
974             /* native does:
975              *    GetWindowRect(pager, &rc)
976              *    PtInRect(btn-left????, cur.x, cur.y)
977              *    if true -> ???
978              *    PtInRect(btn-right????, cur.x, cur.y)
979              *    if true
980              *      RedrawWindow(pager, 0, 0, 5)
981              *      return TRUE
982              */
983
984             GetWindowRect (hwnd, &wnrc);
985             ltrc = wnrc;
986             ltrc.right = ltrc.left + infoPtr->nButtonSize;
987             rbrc = wnrc;
988             rbrc.left = rbrc.right - infoPtr->nButtonSize;
989             TRACE("vert lt rect=(%d,%d)-(%d,%d), rb rect=(%d,%d)-(%d,%d)\n",
990                   ltrc.left, ltrc.top, ltrc.right, ltrc.bottom,
991                   rbrc.left, rbrc.top, rbrc.right, rbrc.bottom);
992             if (PtInRect (&ltrc, cursor) && infoPtr->TLbtnState)
993                 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
994             if (PtInRect (&rbrc, cursor) && infoPtr->BRbtnState)
995                 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
996         }
997         if (infoPtr->TLbtnState && (lpRect->top + infoPtr->nButtonSize < lpRect->bottom))
998             lpRect->top += infoPtr->nButtonSize;
999         if (infoPtr->BRbtnState && (lpRect->bottom - infoPtr->nButtonSize > lpRect->top))
1000             lpRect->bottom -= infoPtr->nButtonSize;
1001         /* ???? */
1002         if ((lpRect->bottom < 0) || (lpRect->bottom > infoPtr->nHeight))
1003             lpRect->bottom = infoPtr->nHeight;
1004     }
1005
1006     TRACE("[%08x] client rect set to %dx%d at (%d,%d) BtnState[%d,%d]\n", 
1007           hwnd, lpRect->right-lpRect->left, lpRect->bottom-lpRect->top,
1008           lpRect->left, lpRect->top,
1009           infoPtr->TLbtnState, infoPtr->BRbtnState);
1010
1011     return 0;
1012 }
1013
1014 static LRESULT
1015 PAGER_NCPaint (HWND hwnd, WPARAM wParam, LPARAM lParam)
1016 {
1017     PAGER_INFO* infoPtr = PAGER_GetInfoPtr(hwnd);
1018     DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
1019     RECT rcWindow, rcBottomRight, rcTopLeft;
1020     HDC hdc;
1021     BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
1022
1023     if (dwStyle & WS_MINIMIZE)
1024         return 0;
1025
1026     DefWindowProcA (hwnd, WM_NCPAINT, wParam, lParam);
1027
1028     if (!(hdc = GetDCEx (hwnd, 0, DCX_USESTYLE | DCX_WINDOW)))
1029         return 0;
1030
1031     GetWindowRect (hwnd, &rcWindow);
1032     OffsetRect (&rcWindow, -rcWindow.left, -rcWindow.top);
1033
1034     rcTopLeft = rcBottomRight = rcWindow;
1035     if (bHorizontal)
1036     {
1037         rcTopLeft.right = rcTopLeft.left + infoPtr->nButtonSize;
1038         rcBottomRight.left = rcBottomRight.right - infoPtr->nButtonSize;
1039     }
1040     else
1041     {
1042         rcTopLeft.bottom = rcTopLeft.top + infoPtr->nButtonSize;
1043         rcBottomRight.top = rcBottomRight.bottom - infoPtr->nButtonSize;
1044     }
1045
1046     PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft,
1047                      bHorizontal, TRUE, infoPtr->TLbtnState);
1048     PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight,
1049                      bHorizontal, FALSE, infoPtr->BRbtnState); 
1050
1051     ReleaseDC( hwnd, hdc );
1052     return 0;
1053 }
1054
1055 static INT 
1056 PAGER_HitTest (HWND hwnd, LPPOINT pt)
1057 {
1058     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1059     RECT clientRect;
1060     BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
1061
1062     GetClientRect (hwnd, &clientRect);
1063
1064     if (PtInRect(&clientRect, *pt))
1065     {
1066         TRACE("HTCLIENT\n");
1067         return HTCLIENT;
1068     }
1069
1070     if (infoPtr->TLbtnState && infoPtr->TLbtnState != PGF_GRAYED)
1071     {
1072         if (bHorizontal)
1073         {
1074             if (pt->x < clientRect.left)
1075             {
1076                 TRACE("HTLEFT\n");
1077                 return HTLEFT;
1078             }
1079         }
1080         else
1081         {
1082             if (pt->y < clientRect.top)
1083             {
1084                 TRACE("HTTOP\n");
1085                 return HTTOP;
1086             }
1087         }
1088     }
1089
1090     if (infoPtr->BRbtnState && infoPtr->BRbtnState != PGF_GRAYED)
1091     {
1092         if (bHorizontal)
1093         {
1094             if (pt->x > clientRect.right)
1095             {
1096                 TRACE("HTRIGHT\n");
1097                 return HTRIGHT;
1098             }
1099         }
1100         else
1101         {
1102             if (pt->y > clientRect.bottom)
1103             {
1104                 TRACE("HTBOTTOM\n");
1105                 return HTBOTTOM;
1106             }
1107         }
1108     }
1109
1110     TRACE("HTNOWHERE\n");
1111     return HTNOWHERE;
1112 }
1113
1114 static LRESULT
1115 PAGER_NCHitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
1116 {
1117     POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
1118     ScreenToClient (hwnd, &pt);
1119     return PAGER_HitTest(hwnd, &pt);
1120 }
1121
1122 static LRESULT
1123 PAGER_SetCursor( HWND hwnd, WPARAM wParam, LPARAM lParam )
1124 {
1125     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1126     BOOL notCaptured = FALSE;
1127
1128     switch(LOWORD(lParam))
1129     {
1130         case HTLEFT:
1131         case HTTOP:
1132             if ((notCaptured = infoPtr->TLbtnState != PGF_HOT))
1133                 infoPtr->TLbtnState = PGF_HOT;
1134             break;
1135         case HTRIGHT:
1136         case HTBOTTOM:
1137             if ((notCaptured = infoPtr->BRbtnState != PGF_HOT))
1138                infoPtr->BRbtnState = PGF_HOT;
1139             break;
1140         default:
1141             return FALSE;
1142     }
1143
1144     if (notCaptured)
1145     {
1146         PAGER_CaptureandTrack(infoPtr, hwnd);
1147
1148         SendMessageA(hwnd, WM_NCPAINT, 0, 0); 
1149     }
1150
1151     return TRUE;
1152 }
1153
1154 static LRESULT
1155 PAGER_MouseLeave (HWND hwnd, WPARAM wParam, LPARAM lParam)
1156 {
1157     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1158
1159     KillTimer (hwnd, TIMERID1);
1160     KillTimer (hwnd, TIMERID2);
1161
1162     TRACE("[%08x] ReleaseCapture\n", hwnd);
1163     ReleaseCapture();
1164     infoPtr->bCapture = FALSE;
1165
1166     /* Notify parent of released mouse capture */
1167     {
1168         NMHDR nmhdr;
1169         ZeroMemory (&nmhdr, sizeof (NMHDR));
1170         nmhdr.hwndFrom = hwnd;
1171         nmhdr.idFrom   = GetWindowLongA (hwnd, GWL_ID);
1172         nmhdr.code = NM_RELEASEDCAPTURE;
1173         SendMessageA (GetParent(hwnd), WM_NOTIFY,
1174                         (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1175     }
1176
1177     /* make HOT btns NORMAL and hide gray btns */
1178     PAGER_UpdateBtns(hwnd, infoPtr, -1, TRUE);
1179
1180     return TRUE;
1181 }
1182
1183 static LRESULT
1184 PAGER_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
1185 {
1186     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1187     POINT clpt, pt = {SLOWORD(lParam), SHIWORD(lParam)};
1188     RECT wnrect, TLbtnrect, BRbtnrect, *btnrect = NULL;
1189     DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
1190     BOOL topLeft = FALSE;
1191     INT btnstate = 0;
1192     INT hit;
1193     HDC hdc;
1194
1195     TRACE("[%08x] to (%ld,%ld)\n", hwnd, pt.x, pt.y);
1196     ClientToScreen(hwnd, &pt);
1197     GetWindowRect(hwnd, &wnrect);
1198     if (PtInRect(&wnrect, pt)) {
1199         TLbtnrect = wnrect;
1200         BRbtnrect = wnrect;
1201         if (dwStyle & PGS_HORZ) {
1202             TLbtnrect.right = TLbtnrect.left + infoPtr->nButtonSize;
1203             BRbtnrect.left = BRbtnrect.right - infoPtr->nButtonSize;
1204         }
1205         else {
1206             TLbtnrect.bottom = TLbtnrect.top + infoPtr->nButtonSize;
1207             BRbtnrect.top = BRbtnrect.bottom - infoPtr->nButtonSize;
1208         }
1209
1210         clpt = pt;
1211         MapWindowPoints(0, hwnd, &clpt, 1);
1212         hit = PAGER_HitTest(hwnd, &clpt);
1213         if (hit == HTLEFT || hit == HTTOP) {
1214             topLeft = TRUE;
1215             btnrect = &TLbtnrect;
1216             infoPtr->TLbtnState = PGF_DEPRESSED;
1217             btnstate = infoPtr->TLbtnState;
1218         }
1219         else if (hit == HTRIGHT || hit == HTBOTTOM) {
1220             topLeft = FALSE;
1221             btnrect = &BRbtnrect;
1222             infoPtr->BRbtnState = PGF_DEPRESSED;
1223             btnstate = infoPtr->BRbtnState;
1224         }
1225
1226         /* If in one of the buttons the capture and draw buttons */
1227         if (btnrect) {
1228             TRACE("[%08x] draw btn (%d,%d)-(%d,%d), Capture %s, style %08lx\n",
1229                   hwnd, btnrect->left, btnrect->top, 
1230                   btnrect->right, btnrect->bottom,
1231                   (infoPtr->bCapture) ? "TRUE" : "FALSE",
1232                   dwStyle);
1233             if (!infoPtr->bCapture)
1234                 PAGER_CaptureandTrack(infoPtr, hwnd);
1235             if (dwStyle & PGS_AUTOSCROLL)
1236                 SetTimer(hwnd, TIMERID1, 0x3e, 0);
1237             MapWindowPoints(0, hwnd, (LPPOINT)btnrect, 2);
1238             hdc = GetWindowDC(hwnd);
1239             /* OffsetRect(wnrect, 0 | 1, 0 | 1) */
1240             PAGER_DrawButton(hdc, infoPtr->clrBk, *btnrect,
1241                              PAGER_IsHorizontal(hwnd), topLeft, btnstate);
1242             ReleaseDC(hwnd, hdc);
1243             return DefWindowProcA (hwnd, WM_MOUSEMOVE, wParam, lParam);
1244         }
1245     }
1246
1247     /* If we think we are captured, then do release */
1248     if (infoPtr->bCapture) {
1249         infoPtr->bCapture = FALSE;
1250
1251         if (GetCapture() == hwnd) {
1252             ReleaseCapture();
1253             /* Notify parent of released mouse capture */
1254             {
1255                 NMHDR nmhdr;
1256                 ZeroMemory (&nmhdr, sizeof (NMHDR));
1257                 nmhdr.hwndFrom = hwnd;
1258                 nmhdr.idFrom   = GetWindowLongA (hwnd, GWL_ID);
1259                 nmhdr.code = NM_RELEASEDCAPTURE;
1260                 SendMessageA (GetParent(hwnd), WM_NOTIFY,
1261                               (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1262             }
1263         }
1264         if (IsWindow(hwnd))
1265             KillTimer(hwnd, TIMERID1);
1266     }
1267     return DefWindowProcA (hwnd, WM_MOUSEMOVE, wParam, lParam);
1268 }
1269
1270 static LRESULT
1271 PAGER_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
1272 {
1273     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1274     BOOL repaintBtns = FALSE;
1275     POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
1276     INT hit;
1277
1278     TRACE("[%08x] at (%d,%d)\n", hwnd, SLOWORD(lParam), SHIWORD(lParam));
1279         
1280     hit = PAGER_HitTest(hwnd, &pt);
1281
1282     /* put btn in DEPRESSED state */
1283     if (hit == HTLEFT || hit == HTTOP)
1284     {
1285         repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED;
1286         infoPtr->TLbtnState = PGF_DEPRESSED;
1287         SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0); 
1288     }
1289     else if (hit == HTRIGHT || hit == HTBOTTOM)
1290     {
1291         repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED;
1292         infoPtr->BRbtnState = PGF_DEPRESSED;
1293         SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0); 
1294     }
1295
1296     if (repaintBtns)
1297         SendMessageA(hwnd, WM_NCPAINT, 0, 0); 
1298
1299     switch(hit)
1300     {
1301     case HTLEFT:
1302         TRACE("[%08x] PGF_SCROLLLEFT\n", hwnd);
1303         PAGER_Scroll(hwnd, PGF_SCROLLLEFT);
1304         break;
1305     case HTTOP:
1306         TRACE("[%08x] PGF_SCROLLUP\n", hwnd);
1307         PAGER_Scroll(hwnd, PGF_SCROLLUP);
1308         break;
1309     case HTRIGHT:
1310         TRACE("[%08x] PGF_SCROLLRIGHT\n", hwnd);
1311         PAGER_Scroll(hwnd, PGF_SCROLLRIGHT);
1312         break;
1313     case HTBOTTOM:
1314         TRACE("[%08x] PGF_SCROLLDOWN\n", hwnd);
1315         PAGER_Scroll(hwnd, PGF_SCROLLDOWN);
1316         break;
1317     default:
1318         break;
1319     }
1320
1321     return TRUE;
1322 }
1323
1324 static LRESULT
1325 PAGER_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
1326 {
1327     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1328     TRACE("[%08x]\n", hwnd);
1329
1330     KillTimer (hwnd, TIMERID1);
1331     KillTimer (hwnd, TIMERID2);
1332
1333     /* make PRESSED btns NORMAL but don't hide gray btns */
1334     PAGER_UpdateBtns(hwnd, infoPtr, -1, FALSE);
1335
1336     return 0;
1337 }
1338
1339 static LRESULT
1340 PAGER_NCLButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
1341 {
1342     POINT pt = {SLOWORD(lParam), SHIWORD(lParam)};
1343
1344     TRACE("[%08x] at (%d,%d)\n", hwnd, SLOWORD(lParam), SHIWORD(lParam));
1345     MapWindowPoints(0, hwnd, &pt, 1);
1346     lParam = MAKELONG(pt.x, pt.y);
1347     return PAGER_LButtonDown (hwnd, wParam, lParam);
1348 }
1349
1350 static LRESULT
1351 PAGER_Timer (HWND hwnd, WPARAM wParam)
1352 {
1353     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1354     DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
1355     INT dir;
1356
1357     /* if initial timer, kill it and start the repeat timer */
1358     if (wParam == TIMERID1) {
1359         if (PAGER_IsHorizontal(hwnd)) {
1360             dir = (infoPtr->TLbtnState & PGF_DEPRESSED) ? 
1361                 PGF_SCROLLLEFT : PGF_SCROLLRIGHT;
1362         }
1363         else {
1364             dir = (infoPtr->TLbtnState & PGF_DEPRESSED) ? 
1365                 PGF_SCROLLUP : PGF_SCROLLDOWN;
1366         }
1367         TRACE("[%08x] TIMERID1: style=%08lx, dir=%d\n", hwnd, dwStyle, dir);
1368         KillTimer(hwnd, TIMERID1);
1369         SetTimer(hwnd, TIMERID1, REPEAT_DELAY, 0);
1370         if (dwStyle & PGS_AUTOSCROLL) {
1371             PAGER_Scroll(hwnd, dir);
1372             SetWindowPos(hwnd, 0,0,0,0,0, 
1373                          SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
1374                          SWP_NOZORDER | SWP_NOACTIVATE);
1375         }
1376         return 0;
1377
1378     }
1379
1380     TRACE("[%08x] TIMERID2: dir=%d\n", hwnd, infoPtr->direction);
1381     KillTimer(hwnd, TIMERID2);
1382     if (infoPtr->direction > 0) {
1383         PAGER_Scroll(hwnd, infoPtr->direction);
1384         SetTimer(hwnd, TIMERID2, REPEAT_DELAY, 0);          
1385     }
1386     return 0;
1387 }
1388
1389 static LRESULT
1390 PAGER_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
1391 {
1392     POINT pt, ptorig;
1393     HDC hdc = (HDC)wParam;
1394     HWND parent;
1395
1396     /* native does:
1397      *   parent = GetParent(pager)
1398      *   pt.x=0; pt.y=0; ?????
1399      *   MapWindowPoints(pager, parent, &pt, 1)
1400      *   OffsetWindowOrgEx(hdc, pt.x, pt.y, &ptorg)
1401      *   SendMessageA(parent, WM_ERASEBKGND, hdc, 0)
1402      *   SetWindowOrgEx(hdc, 0, 0, 0)
1403      */
1404
1405     pt.x = 0;
1406     pt.y = 0;
1407     parent = GetParent(hwnd);
1408     MapWindowPoints(hwnd, parent, &pt, 1);
1409     OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig);
1410     SendMessageA (parent, WM_ERASEBKGND, wParam, lParam);
1411     SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0);
1412
1413
1414 #if 0
1415     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1416     HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
1417     RECT rect;
1418
1419     GetClientRect (hwnd, &rect);
1420     FillRect ((HDC)wParam, &rect, hBrush);
1421
1422     /* background color of the child should be the same as the pager */
1423     if (infoPtr->hwndChild)
1424     {
1425         GetClientRect (infoPtr->hwndChild, &rect);
1426         FillRect ((HDC)wParam, &rect, hBrush);
1427     }
1428
1429     DeleteObject (hBrush);
1430 #endif
1431
1432     return TRUE;
1433 }
1434
1435
1436 static LRESULT
1437 PAGER_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1438 {
1439     /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */
1440
1441     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1442     TRACE("[%08x] %dx%d\n", hwnd, SLOWORD(lParam), SHIWORD(lParam));
1443
1444     if (PAGER_IsHorizontal(hwnd))
1445         infoPtr->nHeight = SHIWORD(lParam);
1446     else
1447         infoPtr->nWidth = SLOWORD(lParam);
1448
1449     return PAGER_RecalcSize(hwnd);
1450 }
1451
1452
1453 static LRESULT WINAPI
1454 PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1455 {
1456     PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1457
1458     if (!infoPtr && (uMsg != WM_CREATE))
1459         return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1460
1461     switch (uMsg)
1462     {
1463         case EM_FMTLINES:
1464             return PAGER_FmtLines(hwnd);
1465
1466         case PGM_FORWARDMOUSE:
1467             return PAGER_ForwardMouse (hwnd, wParam);
1468
1469         case PGM_GETBKCOLOR:
1470             return PAGER_GetBkColor(hwnd);
1471
1472         case PGM_GETBORDER:
1473             return PAGER_GetBorder(hwnd);
1474
1475         case PGM_GETBUTTONSIZE:
1476             return PAGER_GetButtonSize(hwnd);
1477
1478         case PGM_GETPOS:
1479             return PAGER_GetPos(hwnd);
1480
1481         case PGM_GETBUTTONSTATE:
1482             return PAGER_GetButtonState (hwnd, wParam, lParam);
1483
1484 /*      case PGM_GETDROPTARGET: */
1485
1486         case PGM_RECALCSIZE:
1487             return PAGER_RecalcSize(hwnd);
1488     
1489         case PGM_SETBKCOLOR:
1490             return PAGER_SetBkColor (hwnd, wParam, lParam);
1491
1492         case PGM_SETBORDER:
1493             return PAGER_SetBorder (hwnd, wParam, lParam);
1494
1495         case PGM_SETBUTTONSIZE:
1496             return PAGER_SetButtonSize (hwnd, wParam, lParam);
1497
1498         case PGM_SETCHILD:
1499             return PAGER_SetChild (hwnd, wParam, lParam);
1500
1501         case PGM_SETPOS:
1502             return PAGER_SetPos(hwnd, (INT)lParam, FALSE);
1503
1504         case WM_CREATE:
1505             return PAGER_Create (hwnd, wParam, lParam);
1506
1507         case WM_DESTROY:
1508             return PAGER_Destroy (hwnd, wParam, lParam);
1509
1510         case WM_SIZE:
1511             return PAGER_Size (hwnd, wParam, lParam);
1512
1513         case WM_NCPAINT:
1514             return PAGER_NCPaint (hwnd, wParam, lParam);
1515
1516         case WM_WINDOWPOSCHANGING:
1517             return PAGER_HandleWindowPosChanging (hwnd, wParam, (WINDOWPOS*)lParam);
1518
1519         case WM_NCCALCSIZE:
1520             return PAGER_NCCalcSize (hwnd, wParam, lParam);
1521
1522         case WM_NCHITTEST:
1523             return PAGER_NCHitTest (hwnd, wParam, lParam);
1524
1525         case WM_SETCURSOR:
1526         {
1527             if (hwnd == (HWND)wParam)
1528                 return PAGER_SetCursor(hwnd, wParam, lParam);
1529             else /* its for the child */
1530                 return 0;
1531         }
1532
1533         case WM_MOUSEMOVE:
1534             if (infoPtr->bForward && infoPtr->hwndChild)
1535                 PostMessageA(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam);
1536             return PAGER_MouseMove (hwnd, wParam, lParam);
1537
1538         case WM_MOUSELEAVE:
1539             return PAGER_MouseLeave (hwnd, wParam, lParam);     
1540
1541         case WM_NCLBUTTONDOWN:
1542             return PAGER_NCLButtonDown (hwnd, wParam, lParam);
1543
1544         case WM_LBUTTONDOWN:
1545             return PAGER_LButtonDown (hwnd, wParam, lParam);
1546
1547         case WM_NCLBUTTONUP:
1548         case WM_LBUTTONUP:
1549             return PAGER_LButtonUp (hwnd, wParam, lParam);
1550
1551         case WM_ERASEBKGND:
1552             return PAGER_EraseBackground (hwnd, wParam, lParam);
1553 /*
1554         case WM_PAINT:
1555             return PAGER_Paint (hwnd, wParam); 
1556 */
1557         case WM_TIMER:
1558             return PAGER_Timer (hwnd, wParam);
1559
1560         case WM_NOTIFY:
1561         case WM_COMMAND:
1562             return SendMessageA (GetParent (hwnd), uMsg, wParam, lParam);
1563
1564         default:
1565             return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1566     }
1567
1568     return 0;
1569 }
1570
1571
1572 VOID
1573 PAGER_Register (void)
1574 {
1575     WNDCLASSA wndClass;
1576
1577     ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1578     wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS;
1579     wndClass.lpfnWndProc   = (WNDPROC)PAGER_WindowProc;
1580     wndClass.cbClsExtra    = 0;
1581     wndClass.cbWndExtra    = sizeof(PAGER_INFO *);
1582     wndClass.hCursor       = LoadCursorA (0, IDC_ARROWA);
1583     wndClass.hbrBackground = 0;
1584     wndClass.lpszClassName = WC_PAGESCROLLERA;
1585  
1586     RegisterClassA (&wndClass);
1587 }
1588
1589
1590 VOID
1591 PAGER_Unregister (void)
1592 {
1593     UnregisterClassA (WC_PAGESCROLLERA, (HINSTANCE)NULL);
1594 }
1595