Fixed header dependencies to be fully compatible with the Windows
[wine/multimedia.git] / dlls / comctl32 / pager.c
blob74173499136e1e40977df3ac9b1be0b986f5f819
1 /*
2 * Pager control
4 * Copyright 1998, 1999 Eric Kohl
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.
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.
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
20 * NOTES
21 * Tested primarily with the controlspy Pager application.
22 * Susan Farley (susan@codeweavers.com)
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.
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 "wine/debug.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(pager);
43 typedef struct
45 HWND hwndChild; /* handle of the contained wnd */
46 BOOL bNoResize; /* set when created with CCS_NORESIZE */
47 COLORREF clrBk; /* background color */
48 INT nBorder; /* border size for the control */
49 INT nButtonSize;/* size of the pager btns */
50 INT nPos; /* scroll position */
51 INT nWidth; /* from child wnd's response to PGN_CALCSIZE */
52 INT nHeight; /* from child wnd's response to PGN_CALCSIZE */
53 BOOL bForward; /* forward WM_MOUSEMOVE msgs to the contained wnd */
54 BOOL bCapture; /* we have captured the mouse */
55 INT TLbtnState; /* state of top or left btn */
56 INT BRbtnState; /* state of bottom or right btn */
57 INT direction; /* direction of the scroll, (e.g. PGF_SCROLLUP) */
58 } PAGER_INFO;
60 #define PAGER_GetInfoPtr(hwnd) ((PAGER_INFO *)GetWindowLongA(hwnd, 0))
61 #define PAGER_IsHorizontal(hwnd) ((GetWindowLongA (hwnd, GWL_STYLE) & PGS_HORZ))
63 #define MIN_ARROW_WIDTH 8
64 #define MIN_ARROW_HEIGHT 5
66 #define TIMERID1 1
67 #define TIMERID2 2
68 #define INITIAL_DELAY 500
69 #define REPEAT_DELAY 50
71 /* the horizontal arrows are:
73 * 01234 01234
74 * 1 * *
75 * 2 ** **
76 * 3*** ***
77 * 4*** ***
78 * 5 ** **
79 * 6 * *
80 * 7
83 static void
84 PAGER_DrawHorzArrow (HDC hdc, RECT r, INT colorRef, BOOL left)
86 INT x, y, w, h;
87 HPEN hPen, hOldPen;
89 w = r.right - r.left + 1;
90 h = r.bottom - r.top + 1;
91 if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
92 return; /* refuse to draw partial arrow */
94 if (!(hPen = CreatePen( PS_SOLID, 1, GetSysColor( colorRef )))) return;
95 hOldPen = SelectObject ( hdc, hPen );
96 if (left)
98 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 3;
99 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
100 MoveToEx (hdc, x, y, NULL);
101 LineTo (hdc, x--, y+5); y++;
102 MoveToEx (hdc, x, y, NULL);
103 LineTo (hdc, x--, y+3); y++;
104 MoveToEx (hdc, x, y, NULL);
105 LineTo (hdc, x, y+1);
107 else
109 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
110 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
111 MoveToEx (hdc, x, y, NULL);
112 LineTo (hdc, x++, y+5); y++;
113 MoveToEx (hdc, x, y, NULL);
114 LineTo (hdc, x++, y+3); y++;
115 MoveToEx (hdc, x, y, NULL);
116 LineTo (hdc, x, y+1);
119 SelectObject( hdc, hOldPen );
120 DeleteObject( hPen );
123 /* the vertical arrows are:
125 * 01234567 01234567
126 * 1****** **
127 * 2 **** ****
128 * 3 ** ******
132 static void
133 PAGER_DrawVertArrow (HDC hdc, RECT r, INT colorRef, BOOL up)
135 INT x, y, w, h;
136 HPEN hPen, hOldPen;
138 w = r.right - r.left + 1;
139 h = r.bottom - r.top + 1;
140 if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
141 return; /* refuse to draw partial arrow */
143 if (!(hPen = CreatePen( PS_SOLID, 1, GetSysColor( colorRef )))) return;
144 hOldPen = SelectObject ( hdc, hPen );
145 if (up)
147 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
148 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 3;
149 MoveToEx (hdc, x, y, NULL);
150 LineTo (hdc, x+5, y--); x++;
151 MoveToEx (hdc, x, y, NULL);
152 LineTo (hdc, x+3, y--); x++;
153 MoveToEx (hdc, x, y, NULL);
154 LineTo (hdc, x+1, y);
156 else
158 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
159 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
160 MoveToEx (hdc, x, y, NULL);
161 LineTo (hdc, x+5, y++); x++;
162 MoveToEx (hdc, x, y, NULL);
163 LineTo (hdc, x+3, y++); x++;
164 MoveToEx (hdc, x, y, NULL);
165 LineTo (hdc, x+1, y);
168 SelectObject( hdc, hOldPen );
169 DeleteObject( hPen );
172 static void
173 PAGER_DrawButton(HDC hdc, COLORREF clrBk, RECT arrowRect,
174 BOOL horz, BOOL topLeft, INT btnState)
176 HBRUSH hBrush, hOldBrush;
177 RECT rc = arrowRect;
179 if (!btnState) /* PGF_INVISIBLE */
180 return;
182 if ((rc.right - rc.left <= 0) || (rc.bottom - rc.top <= 0))
183 return;
185 hBrush = CreateSolidBrush(clrBk);
186 hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
188 FillRect(hdc, &rc, hBrush);
190 if (btnState == PGF_HOT)
192 DrawEdge( hdc, &rc, BDR_RAISEDINNER, BF_RECT);
193 if (horz)
194 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
195 else
196 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
198 else if (btnState == PGF_NORMAL)
200 DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
201 if (horz)
202 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
203 else
204 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
206 else if (btnState == PGF_DEPRESSED)
208 DrawEdge( hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
209 if (horz)
210 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
211 else
212 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
214 else if (btnState == PGF_GRAYED)
216 DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
217 if (horz)
219 PAGER_DrawHorzArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
220 rc.left++, rc.top++; rc.right++, rc.bottom++;
221 PAGER_DrawHorzArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
223 else
225 PAGER_DrawVertArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
226 rc.left++, rc.top++; rc.right++, rc.bottom++;
227 PAGER_DrawVertArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
231 SelectObject( hdc, hOldBrush );
232 DeleteObject(hBrush);
235 static void PAGER_CaptureandTrack(PAGER_INFO *infoPtr, HWND hwnd)
237 TRACKMOUSEEVENT trackinfo;
239 TRACE("[%p] SetCapture\n", hwnd);
240 SetCapture(hwnd);
241 infoPtr->bCapture = TRUE;
243 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
244 trackinfo.dwFlags = TME_QUERY;
245 trackinfo.hwndTrack = hwnd;
246 trackinfo.dwHoverTime = HOVER_DEFAULT;
248 /* call _TrackMouseEvent to see if we are currently tracking for this hwnd */
249 _TrackMouseEvent(&trackinfo);
251 /* Make sure tracking is enabled so we receive a WM_MOUSELEAVE message */
252 if(!(trackinfo.dwFlags & TME_LEAVE)) {
253 trackinfo.dwFlags = TME_LEAVE; /* notify upon leaving */
255 /* call TRACKMOUSEEVENT so we receive a WM_MOUSELEAVE message */
256 /* and can properly deactivate the hot button */
257 _TrackMouseEvent(&trackinfo);
262 /* << PAGER_GetDropTarget >> */
264 static inline LRESULT
265 PAGER_ForwardMouse (HWND hwnd, WPARAM wParam)
267 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
268 TRACE("[%p]\n", hwnd);
270 infoPtr->bForward = (BOOL)wParam;
272 return 0;
275 static inline LRESULT
276 PAGER_GetButtonState (HWND hwnd, WPARAM wParam, LPARAM lParam)
278 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
279 LRESULT btnState = PGF_INVISIBLE;
280 INT btn = (INT)lParam;
281 TRACE("[%p]\n", hwnd);
283 if (btn == PGB_TOPORLEFT)
284 btnState = infoPtr->TLbtnState;
285 else if (btn == PGB_BOTTOMORRIGHT)
286 btnState = infoPtr->BRbtnState;
288 return btnState;
292 static inline LRESULT
293 PAGER_GetPos(HWND hwnd)
295 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
296 TRACE("[%p] returns %d\n", hwnd, infoPtr->nPos);
297 return (LRESULT)infoPtr->nPos;
300 static inline LRESULT
301 PAGER_GetButtonSize(HWND hwnd)
303 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
304 TRACE("[%p] returns %d\n", hwnd, infoPtr->nButtonSize);
305 return (LRESULT)infoPtr->nButtonSize;
308 static inline LRESULT
309 PAGER_GetBorder(HWND hwnd)
311 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
312 TRACE("[%p] returns %d\n", hwnd, infoPtr->nBorder);
313 return (LRESULT)infoPtr->nBorder;
316 static inline LRESULT
317 PAGER_GetBkColor(HWND hwnd)
319 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
320 TRACE("[%p] returns %06lx\n", hwnd, infoPtr->clrBk);
321 return (LRESULT)infoPtr->clrBk;
324 static void
325 PAGER_CalcSize (HWND hwnd, INT* size, BOOL getWidth)
327 NMPGCALCSIZE nmpgcs;
328 ZeroMemory (&nmpgcs, sizeof (NMPGCALCSIZE));
329 nmpgcs.hdr.hwndFrom = hwnd;
330 nmpgcs.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
331 nmpgcs.hdr.code = PGN_CALCSIZE;
332 nmpgcs.dwFlag = getWidth ? PGF_CALCWIDTH : PGF_CALCHEIGHT;
333 nmpgcs.iWidth = getWidth ? *size : 0;
334 nmpgcs.iHeight = getWidth ? 0 : *size;
335 SendMessageA (GetParent (hwnd), WM_NOTIFY,
336 (WPARAM)nmpgcs.hdr.idFrom, (LPARAM)&nmpgcs);
338 *size = getWidth ? nmpgcs.iWidth : nmpgcs.iHeight;
340 TRACE("[%p] PGN_CALCSIZE returns %s=%d\n", hwnd,
341 getWidth ? "width" : "height", *size);
344 static void
345 PAGER_PositionChildWnd(HWND hwnd, PAGER_INFO* infoPtr)
347 if (infoPtr->hwndChild)
349 RECT rcClient;
350 int nPos = infoPtr->nPos;
352 /* compensate for a grayed btn, which will soon become invisible */
353 if (infoPtr->TLbtnState == PGF_GRAYED)
354 nPos += infoPtr->nButtonSize;
356 GetClientRect(hwnd, &rcClient);
358 if (PAGER_IsHorizontal(hwnd))
360 int wndSize = max(0, rcClient.right - rcClient.left);
361 if (infoPtr->nWidth < wndSize)
362 infoPtr->nWidth = wndSize;
364 TRACE("[%p] SWP %dx%d at (%d,%d)\n", hwnd,
365 infoPtr->nWidth, infoPtr->nHeight,
366 -nPos, 0);
367 SetWindowPos(infoPtr->hwndChild, 0,
368 -nPos, 0,
369 infoPtr->nWidth, infoPtr->nHeight,
370 SWP_NOZORDER);
372 else
374 int wndSize = max(0, rcClient.bottom - rcClient.top);
375 if (infoPtr->nHeight < wndSize)
376 infoPtr->nHeight = wndSize;
378 TRACE("[%p] SWP %dx%d at (%d,%d)\n", hwnd,
379 infoPtr->nWidth, infoPtr->nHeight,
380 0, -nPos);
381 SetWindowPos(infoPtr->hwndChild, 0,
382 0, -nPos,
383 infoPtr->nWidth, infoPtr->nHeight,
384 SWP_NOZORDER);
387 InvalidateRect(infoPtr->hwndChild, NULL, TRUE);
391 static INT
392 PAGER_GetScrollRange(HWND hwnd, PAGER_INFO* infoPtr)
394 INT scrollRange = 0;
396 if (infoPtr->hwndChild)
398 INT wndSize, childSize;
399 RECT wndRect;
400 GetWindowRect(hwnd, &wndRect);
402 if (PAGER_IsHorizontal(hwnd))
404 wndSize = wndRect.right - wndRect.left;
405 PAGER_CalcSize(hwnd, &infoPtr->nWidth, TRUE);
406 childSize = infoPtr->nWidth;
408 else
410 wndSize = wndRect.bottom - wndRect.top;
411 PAGER_CalcSize(hwnd, &infoPtr->nHeight, FALSE);
412 childSize = infoPtr->nHeight;
415 TRACE("childSize = %d, wndSize = %d\n", childSize, wndSize);
416 if (childSize > wndSize)
417 scrollRange = childSize - wndSize + infoPtr->nButtonSize;
420 TRACE("[%p] returns %d\n", hwnd, scrollRange);
421 return scrollRange;
424 static void
425 PAGER_GrayAndRestoreBtns(PAGER_INFO* infoPtr, INT scrollRange,
426 BOOL* needsResize, BOOL* needsRepaint)
428 if (infoPtr->nPos > 0)
430 *needsResize |= !infoPtr->TLbtnState; /* PGF_INVISIBLE */
431 if (infoPtr->TLbtnState != PGF_DEPRESSED)
432 infoPtr->TLbtnState = PGF_NORMAL;
434 else
436 *needsRepaint |= (infoPtr->TLbtnState != PGF_GRAYED);
437 infoPtr->TLbtnState = PGF_GRAYED;
440 if (scrollRange <= 0)
442 *needsRepaint |= (infoPtr->TLbtnState != PGF_GRAYED);
443 infoPtr->TLbtnState = PGF_GRAYED;
444 *needsRepaint |= (infoPtr->BRbtnState != PGF_GRAYED);
445 infoPtr->BRbtnState = PGF_GRAYED;
447 else if (infoPtr->nPos < scrollRange)
449 *needsResize |= !infoPtr->BRbtnState; /* PGF_INVISIBLE */
450 if (infoPtr->BRbtnState != PGF_DEPRESSED)
451 infoPtr->BRbtnState = PGF_NORMAL;
453 else
455 *needsRepaint |= (infoPtr->BRbtnState != PGF_GRAYED);
456 infoPtr->BRbtnState = PGF_GRAYED;
461 static void
462 PAGER_NormalizeBtns(PAGER_INFO* infoPtr, BOOL* needsRepaint)
464 if (infoPtr->TLbtnState & (PGF_HOT | PGF_DEPRESSED))
466 infoPtr->TLbtnState = PGF_NORMAL;
467 *needsRepaint = TRUE;
470 if (infoPtr->BRbtnState & (PGF_HOT | PGF_DEPRESSED))
472 infoPtr->BRbtnState = PGF_NORMAL;
473 *needsRepaint = TRUE;
477 static void
478 PAGER_HideGrayBtns(PAGER_INFO* infoPtr, BOOL* needsResize)
480 if (infoPtr->TLbtnState == PGF_GRAYED)
482 infoPtr->TLbtnState = PGF_INVISIBLE;
483 *needsResize = TRUE;
486 if (infoPtr->BRbtnState == PGF_GRAYED)
488 infoPtr->BRbtnState = PGF_INVISIBLE;
489 *needsResize = TRUE;
493 static void
494 PAGER_UpdateBtns(HWND hwnd, PAGER_INFO *infoPtr,
495 INT scrollRange, BOOL hideGrayBtns)
497 BOOL resizeClient = FALSE;
498 BOOL repaintBtns = FALSE;
500 if (scrollRange < 0)
501 PAGER_NormalizeBtns(infoPtr, &repaintBtns);
502 else
503 PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
505 if (hideGrayBtns)
506 PAGER_HideGrayBtns(infoPtr, &resizeClient);
508 if (resizeClient) /* initiate NCCalcSize to resize client wnd */ {
509 SetWindowPos(hwnd, 0,0,0,0,0,
510 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
511 SWP_NOZORDER | SWP_NOACTIVATE);
514 if (repaintBtns)
515 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
518 static LRESULT
519 PAGER_SetPos(HWND hwnd, INT newPos, BOOL fromBtnPress)
521 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
522 INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr);
523 INT oldPos = infoPtr->nPos;
525 if ((scrollRange <= 0) || (newPos < 0))
526 infoPtr->nPos = 0;
527 else if (newPos > scrollRange)
528 infoPtr->nPos = scrollRange;
529 else
530 infoPtr->nPos = newPos;
532 TRACE("[%p] pos=%d, oldpos=%d\n", hwnd, infoPtr->nPos, oldPos);
534 if (infoPtr->nPos != oldPos)
536 /* gray and restore btns, and if from WM_SETPOS, hide the gray btns */
537 PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, !fromBtnPress);
538 PAGER_PositionChildWnd(hwnd, infoPtr);
541 return 0;
544 static LRESULT
545 PAGER_HandleWindowPosChanging(HWND hwnd, WPARAM wParam, WINDOWPOS *winpos)
547 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
549 if (infoPtr->bNoResize && !(winpos->flags & SWP_NOSIZE))
551 /* don't let the app resize the nonscrollable dimension of a control
552 * that was created with CCS_NORESIZE style
553 * (i.e. height for a horizontal pager, or width for a vertical one) */
555 /* except if the current dimension is 0 and app is setting for
556 * first time, then save amount as dimension. - GA 8/01 */
558 if (PAGER_IsHorizontal(hwnd))
559 if (!infoPtr->nHeight && winpos->cy)
560 infoPtr->nHeight = winpos->cy;
561 else
562 winpos->cy = infoPtr->nHeight;
563 else
564 if (!infoPtr->nWidth && winpos->cx)
565 infoPtr->nWidth = winpos->cx;
566 else
567 winpos->cx = infoPtr->nWidth;
568 return 0;
571 DefWindowProcA (hwnd, WM_WINDOWPOSCHANGING, wParam, (LPARAM)winpos);
573 return 1;
576 static INT
577 PAGER_SetFixedWidth(HWND hwnd, PAGER_INFO* infoPtr)
579 /* Must set the non-scrollable dimension to be less than the full height/width
580 * so that NCCalcSize is called. The Msoft docs mention 3/4 factor for button
581 * size, and experimentation shows that affect is almost right. */
583 RECT wndRect;
584 INT delta, h;
585 GetWindowRect(hwnd, &wndRect);
587 /* see what the app says for btn width */
588 PAGER_CalcSize(hwnd, &infoPtr->nWidth, TRUE);
590 if (infoPtr->bNoResize)
592 delta = wndRect.right - wndRect.left - infoPtr->nWidth;
593 if (delta > infoPtr->nButtonSize)
594 infoPtr->nWidth += 4 * infoPtr->nButtonSize / 3;
595 else if (delta > 0)
596 infoPtr->nWidth += infoPtr->nButtonSize / 3;
599 h = wndRect.bottom - wndRect.top + infoPtr->nButtonSize;
601 TRACE("[%p] infoPtr->nWidth set to %d\n",
602 hwnd, infoPtr->nWidth);
604 return h;
607 static INT
608 PAGER_SetFixedHeight(HWND hwnd, PAGER_INFO* infoPtr)
610 /* Must set the non-scrollable dimension to be less than the full height/width
611 * so that NCCalcSize is called. The Msoft docs mention 3/4 factor for button
612 * size, and experimentation shows that affect is almost right. */
614 RECT wndRect;
615 INT delta, w;
616 GetWindowRect(hwnd, &wndRect);
618 /* see what the app says for btn height */
619 PAGER_CalcSize(hwnd, &infoPtr->nHeight, FALSE);
621 if (infoPtr->bNoResize)
623 delta = wndRect.bottom - wndRect.top - infoPtr->nHeight;
624 if (delta > infoPtr->nButtonSize)
625 infoPtr->nHeight += infoPtr->nButtonSize;
626 else if (delta > 0)
627 infoPtr->nHeight += infoPtr->nButtonSize / 3;
630 w = wndRect.right - wndRect.left + infoPtr->nButtonSize;
632 TRACE("[%p] infoPtr->nHeight set to %d\n",
633 hwnd, infoPtr->nHeight);
635 return w;
638 /******************************************************************
639 * For the PGM_RECALCSIZE message (but not the other uses in *
640 * this module), the native control does only the following: *
642 * if (some condition) *
643 * PostMessageA(hwnd, EM_FMTLINES, 0, 0); *
644 * return DefWindowProcA(hwnd, PGM_RECALCSIZE, 0, 0); *
646 * When we figure out what the "some condition" is we will *
647 * implement that for the message processing. *
648 ******************************************************************/
650 static LRESULT
651 PAGER_RecalcSize(HWND hwnd)
653 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
655 TRACE("[%p]\n", hwnd);
657 if (infoPtr->hwndChild)
659 INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr);
661 if (scrollRange <= 0)
663 infoPtr->nPos = -1;
664 PAGER_SetPos(hwnd, 0, FALSE);
666 else
668 PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, TRUE);
669 PAGER_PositionChildWnd(hwnd, infoPtr);
673 return 1;
677 static LRESULT
678 PAGER_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
680 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
681 COLORREF clrTemp = infoPtr->clrBk;
683 infoPtr->clrBk = (COLORREF)lParam;
684 TRACE("[%p] %06lx\n", hwnd, infoPtr->clrBk);
686 /* the native control seems to do things this way */
687 SetWindowPos(hwnd, 0,0,0,0,0,
688 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
689 SWP_NOZORDER | SWP_NOACTIVATE);
691 RedrawWindow(hwnd, 0, 0, RDW_ERASE | RDW_INVALIDATE);
693 return (LRESULT)clrTemp;
697 static LRESULT
698 PAGER_SetBorder (HWND hwnd, WPARAM wParam, LPARAM lParam)
700 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
701 INT nTemp = infoPtr->nBorder;
703 infoPtr->nBorder = (INT)lParam;
704 TRACE("[%p] %d\n", hwnd, infoPtr->nBorder);
706 PAGER_RecalcSize(hwnd);
708 return (LRESULT)nTemp;
712 static LRESULT
713 PAGER_SetButtonSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
715 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
716 INT nTemp = infoPtr->nButtonSize;
718 infoPtr->nButtonSize = (INT)lParam;
719 TRACE("[%p] %d\n", hwnd, infoPtr->nButtonSize);
721 PAGER_RecalcSize(hwnd);
723 return (LRESULT)nTemp;
727 static LRESULT
728 PAGER_SetChild (HWND hwnd, WPARAM wParam, LPARAM lParam)
730 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
731 INT hw;
733 infoPtr->hwndChild = IsWindow ((HWND)lParam) ? (HWND)lParam : 0;
735 if (infoPtr->hwndChild)
737 TRACE("[%p] hwndChild=%p\n", hwnd, infoPtr->hwndChild);
739 if (PAGER_IsHorizontal(hwnd)) {
740 hw = PAGER_SetFixedHeight(hwnd, infoPtr);
741 /* adjust non-scrollable dimension to fit the child */
742 SetWindowPos(hwnd, 0, 0,0, hw, infoPtr->nHeight,
743 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
744 SWP_NOSIZE | SWP_NOACTIVATE);
746 else {
747 hw = PAGER_SetFixedWidth(hwnd, infoPtr);
748 /* adjust non-scrollable dimension to fit the child */
749 SetWindowPos(hwnd, 0, 0,0, infoPtr->nWidth, hw,
750 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
751 SWP_NOSIZE | SWP_NOACTIVATE);
754 /* position child within the page scroller */
755 SetWindowPos(infoPtr->hwndChild, HWND_TOP,
756 0,0,0,0,
757 SWP_SHOWWINDOW | SWP_NOSIZE); /* native is 0 */
759 infoPtr->nPos = -1;
760 PAGER_SetPos(hwnd, 0, FALSE);
763 return 0;
766 static void
767 PAGER_Scroll(HWND hwnd, INT dir)
769 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
770 NMPGSCROLL nmpgScroll;
771 RECT rcWnd;
773 if (infoPtr->hwndChild)
775 ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL));
776 nmpgScroll.hdr.hwndFrom = hwnd;
777 nmpgScroll.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
778 nmpgScroll.hdr.code = PGN_SCROLL;
780 GetWindowRect(hwnd, &rcWnd);
781 GetClientRect(hwnd, &nmpgScroll.rcParent);
782 nmpgScroll.iXpos = nmpgScroll.iYpos = 0;
783 nmpgScroll.iDir = dir;
785 if (PAGER_IsHorizontal(hwnd))
787 nmpgScroll.iScroll = rcWnd.right - rcWnd.left;
788 nmpgScroll.iXpos = infoPtr->nPos;
790 else
792 nmpgScroll.iScroll = rcWnd.bottom - rcWnd.top;
793 nmpgScroll.iYpos = infoPtr->nPos;
795 nmpgScroll.iScroll -= 2*infoPtr->nButtonSize;
797 SendMessageA (GetParent(hwnd), WM_NOTIFY,
798 (WPARAM)nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll);
800 TRACE("[%p] PGN_SCROLL returns iScroll=%d\n", hwnd, nmpgScroll.iScroll);
802 if (nmpgScroll.iScroll > 0)
804 infoPtr->direction = dir;
806 if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP)
807 PAGER_SetPos(hwnd, infoPtr->nPos - nmpgScroll.iScroll, TRUE);
808 else
809 PAGER_SetPos(hwnd, infoPtr->nPos + nmpgScroll.iScroll, TRUE);
811 else
812 infoPtr->direction = -1;
816 static LRESULT
817 PAGER_FmtLines(HWND hwnd)
819 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
821 /* initiate NCCalcSize to resize client wnd and get size */
822 SetWindowPos(hwnd, 0, 0,0,0,0,
823 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
824 SWP_NOZORDER | SWP_NOACTIVATE);
826 SetWindowPos(infoPtr->hwndChild, 0,
827 0,0,infoPtr->nWidth,infoPtr->nHeight,
830 return DefWindowProcA (hwnd, EM_FMTLINES, 0, 0);
833 static LRESULT
834 PAGER_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
836 PAGER_INFO *infoPtr;
837 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
839 /* allocate memory for info structure */
840 infoPtr = (PAGER_INFO *)COMCTL32_Alloc (sizeof(PAGER_INFO));
841 SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
843 /* set default settings */
844 infoPtr->hwndChild = NULL;
845 infoPtr->bNoResize = dwStyle & CCS_NORESIZE;
846 infoPtr->clrBk = GetSysColor(COLOR_BTNFACE);
847 infoPtr->nBorder = 0;
848 infoPtr->nButtonSize = 12;
849 infoPtr->nPos = 0;
850 infoPtr->nWidth = 0;
851 infoPtr->nHeight = 0;
852 infoPtr->bForward = FALSE;
853 infoPtr->bCapture = FALSE;
854 infoPtr->TLbtnState = PGF_INVISIBLE;
855 infoPtr->BRbtnState = PGF_INVISIBLE;
856 infoPtr->direction = -1;
858 if (dwStyle & PGS_DRAGNDROP)
859 FIXME("[%p] Drag and Drop style is not implemented yet.\n", hwnd);
861 * If neither horizontal nor vertical style specified, default to vertical.
862 * This is probably not necessary, since the style may be set later on as
863 * the control is initialized, but just in case it isn't, set it here.
865 if (!(dwStyle & PGS_HORZ) && !(dwStyle & PGS_VERT))
867 dwStyle |= PGS_VERT;
868 SetWindowLongA(hwnd, GWL_STYLE, dwStyle);
871 return 0;
875 static LRESULT
876 PAGER_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
878 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
879 /* free pager info data */
880 COMCTL32_Free (infoPtr);
881 SetWindowLongA (hwnd, 0, 0);
882 return 0;
885 static LRESULT
886 PAGER_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
888 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
889 LPRECT lpRect = (LPRECT)lParam;
890 RECT rcChildw, rcmyw, wnrc, ltrc, rbrc;
891 POINT cursor;
892 BOOL resizeClient = FALSE;
893 BOOL repaintBtns = FALSE;
894 INT scrollRange;
897 * lParam points to a RECT struct. On entry, the struct
898 * contains the proposed wnd rectangle for the window.
899 * On exit, the struct should contain the screen
900 * coordinates of the corresponding window's client area.
903 DefWindowProcA (hwnd, WM_NCCALCSIZE, wParam, lParam);
905 TRACE("orig rect=(%ld,%ld)-(%ld,%ld)\n",
906 lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
908 if (PAGER_IsHorizontal(hwnd))
910 infoPtr->nWidth = lpRect->right - lpRect->left;
911 PAGER_CalcSize (hwnd, &infoPtr->nWidth, TRUE);
912 GetWindowRect (infoPtr->hwndChild, &rcChildw);
913 MapWindowPoints (0, hwnd, (LPPOINT)&rcChildw, 2);
914 GetCursorPos (&cursor);
915 GetWindowRect (hwnd, &rcmyw);
917 /* Reset buttons and hide any grey ones */
918 scrollRange = infoPtr->nWidth - (rcmyw.right - rcmyw.left);
920 TRACE("nPos=%d, scrollrange=%d, nHeigth=%d, myw=(%ld,%ld)-(%ld,%ld), cursor=(%ld,%ld)\n",
921 infoPtr->nPos, scrollRange, infoPtr->nHeight,
922 rcmyw.left, rcmyw.top,
923 rcmyw.right, rcmyw.bottom,
924 cursor.x, cursor.y);
925 PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
926 PAGER_HideGrayBtns(infoPtr, &resizeClient);
928 if (PtInRect (&rcmyw, cursor)) {
929 GetWindowRect (hwnd, &wnrc);
930 ltrc = wnrc;
931 ltrc.right = ltrc.left + infoPtr->nButtonSize;
932 rbrc = wnrc;
933 rbrc.left = rbrc.right - infoPtr->nButtonSize;
934 TRACE("horz lt rect=(%ld,%ld)-(%ld,%ld), rb rect=(%ld,%ld)-(%ld,%ld)\n",
935 ltrc.left, ltrc.top, ltrc.right, ltrc.bottom,
936 rbrc.left, rbrc.top, rbrc.right, rbrc.bottom);
937 if (PtInRect (&ltrc, cursor) && infoPtr->TLbtnState)
938 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
939 if (PtInRect (&rbrc, cursor) && infoPtr->BRbtnState)
940 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
942 if (infoPtr->TLbtnState && (lpRect->left + infoPtr->nButtonSize < lpRect->right))
943 lpRect->left += infoPtr->nButtonSize;
944 if (infoPtr->BRbtnState && (lpRect->right - infoPtr->nButtonSize > lpRect->left))
945 lpRect->right -= infoPtr->nButtonSize;
947 else
949 /* native does: (from trace of IE4 opening "Favorites" frame)
950 * DefWindowProc
951 * WM_NOITFY PGN_CALCSIZE w/ dwFlag=2
952 * GetWindowRect (child, &rc)
953 * MapWindowPoints (0, syspager, &rc, 2)
954 * GetCursorPos( &cur )
955 * GetWindowRect (syspager, &rc2)
956 * PtInRect (&rc2, cur.x, cur.y) rtns 0
957 * returns with rect empty
959 infoPtr->nHeight = lpRect->bottom - lpRect->top;
960 PAGER_CalcSize (hwnd, &infoPtr->nHeight, FALSE);
961 GetWindowRect (infoPtr->hwndChild, &rcChildw);
962 MapWindowPoints (0, hwnd, (LPPOINT)&rcChildw, 2);
963 GetCursorPos (&cursor);
964 GetWindowRect (hwnd, &rcmyw);
966 /* Reset buttons and hide any grey ones */
967 scrollRange = infoPtr->nHeight - (rcmyw.bottom - rcmyw.top);
969 TRACE("nPos=%d, scrollrange=%d, nHeigth=%d, myw=(%ld,%ld)-(%ld,%ld), cursor=(%ld,%ld)\n",
970 infoPtr->nPos, scrollRange, infoPtr->nHeight,
971 rcmyw.left, rcmyw.top,
972 rcmyw.right, rcmyw.bottom,
973 cursor.x, cursor.y);
974 PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
975 PAGER_HideGrayBtns(infoPtr, &resizeClient);
977 if (PtInRect (&rcmyw, cursor)) {
979 /* native does:
980 * GetWindowRect(pager, &rc)
981 * PtInRect(btn-left????, cur.x, cur.y)
982 * if true -> ???
983 * PtInRect(btn-right????, cur.x, cur.y)
984 * if true
985 * RedrawWindow(pager, 0, 0, 5)
986 * return TRUE
989 GetWindowRect (hwnd, &wnrc);
990 ltrc = wnrc;
991 ltrc.right = ltrc.left + infoPtr->nButtonSize;
992 rbrc = wnrc;
993 rbrc.left = rbrc.right - infoPtr->nButtonSize;
994 TRACE("vert lt rect=(%ld,%ld)-(%ld,%ld), rb rect=(%ld,%ld)-(%ld,%ld)\n",
995 ltrc.left, ltrc.top, ltrc.right, ltrc.bottom,
996 rbrc.left, rbrc.top, rbrc.right, rbrc.bottom);
997 if (PtInRect (&ltrc, cursor) && infoPtr->TLbtnState)
998 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
999 if (PtInRect (&rbrc, cursor) && infoPtr->BRbtnState)
1000 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
1002 if (infoPtr->TLbtnState && (lpRect->top + infoPtr->nButtonSize < lpRect->bottom))
1003 lpRect->top += infoPtr->nButtonSize;
1004 if (infoPtr->BRbtnState && (lpRect->bottom - infoPtr->nButtonSize > lpRect->top))
1005 lpRect->bottom -= infoPtr->nButtonSize;
1008 TRACE("[%p] client rect set to %ldx%ld at (%ld,%ld) BtnState[%d,%d]\n",
1009 hwnd, lpRect->right-lpRect->left, lpRect->bottom-lpRect->top,
1010 lpRect->left, lpRect->top,
1011 infoPtr->TLbtnState, infoPtr->BRbtnState);
1013 return 0;
1016 static LRESULT
1017 PAGER_NCPaint (HWND hwnd, WPARAM wParam, LPARAM lParam)
1019 PAGER_INFO* infoPtr = PAGER_GetInfoPtr(hwnd);
1020 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
1021 RECT rcWindow, rcBottomRight, rcTopLeft;
1022 HDC hdc;
1023 BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
1025 if (dwStyle & WS_MINIMIZE)
1026 return 0;
1028 DefWindowProcA (hwnd, WM_NCPAINT, wParam, lParam);
1030 if (!(hdc = GetDCEx (hwnd, 0, DCX_USESTYLE | DCX_WINDOW)))
1031 return 0;
1033 GetWindowRect (hwnd, &rcWindow);
1034 OffsetRect (&rcWindow, -rcWindow.left, -rcWindow.top);
1036 rcTopLeft = rcBottomRight = rcWindow;
1037 if (bHorizontal)
1039 rcTopLeft.right = rcTopLeft.left + infoPtr->nButtonSize;
1040 rcBottomRight.left = rcBottomRight.right - infoPtr->nButtonSize;
1042 else
1044 rcTopLeft.bottom = rcTopLeft.top + infoPtr->nButtonSize;
1045 rcBottomRight.top = rcBottomRight.bottom - infoPtr->nButtonSize;
1048 PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft,
1049 bHorizontal, TRUE, infoPtr->TLbtnState);
1050 PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight,
1051 bHorizontal, FALSE, infoPtr->BRbtnState);
1053 ReleaseDC( hwnd, hdc );
1054 return 0;
1057 static INT
1058 PAGER_HitTest (HWND hwnd, LPPOINT pt)
1060 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1061 RECT clientRect;
1062 BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
1064 GetClientRect (hwnd, &clientRect);
1066 if (PtInRect(&clientRect, *pt))
1068 TRACE("HTCLIENT\n");
1069 return HTCLIENT;
1072 if (infoPtr->TLbtnState && infoPtr->TLbtnState != PGF_GRAYED)
1074 if (bHorizontal)
1076 if (pt->x < clientRect.left)
1078 TRACE("HTLEFT\n");
1079 return HTLEFT;
1082 else
1084 if (pt->y < clientRect.top)
1086 TRACE("HTTOP\n");
1087 return HTTOP;
1092 if (infoPtr->BRbtnState && infoPtr->BRbtnState != PGF_GRAYED)
1094 if (bHorizontal)
1096 if (pt->x > clientRect.right)
1098 TRACE("HTRIGHT\n");
1099 return HTRIGHT;
1102 else
1104 if (pt->y > clientRect.bottom)
1106 TRACE("HTBOTTOM\n");
1107 return HTBOTTOM;
1112 TRACE("HTNOWHERE\n");
1113 return HTNOWHERE;
1116 static LRESULT
1117 PAGER_NCHitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
1119 POINT pt;
1121 pt.x = SLOWORD(lParam);
1122 pt.y = SHIWORD(lParam);
1124 ScreenToClient (hwnd, &pt);
1125 return PAGER_HitTest(hwnd, &pt);
1128 static LRESULT
1129 PAGER_SetCursor( HWND hwnd, WPARAM wParam, LPARAM lParam )
1131 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1132 BOOL notCaptured = FALSE;
1134 switch(LOWORD(lParam))
1136 case HTLEFT:
1137 case HTTOP:
1138 if ((notCaptured = infoPtr->TLbtnState != PGF_HOT))
1139 infoPtr->TLbtnState = PGF_HOT;
1140 break;
1141 case HTRIGHT:
1142 case HTBOTTOM:
1143 if ((notCaptured = infoPtr->BRbtnState != PGF_HOT))
1144 infoPtr->BRbtnState = PGF_HOT;
1145 break;
1146 default:
1147 return FALSE;
1150 if (notCaptured)
1152 PAGER_CaptureandTrack(infoPtr, hwnd);
1154 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
1157 return TRUE;
1160 static LRESULT
1161 PAGER_MouseLeave (HWND hwnd, WPARAM wParam, LPARAM lParam)
1163 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1165 KillTimer (hwnd, TIMERID1);
1166 KillTimer (hwnd, TIMERID2);
1168 TRACE("[%p] ReleaseCapture\n", hwnd);
1169 ReleaseCapture();
1170 infoPtr->bCapture = FALSE;
1172 /* Notify parent of released mouse capture */
1174 NMHDR nmhdr;
1175 ZeroMemory (&nmhdr, sizeof (NMHDR));
1176 nmhdr.hwndFrom = hwnd;
1177 nmhdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
1178 nmhdr.code = NM_RELEASEDCAPTURE;
1179 SendMessageA (GetParent(hwnd), WM_NOTIFY,
1180 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1183 /* make HOT btns NORMAL and hide gray btns */
1184 PAGER_UpdateBtns(hwnd, infoPtr, -1, TRUE);
1186 return TRUE;
1189 static LRESULT
1190 PAGER_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam)
1192 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1193 POINT clpt, pt;
1194 RECT wnrect, TLbtnrect, BRbtnrect, *btnrect = NULL;
1195 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
1196 BOOL topLeft = FALSE;
1197 INT btnstate = 0;
1198 INT hit;
1199 HDC hdc;
1201 pt.x = SLOWORD(lParam);
1202 pt.y = SHIWORD(lParam);
1204 TRACE("[%p] to (%ld,%ld)\n", hwnd, pt.x, pt.y);
1205 ClientToScreen(hwnd, &pt);
1206 GetWindowRect(hwnd, &wnrect);
1207 if (PtInRect(&wnrect, pt)) {
1208 TLbtnrect = wnrect;
1209 BRbtnrect = wnrect;
1210 if (dwStyle & PGS_HORZ) {
1211 TLbtnrect.right = TLbtnrect.left + infoPtr->nButtonSize;
1212 BRbtnrect.left = BRbtnrect.right - infoPtr->nButtonSize;
1214 else {
1215 TLbtnrect.bottom = TLbtnrect.top + infoPtr->nButtonSize;
1216 BRbtnrect.top = BRbtnrect.bottom - infoPtr->nButtonSize;
1219 clpt = pt;
1220 MapWindowPoints(0, hwnd, &clpt, 1);
1221 hit = PAGER_HitTest(hwnd, &clpt);
1222 if (hit == HTLEFT || hit == HTTOP) {
1223 topLeft = TRUE;
1224 btnrect = &TLbtnrect;
1225 infoPtr->TLbtnState = PGF_DEPRESSED;
1226 btnstate = infoPtr->TLbtnState;
1228 else if (hit == HTRIGHT || hit == HTBOTTOM) {
1229 topLeft = FALSE;
1230 btnrect = &BRbtnrect;
1231 infoPtr->BRbtnState = PGF_DEPRESSED;
1232 btnstate = infoPtr->BRbtnState;
1235 /* If in one of the buttons the capture and draw buttons */
1236 if (btnrect) {
1237 TRACE("[%p] draw btn (%ld,%ld)-(%ld,%ld), Capture %s, style %08lx\n",
1238 hwnd, btnrect->left, btnrect->top,
1239 btnrect->right, btnrect->bottom,
1240 (infoPtr->bCapture) ? "TRUE" : "FALSE",
1241 dwStyle);
1242 if (!infoPtr->bCapture)
1243 PAGER_CaptureandTrack(infoPtr, hwnd);
1244 if (dwStyle & PGS_AUTOSCROLL)
1245 SetTimer(hwnd, TIMERID1, 0x3e, 0);
1246 MapWindowPoints(0, hwnd, (LPPOINT)btnrect, 2);
1247 hdc = GetWindowDC(hwnd);
1248 /* OffsetRect(wnrect, 0 | 1, 0 | 1) */
1249 PAGER_DrawButton(hdc, infoPtr->clrBk, *btnrect,
1250 PAGER_IsHorizontal(hwnd), topLeft, btnstate);
1251 ReleaseDC(hwnd, hdc);
1252 return DefWindowProcA (hwnd, WM_MOUSEMOVE, wParam, lParam);
1256 /* If we think we are captured, then do release */
1257 if (infoPtr->bCapture) {
1258 infoPtr->bCapture = FALSE;
1260 if (GetCapture() == hwnd) {
1261 ReleaseCapture();
1262 /* Notify parent of released mouse capture */
1264 NMHDR nmhdr;
1265 ZeroMemory (&nmhdr, sizeof (NMHDR));
1266 nmhdr.hwndFrom = hwnd;
1267 nmhdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
1268 nmhdr.code = NM_RELEASEDCAPTURE;
1269 SendMessageA (GetParent(hwnd), WM_NOTIFY,
1270 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1273 if (IsWindow(hwnd))
1274 KillTimer(hwnd, TIMERID1);
1276 return DefWindowProcA (hwnd, WM_MOUSEMOVE, wParam, lParam);
1279 static LRESULT
1280 PAGER_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
1282 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1283 BOOL repaintBtns = FALSE;
1284 POINT pt;
1285 INT hit;
1287 pt.x = SLOWORD(lParam);
1288 pt.y = SHIWORD(lParam);
1290 TRACE("[%p] at (%d,%d)\n", hwnd, SLOWORD(lParam), SHIWORD(lParam));
1292 hit = PAGER_HitTest(hwnd, &pt);
1294 /* put btn in DEPRESSED state */
1295 if (hit == HTLEFT || hit == HTTOP)
1297 repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED;
1298 infoPtr->TLbtnState = PGF_DEPRESSED;
1299 SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0);
1301 else if (hit == HTRIGHT || hit == HTBOTTOM)
1303 repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED;
1304 infoPtr->BRbtnState = PGF_DEPRESSED;
1305 SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0);
1308 if (repaintBtns)
1309 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
1311 switch(hit)
1313 case HTLEFT:
1314 TRACE("[%p] PGF_SCROLLLEFT\n", hwnd);
1315 PAGER_Scroll(hwnd, PGF_SCROLLLEFT);
1316 break;
1317 case HTTOP:
1318 TRACE("[%p] PGF_SCROLLUP\n", hwnd);
1319 PAGER_Scroll(hwnd, PGF_SCROLLUP);
1320 break;
1321 case HTRIGHT:
1322 TRACE("[%p] PGF_SCROLLRIGHT\n", hwnd);
1323 PAGER_Scroll(hwnd, PGF_SCROLLRIGHT);
1324 break;
1325 case HTBOTTOM:
1326 TRACE("[%p] PGF_SCROLLDOWN\n", hwnd);
1327 PAGER_Scroll(hwnd, PGF_SCROLLDOWN);
1328 break;
1329 default:
1330 break;
1333 return TRUE;
1336 static LRESULT
1337 PAGER_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
1339 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1340 TRACE("[%p]\n", hwnd);
1342 KillTimer (hwnd, TIMERID1);
1343 KillTimer (hwnd, TIMERID2);
1345 /* make PRESSED btns NORMAL but don't hide gray btns */
1346 PAGER_UpdateBtns(hwnd, infoPtr, -1, FALSE);
1348 return 0;
1351 static LRESULT
1352 PAGER_NCLButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
1354 POINT pt;
1356 pt.x = SLOWORD(lParam);
1357 pt.y = SHIWORD(lParam);
1359 TRACE("[%p] at (%d,%d)\n", hwnd, SLOWORD(lParam), SHIWORD(lParam));
1360 MapWindowPoints(0, hwnd, &pt, 1);
1361 lParam = MAKELONG(pt.x, pt.y);
1362 return PAGER_LButtonDown (hwnd, wParam, lParam);
1365 static LRESULT
1366 PAGER_Timer (HWND hwnd, WPARAM wParam)
1368 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1369 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
1370 INT dir;
1372 /* if initial timer, kill it and start the repeat timer */
1373 if (wParam == TIMERID1) {
1374 if (PAGER_IsHorizontal(hwnd)) {
1375 dir = (infoPtr->TLbtnState & PGF_DEPRESSED) ?
1376 PGF_SCROLLLEFT : PGF_SCROLLRIGHT;
1378 else {
1379 dir = (infoPtr->TLbtnState & PGF_DEPRESSED) ?
1380 PGF_SCROLLUP : PGF_SCROLLDOWN;
1382 TRACE("[%p] TIMERID1: style=%08lx, dir=%d\n", hwnd, dwStyle, dir);
1383 KillTimer(hwnd, TIMERID1);
1384 SetTimer(hwnd, TIMERID1, REPEAT_DELAY, 0);
1385 if (dwStyle & PGS_AUTOSCROLL) {
1386 PAGER_Scroll(hwnd, dir);
1387 SetWindowPos(hwnd, 0,0,0,0,0,
1388 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
1389 SWP_NOZORDER | SWP_NOACTIVATE);
1391 return 0;
1395 TRACE("[%p] TIMERID2: dir=%d\n", hwnd, infoPtr->direction);
1396 KillTimer(hwnd, TIMERID2);
1397 if (infoPtr->direction > 0) {
1398 PAGER_Scroll(hwnd, infoPtr->direction);
1399 SetTimer(hwnd, TIMERID2, REPEAT_DELAY, 0);
1401 return 0;
1404 static LRESULT
1405 PAGER_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
1407 POINT pt, ptorig;
1408 HDC hdc = (HDC)wParam;
1409 HWND parent;
1411 /* native does:
1412 * parent = GetParent(pager)
1413 * pt.x=0; pt.y=0; ?????
1414 * MapWindowPoints(pager, parent, &pt, 1)
1415 * OffsetWindowOrgEx(hdc, pt.x, pt.y, &ptorg)
1416 * SendMessageA(parent, WM_ERASEBKGND, hdc, 0)
1417 * SetWindowOrgEx(hdc, 0, 0, 0)
1420 pt.x = 0;
1421 pt.y = 0;
1422 parent = GetParent(hwnd);
1423 MapWindowPoints(hwnd, parent, &pt, 1);
1424 OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig);
1425 SendMessageA (parent, WM_ERASEBKGND, wParam, lParam);
1426 SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0);
1429 #if 0
1430 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1431 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
1432 RECT rect;
1434 GetClientRect (hwnd, &rect);
1435 FillRect ((HDC)wParam, &rect, hBrush);
1437 /* background color of the child should be the same as the pager */
1438 if (infoPtr->hwndChild)
1440 GetClientRect (infoPtr->hwndChild, &rect);
1441 FillRect ((HDC)wParam, &rect, hBrush);
1444 DeleteObject (hBrush);
1445 #endif
1447 return TRUE;
1451 static LRESULT
1452 PAGER_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1454 /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */
1456 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1457 TRACE("[%p] %dx%d\n", hwnd, SLOWORD(lParam), SHIWORD(lParam));
1459 if (PAGER_IsHorizontal(hwnd))
1460 infoPtr->nHeight = SHIWORD(lParam);
1461 else
1462 infoPtr->nWidth = SLOWORD(lParam);
1464 return PAGER_RecalcSize(hwnd);
1468 static LRESULT WINAPI
1469 PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1471 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1473 if (!infoPtr && (uMsg != WM_CREATE))
1474 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1476 switch (uMsg)
1478 case EM_FMTLINES:
1479 return PAGER_FmtLines(hwnd);
1481 case PGM_FORWARDMOUSE:
1482 return PAGER_ForwardMouse (hwnd, wParam);
1484 case PGM_GETBKCOLOR:
1485 return PAGER_GetBkColor(hwnd);
1487 case PGM_GETBORDER:
1488 return PAGER_GetBorder(hwnd);
1490 case PGM_GETBUTTONSIZE:
1491 return PAGER_GetButtonSize(hwnd);
1493 case PGM_GETPOS:
1494 return PAGER_GetPos(hwnd);
1496 case PGM_GETBUTTONSTATE:
1497 return PAGER_GetButtonState (hwnd, wParam, lParam);
1499 /* case PGM_GETDROPTARGET: */
1501 case PGM_RECALCSIZE:
1502 return PAGER_RecalcSize(hwnd);
1504 case PGM_SETBKCOLOR:
1505 return PAGER_SetBkColor (hwnd, wParam, lParam);
1507 case PGM_SETBORDER:
1508 return PAGER_SetBorder (hwnd, wParam, lParam);
1510 case PGM_SETBUTTONSIZE:
1511 return PAGER_SetButtonSize (hwnd, wParam, lParam);
1513 case PGM_SETCHILD:
1514 return PAGER_SetChild (hwnd, wParam, lParam);
1516 case PGM_SETPOS:
1517 return PAGER_SetPos(hwnd, (INT)lParam, FALSE);
1519 case WM_CREATE:
1520 return PAGER_Create (hwnd, wParam, lParam);
1522 case WM_DESTROY:
1523 return PAGER_Destroy (hwnd, wParam, lParam);
1525 case WM_SIZE:
1526 return PAGER_Size (hwnd, wParam, lParam);
1528 case WM_NCPAINT:
1529 return PAGER_NCPaint (hwnd, wParam, lParam);
1531 case WM_WINDOWPOSCHANGING:
1532 return PAGER_HandleWindowPosChanging (hwnd, wParam, (WINDOWPOS*)lParam);
1534 case WM_NCCALCSIZE:
1535 return PAGER_NCCalcSize (hwnd, wParam, lParam);
1537 case WM_NCHITTEST:
1538 return PAGER_NCHitTest (hwnd, wParam, lParam);
1540 case WM_SETCURSOR:
1542 if (hwnd == (HWND)wParam)
1543 return PAGER_SetCursor(hwnd, wParam, lParam);
1544 else /* its for the child */
1545 return 0;
1548 case WM_MOUSEMOVE:
1549 if (infoPtr->bForward && infoPtr->hwndChild)
1550 PostMessageA(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam);
1551 return PAGER_MouseMove (hwnd, wParam, lParam);
1553 case WM_MOUSELEAVE:
1554 return PAGER_MouseLeave (hwnd, wParam, lParam);
1556 case WM_NCLBUTTONDOWN:
1557 return PAGER_NCLButtonDown (hwnd, wParam, lParam);
1559 case WM_LBUTTONDOWN:
1560 return PAGER_LButtonDown (hwnd, wParam, lParam);
1562 case WM_NCLBUTTONUP:
1563 case WM_LBUTTONUP:
1564 return PAGER_LButtonUp (hwnd, wParam, lParam);
1566 case WM_ERASEBKGND:
1567 return PAGER_EraseBackground (hwnd, wParam, lParam);
1569 case WM_PAINT:
1570 return PAGER_Paint (hwnd, wParam);
1572 case WM_TIMER:
1573 return PAGER_Timer (hwnd, wParam);
1575 case WM_NOTIFY:
1576 case WM_COMMAND:
1577 return SendMessageA (GetParent (hwnd), uMsg, wParam, lParam);
1579 default:
1580 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1583 return 0;
1587 VOID
1588 PAGER_Register (void)
1590 WNDCLASSA wndClass;
1592 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1593 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS;
1594 wndClass.lpfnWndProc = (WNDPROC)PAGER_WindowProc;
1595 wndClass.cbClsExtra = 0;
1596 wndClass.cbWndExtra = sizeof(PAGER_INFO *);
1597 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
1598 wndClass.hbrBackground = 0;
1599 wndClass.lpszClassName = WC_PAGESCROLLERA;
1601 RegisterClassA (&wndClass);
1605 VOID
1606 PAGER_Unregister (void)
1608 UnregisterClassA (WC_PAGESCROLLERA, NULL);