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