- Background of the child wnd should be erased with the same color as
[wine/dcerpc.git] / dlls / comctl32 / pager.c
blob706079b4b4cedfade9a895e6ee11b66e5065970a
1 /*
2 * Pager control
4 * Copyright 1998, 1999 Eric Kohl
6 * NOTES
7 * Tested primarily with the controlspy Pager application.
8 * Susan Farley (susan@codeweavers.com)
10 * TODO:
11 * Implement repetitive button press.
12 * Adjust arrow size relative to size of button.
13 * Allow border size changes.
14 * Implement drag and drop style.
17 #include <string.h>
18 #include "winbase.h"
19 #include "commctrl.h"
20 #include "debugtools.h"
22 DEFAULT_DEBUG_CHANNEL(pager);
24 typedef struct
26 HWND hwndChild; /* handle of the contained wnd */
27 BOOL bNoResize; /* set when created with CCS_NORESIZE */
28 COLORREF clrBk; /* background color */
29 INT nBorder; /* border size for the control */
30 INT nButtonSize;/* size of the pager btns */
31 INT nPos; /* scroll position */
32 INT nWidth; /* from child wnd's response to PGN_CALCSIZE */
33 INT nHeight; /* from child wnd's response to PGN_CALCSIZE */
34 BOOL bForward; /* forward WM_MOUSEMOVE msgs to the contained wnd */
35 INT TLbtnState; /* state of top or left btn */
36 INT BRbtnState; /* state of bottom or right btn */
38 } PAGER_INFO;
40 #define PAGER_GetInfoPtr(hwnd) ((PAGER_INFO *)GetWindowLongA(hwnd, 0))
41 #define PAGER_IsHorizontal(hwnd) ((GetWindowLongA (hwnd, GWL_STYLE) & PGS_HORZ))
43 #define MIN_ARROW_WIDTH 8
44 #define MIN_ARROW_HEIGHT 5
47 /* the horizontal arrows are:
49 * 01234 01234
50 * 1 * *
51 * 2 ** **
52 * 3*** ***
53 * 4*** ***
54 * 5 ** **
55 * 6 * *
56 * 7
59 static void
60 PAGER_DrawHorzArrow (HDC hdc, RECT r, INT colorRef, BOOL left)
62 INT x, y, w, h;
63 HPEN hOldPen;
65 w = r.right - r.left + 1;
66 h = r.bottom - r.top + 1;
67 if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
68 return; /* refuse to draw partial arrow */
70 hOldPen = SelectObject ( hdc, GetSysColorPen (colorRef));
71 if (left)
73 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 3;
74 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
75 MoveToEx (hdc, x, y, NULL);
76 LineTo (hdc, x--, y+5); y++;
77 MoveToEx (hdc, x, y, NULL);
78 LineTo (hdc, x--, y+3); y++;
79 MoveToEx (hdc, x, y, NULL);
80 LineTo (hdc, x, y+1);
82 else
84 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
85 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
86 MoveToEx (hdc, x, y, NULL);
87 LineTo (hdc, x++, y+5); y++;
88 MoveToEx (hdc, x, y, NULL);
89 LineTo (hdc, x++, y+3); y++;
90 MoveToEx (hdc, x, y, NULL);
91 LineTo (hdc, x, y+1);
94 SelectObject( hdc, hOldPen );
97 /* the vertical arrows are:
99 * 01234567 01234567
100 * 1****** **
101 * 2 **** ****
102 * 3 ** ******
106 static void
107 PAGER_DrawVertArrow (HDC hdc, RECT r, INT colorRef, BOOL up)
109 INT x, y, w, h;
110 HPEN hOldPen;
112 w = r.right - r.left + 1;
113 h = r.bottom - r.top + 1;
114 if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
115 return; /* refuse to draw partial arrow */
117 hOldPen = SelectObject ( hdc, GetSysColorPen (colorRef));
118 if (up)
120 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
121 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 3;
122 MoveToEx (hdc, x, y, NULL);
123 LineTo (hdc, x+5, y--); x++;
124 MoveToEx (hdc, x, y, NULL);
125 LineTo (hdc, x+3, y--); x++;
126 MoveToEx (hdc, x, y, NULL);
127 LineTo (hdc, x+1, y);
129 else
131 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
132 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
133 MoveToEx (hdc, x, y, NULL);
134 LineTo (hdc, x+5, y++); x++;
135 MoveToEx (hdc, x, y, NULL);
136 LineTo (hdc, x+3, y++); x++;
137 MoveToEx (hdc, x, y, NULL);
138 LineTo (hdc, x+1, y);
141 SelectObject( hdc, hOldPen );
144 static void
145 PAGER_DrawButton(HDC hdc, COLORREF clrBk, RECT arrowRect,
146 BOOL horz, BOOL topLeft, INT btnState)
148 HBRUSH hBrush, hOldBrush;
149 RECT rc = arrowRect;
151 if (!btnState) /* PGF_INVISIBLE */
152 return;
154 if ((rc.right - rc.left <= 0) || (rc.bottom - rc.top <= 0))
155 return;
157 hBrush = CreateSolidBrush(clrBk);
158 hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
160 FillRect(hdc, &rc, hBrush);
162 if (btnState == PGF_HOT)
164 rc.left++, rc.top++; rc.right++, rc.bottom++;
165 DrawEdge( hdc, &rc, EDGE_RAISED, BF_RECT);
166 if (horz)
167 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
168 else
169 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
170 rc.left--, rc.top--; rc.right--, rc.bottom--;
172 else if (btnState == PGF_NORMAL)
174 DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
175 if (horz)
176 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
177 else
178 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
180 else if (btnState == PGF_DEPRESSED)
182 rc.left++, rc.top++;
183 DrawEdge( hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
184 if (horz)
185 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
186 else
187 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
188 rc.left--, rc.top--;
190 else if (btnState == PGF_GRAYED)
192 DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
193 if (horz)
195 PAGER_DrawHorzArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
196 rc.left++, rc.top++; rc.right++, rc.bottom++;
197 PAGER_DrawHorzArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
199 else
201 PAGER_DrawVertArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
202 rc.left++, rc.top++; rc.right++, rc.bottom++;
203 PAGER_DrawVertArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
205 rc.left--, rc.top--; rc.right--, rc.bottom--;
208 SelectObject( hdc, hOldBrush );
209 DeleteObject(hBrush);
212 /* << PAGER_GetDropTarget >> */
214 static inline LRESULT
215 PAGER_ForwardMouse (HWND hwnd, WPARAM wParam)
217 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
218 TRACE("[%04x]\n", hwnd);
220 infoPtr->bForward = (BOOL)wParam;
222 return 0;
225 static inline LRESULT
226 PAGER_GetButtonState (HWND hwnd, WPARAM wParam, LPARAM lParam)
228 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
229 LRESULT btnState = PGF_INVISIBLE;
230 INT btn = (INT)lParam;
231 TRACE("[%04x]\n", hwnd);
233 if (btn == PGB_TOPORLEFT)
234 btnState = infoPtr->TLbtnState;
235 else if (btn == PGB_BOTTOMORRIGHT)
236 btnState = infoPtr->BRbtnState;
238 return btnState;
242 static inline LRESULT
243 PAGER_GetPos(HWND hwnd)
245 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
246 TRACE("[%04x] returns %d\n", hwnd, infoPtr->nPos);
247 return (LRESULT)infoPtr->nPos;
250 static inline LRESULT
251 PAGER_GetButtonSize(HWND hwnd)
253 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
254 TRACE("[%04x] returns %d\n", hwnd, infoPtr->nButtonSize);
255 return (LRESULT)infoPtr->nButtonSize;
258 static inline LRESULT
259 PAGER_GetBorder(HWND hwnd)
261 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
262 TRACE("[%04x] returns %d\n", hwnd, infoPtr->nBorder);
263 return (LRESULT)infoPtr->nBorder;
266 static inline LRESULT
267 PAGER_GetBkColor(HWND hwnd)
269 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
270 TRACE("[%04x] returns %06lx\n", hwnd, infoPtr->clrBk);
271 return (LRESULT)infoPtr->clrBk;
274 static void
275 PAGER_CalcSize (HWND hwnd, INT* size, BOOL getWidth)
277 NMPGCALCSIZE nmpgcs;
278 ZeroMemory (&nmpgcs, sizeof (NMPGCALCSIZE));
279 nmpgcs.hdr.hwndFrom = hwnd;
280 nmpgcs.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
281 nmpgcs.hdr.code = PGN_CALCSIZE;
282 nmpgcs.dwFlag = getWidth ? PGF_CALCWIDTH : PGF_CALCHEIGHT;
283 nmpgcs.iWidth = getWidth ? *size : 0;
284 nmpgcs.iHeight = getWidth ? 0 : *size;
285 SendMessageA (hwnd, WM_NOTIFY,
286 (WPARAM)nmpgcs.hdr.idFrom, (LPARAM)&nmpgcs);
288 *size = getWidth ? nmpgcs.iWidth : nmpgcs.iHeight;
290 TRACE("[%04x] PGN_CALCSIZE returns %s=%d\n", hwnd,
291 getWidth ? "width" : "height", *size);
294 static void
295 PAGER_PositionChildWnd(HWND hwnd, PAGER_INFO* infoPtr)
297 if (infoPtr->hwndChild)
299 RECT rcClient;
300 int nPos = infoPtr->nPos;
302 /* compensate for a grayed btn, which will soon become invisible */
303 if (infoPtr->TLbtnState == PGF_GRAYED)
304 nPos += infoPtr->nButtonSize;
306 GetClientRect(hwnd, &rcClient);
308 if (PAGER_IsHorizontal(hwnd))
310 int wndSize = max(0, rcClient.right - rcClient.left);
311 if (infoPtr->nWidth < wndSize)
312 infoPtr->nWidth = wndSize;
314 TRACE("[%04x] SWP %dx%d at (%d,%d)\n", hwnd,
315 infoPtr->nWidth, infoPtr->nHeight,
316 -nPos, 0);
317 SetWindowPos(infoPtr->hwndChild, 0,
318 -nPos, 0,
319 infoPtr->nWidth, infoPtr->nHeight,
320 SWP_NOZORDER);
322 else
324 int wndSize = max(0, rcClient.bottom - rcClient.top);
325 if (infoPtr->nHeight < wndSize)
326 infoPtr->nHeight = wndSize;
328 TRACE("[%04x] SWP %dx%d at (%d,%d)\n", hwnd,
329 infoPtr->nWidth, infoPtr->nHeight,
330 0, -nPos);
331 SetWindowPos(infoPtr->hwndChild, 0,
332 0, -nPos,
333 infoPtr->nWidth, infoPtr->nHeight,
334 SWP_NOZORDER);
337 InvalidateRect(infoPtr->hwndChild, NULL, TRUE);
341 static INT
342 PAGER_GetScrollRange(HWND hwnd, PAGER_INFO* infoPtr)
344 INT scrollRange = 0;
346 if (infoPtr->hwndChild)
348 INT wndSize, childSize;
349 RECT wndRect;
350 GetWindowRect(hwnd, &wndRect);
352 if (PAGER_IsHorizontal(hwnd))
354 wndSize = wndRect.right - wndRect.left;
355 PAGER_CalcSize(hwnd, &infoPtr->nWidth, TRUE);
356 childSize = infoPtr->nWidth;
358 else
360 wndSize = wndRect.bottom - wndRect.top;
361 PAGER_CalcSize(hwnd, &infoPtr->nHeight, FALSE);
362 childSize = infoPtr->nHeight;
365 TRACE("childSize = %d, wndSize = %d\n", childSize, wndSize);
366 if (childSize > wndSize)
367 scrollRange = childSize - wndSize + infoPtr->nButtonSize;
370 TRACE("[%04x] returns %d\n", hwnd, scrollRange);
371 return scrollRange;
374 static void
375 PAGER_GrayAndRestoreBtns(PAGER_INFO* infoPtr, INT scrollRange,
376 BOOL* needsResize, BOOL* needsRepaint)
378 if (infoPtr->nPos > 0)
380 *needsResize |= !infoPtr->TLbtnState; /* PGF_INVISIBLE */
381 if (infoPtr->TLbtnState != PGF_DEPRESSED)
382 infoPtr->TLbtnState = PGF_NORMAL;
384 else
386 *needsRepaint |= (infoPtr->TLbtnState != PGF_GRAYED);
387 infoPtr->TLbtnState = PGF_GRAYED;
390 if (scrollRange <= 0)
392 *needsRepaint |= (infoPtr->TLbtnState != PGF_GRAYED);
393 infoPtr->TLbtnState = PGF_GRAYED;
394 *needsRepaint |= (infoPtr->BRbtnState != PGF_GRAYED);
395 infoPtr->BRbtnState = PGF_GRAYED;
397 else if (infoPtr->nPos < scrollRange)
399 *needsResize |= !infoPtr->BRbtnState; /* PGF_INVISIBLE */
400 if (infoPtr->BRbtnState != PGF_DEPRESSED)
401 infoPtr->BRbtnState = PGF_NORMAL;
403 else
405 *needsRepaint |= (infoPtr->BRbtnState != PGF_GRAYED);
406 infoPtr->BRbtnState = PGF_GRAYED;
411 static void
412 PAGER_NormalizeBtns(PAGER_INFO* infoPtr, BOOL* needsRepaint)
414 if (infoPtr->TLbtnState & (PGF_HOT | PGF_DEPRESSED))
416 infoPtr->TLbtnState = PGF_NORMAL;
417 *needsRepaint = TRUE;
420 if (infoPtr->BRbtnState & (PGF_HOT | PGF_DEPRESSED))
422 infoPtr->BRbtnState = PGF_NORMAL;
423 *needsRepaint = TRUE;
427 static void
428 PAGER_HideGrayBtns(PAGER_INFO* infoPtr, BOOL* needsResize)
430 if (infoPtr->TLbtnState == PGF_GRAYED)
432 infoPtr->TLbtnState = PGF_INVISIBLE;
433 *needsResize = TRUE;
436 if (infoPtr->BRbtnState == PGF_GRAYED)
438 infoPtr->BRbtnState = PGF_INVISIBLE;
439 *needsResize = TRUE;
443 static void
444 PAGER_UpdateBtns(HWND hwnd, PAGER_INFO *infoPtr,
445 INT scrollRange, BOOL hideGrayBtns)
447 BOOL resizeClient = FALSE;
448 BOOL repaintBtns = FALSE;
450 if (scrollRange < 0)
451 PAGER_NormalizeBtns(infoPtr, &repaintBtns);
452 else
453 PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
455 if (hideGrayBtns)
456 PAGER_HideGrayBtns(infoPtr, &resizeClient);
458 if (resizeClient) /* initiate NCCalcSize to resize client wnd */
459 SetWindowPos(hwnd, 0,0,0,0,0,
460 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
461 SWP_NOZORDER | SWP_NOACTIVATE);
463 if (repaintBtns)
464 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
467 static LRESULT
468 PAGER_SetPos(HWND hwnd, INT newPos, BOOL fromBtnPress)
470 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
471 INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr);
473 if ((scrollRange <= 0) || (newPos < 0))
474 infoPtr->nPos = 0;
475 else if (newPos > scrollRange)
476 infoPtr->nPos = scrollRange;
477 else
478 infoPtr->nPos = newPos;
480 TRACE("[%04x] pos=%d\n", hwnd, infoPtr->nPos);
482 /* gray and restore btns, and if from WM_SETPOS, hide the gray btns */
483 PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, !fromBtnPress);
484 PAGER_PositionChildWnd(hwnd, infoPtr);
486 return 0;
489 static LRESULT
490 PAGER_HandleWindowPosChanging(HWND hwnd, WINDOWPOS *winpos)
492 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
494 if (infoPtr->bNoResize && !(winpos->flags & SWP_NOSIZE))
496 /* don't let the app resize the nonscrollable dimension of a control
497 * that was created with CCS_NORESIZE style
498 * (i.e. height for a horizontal pager, or width for a vertical one) */
500 if (PAGER_IsHorizontal(hwnd))
501 winpos->cy = infoPtr->nHeight;
502 else
503 winpos->cx = infoPtr->nWidth;
506 return 0;
509 static void
510 PAGER_SetFixedWidth(HWND hwnd, PAGER_INFO* infoPtr)
512 /* Must set the non-scrollable dimension to be less than the full height/width
513 * so that NCCalcSize is called. The Msoft docs mention 3/4 factor for button
514 * size, and experimentation shows that affect is almost right. */
516 RECT wndRect;
517 INT delta, h;
518 GetWindowRect(hwnd, &wndRect);
520 /* see what the app says for btn width */
521 PAGER_CalcSize(hwnd, &infoPtr->nWidth, TRUE);
523 if (infoPtr->bNoResize)
525 delta = wndRect.right - wndRect.left - infoPtr->nWidth;
526 if (delta > infoPtr->nButtonSize)
527 infoPtr->nWidth += 4 * infoPtr->nButtonSize / 3;
528 else if (delta > 0)
529 infoPtr->nWidth += infoPtr->nButtonSize / 3;
532 h = wndRect.bottom - wndRect.top + infoPtr->nButtonSize;
534 /* adjust non-scrollable dimension to fit the child */
535 SetWindowPos(hwnd, 0, 0,0, infoPtr->nWidth, h,
536 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER);
539 TRACE("[%04x] infoPtr->nWidth set to %d\n",
540 hwnd, infoPtr->nWidth);
543 static void
544 PAGER_SetFixedHeight(HWND hwnd, PAGER_INFO* infoPtr)
546 /* Must set the non-scrollable dimension to be less than the full height/width
547 * so that NCCalcSize is called. The Msoft docs mention 3/4 factor for button
548 * size, and experimentation shows that affect is almost right. */
550 RECT wndRect;
551 INT delta, w;
552 GetWindowRect(hwnd, &wndRect);
554 /* see what the app says for btn height */
555 PAGER_CalcSize(hwnd, &infoPtr->nHeight, FALSE);
557 if (infoPtr->bNoResize)
559 delta = wndRect.bottom - wndRect.top - infoPtr->nHeight;
560 if (delta > infoPtr->nButtonSize)
561 infoPtr->nHeight += 4 * infoPtr->nButtonSize / 3;
562 else if (delta > 0)
563 infoPtr->nHeight += infoPtr->nButtonSize / 3;
566 w = wndRect.right - wndRect.left + infoPtr->nButtonSize;
568 /* adjust non-scrollable dimension to fit the child */
569 SetWindowPos(hwnd, 0, 0,0, w, infoPtr->nHeight,
570 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER);
572 TRACE("[%04x] infoPtr->nHeight set to %d\n",
573 hwnd, infoPtr->nHeight);
576 static LRESULT
577 PAGER_RecalcSize(HWND hwnd)
579 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
581 TRACE("[%04x]\n", hwnd);
583 if (infoPtr->hwndChild)
585 INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr);
587 if (scrollRange <= 0)
588 PAGER_SetPos(hwnd, 0, FALSE);
589 else
591 PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, TRUE);
592 PAGER_PositionChildWnd(hwnd, infoPtr);
596 return 1;
600 static LRESULT
601 PAGER_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
603 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
604 COLORREF clrTemp = infoPtr->clrBk;
606 infoPtr->clrBk = (COLORREF)lParam;
607 TRACE("[%04x] %06lx\n", hwnd, infoPtr->clrBk);
609 PAGER_RecalcSize(hwnd);
610 SendMessageA(hwnd, WM_NCPAINT, (WPARAM)0, (LPARAM)0);
612 return (LRESULT)clrTemp;
616 static LRESULT
617 PAGER_SetBorder (HWND hwnd, WPARAM wParam, LPARAM lParam)
619 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
620 INT nTemp = infoPtr->nBorder;
622 infoPtr->nBorder = (INT)lParam;
623 TRACE("[%04x] %d\n", hwnd, infoPtr->nBorder);
625 PAGER_RecalcSize(hwnd);
627 return (LRESULT)nTemp;
631 static LRESULT
632 PAGER_SetButtonSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
634 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
635 INT nTemp = infoPtr->nButtonSize;
637 infoPtr->nButtonSize = (INT)lParam;
638 TRACE("[%04x] %d\n", hwnd, infoPtr->nButtonSize);
640 PAGER_RecalcSize(hwnd);
642 return (LRESULT)nTemp;
646 static LRESULT
647 PAGER_SetChild (HWND hwnd, WPARAM wParam, LPARAM lParam)
649 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
651 infoPtr->hwndChild = IsWindow ((HWND)lParam) ? (HWND)lParam : 0;
653 if (infoPtr->hwndChild)
655 TRACE("[%04x] hwndChild=%04x\n", hwnd, infoPtr->hwndChild);
657 if (PAGER_IsHorizontal(hwnd))
658 PAGER_SetFixedHeight(hwnd, infoPtr);
659 else
660 PAGER_SetFixedWidth(hwnd, infoPtr);
662 /* position child within the page scroller */
663 SetWindowPos(infoPtr->hwndChild, HWND_TOP,
664 0,0,0,0,
665 SWP_SHOWWINDOW | SWP_NOSIZE);
667 PAGER_SetPos(hwnd, 0, FALSE);
670 return 0;
673 static void
674 PAGER_Scroll(HWND hwnd, INT dir)
676 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
677 NMPGSCROLL nmpgScroll;
678 RECT rcWnd;
680 if (infoPtr->hwndChild)
682 ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL));
683 nmpgScroll.hdr.hwndFrom = hwnd;
684 nmpgScroll.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
685 nmpgScroll.hdr.code = PGN_SCROLL;
687 GetWindowRect(hwnd, &rcWnd);
688 GetClientRect(hwnd, &nmpgScroll.rcParent);
689 nmpgScroll.iXpos = nmpgScroll.iYpos = 0;
690 nmpgScroll.iDir = dir;
692 if (PAGER_IsHorizontal(hwnd))
694 nmpgScroll.iScroll = rcWnd.right - rcWnd.left;
695 nmpgScroll.iXpos = infoPtr->nPos;
697 else
699 nmpgScroll.iScroll = rcWnd.bottom - rcWnd.top;
700 nmpgScroll.iYpos = infoPtr->nPos;
702 nmpgScroll.iScroll -= 2*infoPtr->nButtonSize;
704 SendMessageA (hwnd, WM_NOTIFY,
705 (WPARAM)nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll);
707 TRACE("[%04x] PGN_SCROLL returns iScroll=%d\n", hwnd, nmpgScroll.iScroll);
709 if (nmpgScroll.iScroll > 0)
711 if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP)
712 PAGER_SetPos(hwnd, infoPtr->nPos - nmpgScroll.iScroll, TRUE);
713 else
714 PAGER_SetPos(hwnd, infoPtr->nPos + nmpgScroll.iScroll, TRUE);
719 static LRESULT
720 PAGER_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
722 PAGER_INFO *infoPtr;
723 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
725 /* allocate memory for info structure */
726 infoPtr = (PAGER_INFO *)COMCTL32_Alloc (sizeof(PAGER_INFO));
727 SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
729 /* set default settings */
730 infoPtr->hwndChild = (HWND)NULL;
731 infoPtr->bNoResize = dwStyle & CCS_NORESIZE;
732 infoPtr->clrBk = GetSysColor(COLOR_BTNFACE);
733 infoPtr->nBorder = 0;
734 infoPtr->nButtonSize = 12;
735 infoPtr->nPos = 0;
736 infoPtr->nWidth = 0;
737 infoPtr->nHeight = 0;
738 infoPtr->bForward = FALSE;
739 infoPtr->TLbtnState = PGF_INVISIBLE;
740 infoPtr->BRbtnState = PGF_INVISIBLE;
742 if (dwStyle & PGS_AUTOSCROLL)
743 FIXME("[%04x] Autoscroll style is not implemented yet.\n", hwnd);
744 if (dwStyle & PGS_DRAGNDROP)
745 FIXME("[%04x] Drag and Drop style is not implemented yet.\n", hwnd);
747 * If neither horizontal nor vertical style specified, default to vertical.
748 * This is probably not necessary, since the style may be set later on as
749 * the control is initialized, but just in case it isn't, set it here.
751 if (!(dwStyle & PGS_HORZ) && !(dwStyle & PGS_VERT))
753 dwStyle |= PGS_VERT;
754 SetWindowLongA(hwnd, GWL_STYLE, dwStyle);
757 return 0;
761 static LRESULT
762 PAGER_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
764 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
765 /* free pager info data */
766 COMCTL32_Free (infoPtr);
767 SetWindowLongA (hwnd, 0, 0);
768 return 0;
771 static LRESULT
772 PAGER_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
774 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
775 LPRECT lpRect = (LPRECT)lParam;
777 * lParam points to a RECT struct. On entry, the struct
778 * contains the proposed wnd rectangle for the window.
779 * On exit, the struct should contain the screen
780 * coordinates of the corresponding window's client area.
783 if (PAGER_IsHorizontal(hwnd))
785 if (infoPtr->TLbtnState) /* != PGF_INVISIBLE */
786 lpRect->left += infoPtr->nButtonSize;
787 if (infoPtr->BRbtnState)
788 lpRect->right -= infoPtr->nButtonSize;
790 else
792 if (infoPtr->TLbtnState)
793 lpRect->top += infoPtr->nButtonSize;
794 if (infoPtr->BRbtnState)
795 lpRect->bottom -= infoPtr->nButtonSize;
798 TRACE("[%04x] client rect set to %dx%d at (%d,%d)\n", hwnd,
799 lpRect->right-lpRect->left,
800 lpRect->bottom-lpRect->top,
801 lpRect->left, lpRect->top);
803 return 0;
806 static LRESULT
807 PAGER_NCPaint (HWND hwnd, WPARAM wParam, LPARAM lParam)
809 PAGER_INFO* infoPtr = PAGER_GetInfoPtr(hwnd);
810 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
811 RECT rcWindow, rcBottomRight, rcTopLeft;
812 HDC hdc;
813 BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
815 if (dwStyle & WS_MINIMIZE)
816 return 0;
818 DefWindowProcA (hwnd, WM_NCPAINT, wParam, lParam);
820 if (!(hdc = GetDCEx (hwnd, 0, DCX_USESTYLE | DCX_WINDOW)))
821 return 0;
823 GetWindowRect (hwnd, &rcWindow);
824 OffsetRect (&rcWindow, -rcWindow.left, -rcWindow.top);
826 rcTopLeft = rcBottomRight = rcWindow;
827 if (bHorizontal)
829 rcTopLeft.right = rcTopLeft.left + infoPtr->nButtonSize;
830 rcBottomRight.left = rcBottomRight.right - infoPtr->nButtonSize;
832 else
834 rcTopLeft.bottom = rcTopLeft.top + infoPtr->nButtonSize;
835 rcBottomRight.top = rcBottomRight.bottom - infoPtr->nButtonSize;
838 PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft,
839 bHorizontal, TRUE, infoPtr->TLbtnState);
840 PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight,
841 bHorizontal, FALSE, infoPtr->BRbtnState);
843 ReleaseDC( hwnd, hdc );
844 return 0;
847 static INT
848 PAGER_HitTest (HWND hwnd, LPPOINT pt)
850 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
851 RECT clientRect;
852 BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
854 GetClientRect (hwnd, &clientRect);
856 if (PtInRect(&clientRect, *pt))
858 /* TRACE("HTCLIENT\n"); */
859 return HTCLIENT;
862 if (infoPtr->TLbtnState && infoPtr->TLbtnState != PGF_GRAYED)
864 if (bHorizontal)
866 if (pt->x < clientRect.left)
868 /* TRACE("HTLEFT\n"); */
869 return HTLEFT;
872 else
874 if (pt->y < clientRect.top)
876 /* TRACE("HTTOP\n"); */
877 return HTTOP;
882 if (infoPtr->BRbtnState && infoPtr->BRbtnState != PGF_GRAYED)
884 if (bHorizontal)
886 if (pt->x > clientRect.right)
888 /* TRACE("HTRIGHT\n"); */
889 return HTRIGHT;
892 else
894 if (pt->y > clientRect.bottom)
896 /* TRACE("HTBOTTOM\n"); */
897 return HTBOTTOM;
902 /* TRACE("HTNOWHERE\n"); */
903 return HTNOWHERE;
906 static LRESULT
907 PAGER_NCHitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
909 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
910 ScreenToClient (hwnd, &pt);
911 return PAGER_HitTest(hwnd, &pt);
914 static LRESULT
915 PAGER_SetCursor( HWND hwnd, WPARAM wParam, LPARAM lParam )
917 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
918 BOOL notCaptured = FALSE;
920 switch(LOWORD(lParam))
922 case HTLEFT:
923 case HTTOP:
924 if ((notCaptured = infoPtr->TLbtnState != PGF_HOT))
925 infoPtr->TLbtnState = PGF_HOT;
926 break;
927 case HTRIGHT:
928 case HTBOTTOM:
929 if ((notCaptured = infoPtr->BRbtnState != PGF_HOT))
930 infoPtr->BRbtnState = PGF_HOT;
931 break;
932 default:
933 return FALSE;
936 if (notCaptured)
938 TRACKMOUSEEVENT trackinfo;
940 TRACE("[%04x] SetCapture\n", hwnd);
941 SetCapture(hwnd);
943 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
944 trackinfo.dwFlags = TME_QUERY;
945 trackinfo.hwndTrack = hwnd;
946 trackinfo.dwHoverTime = HOVER_DEFAULT;
948 /* call _TrackMouseEvent to see if we are currently tracking for this hwnd */
949 _TrackMouseEvent(&trackinfo);
951 /* Make sure tracking is enabled so we receive a WM_MOUSELEAVE message */
952 if(!(trackinfo.dwFlags & TME_LEAVE)) {
953 trackinfo.dwFlags = TME_LEAVE; /* notify upon leaving */
955 /* call TRACKMOUSEEVENT so we receive a WM_MOUSELEAVE message */
956 /* and can properly deactivate the hot button */
957 _TrackMouseEvent(&trackinfo);
960 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
963 return TRUE;
966 static LRESULT
967 PAGER_MouseLeave (HWND hwnd, WPARAM wParam, LPARAM lParam)
969 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
971 TRACE("[%04x] ReleaseCapture\n", hwnd);
972 ReleaseCapture();
974 /* Notify parent of released mouse capture */
976 NMHDR nmhdr;
977 ZeroMemory (&nmhdr, sizeof (NMHDR));
978 nmhdr.hwndFrom = hwnd;
979 nmhdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
980 nmhdr.code = NM_RELEASEDCAPTURE;
981 SendMessageA (GetParent(hwnd), WM_NOTIFY,
982 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
985 /* make HOT btns NORMAL and hide gray btns */
986 PAGER_UpdateBtns(hwnd, infoPtr, -1, TRUE);
988 return TRUE;
991 static LRESULT
992 PAGER_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
994 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
995 BOOL repaintBtns = FALSE;
996 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
997 INT hit;
999 TRACE("[%04x]\n", hwnd);
1001 hit = PAGER_HitTest(hwnd, &pt);
1003 /* put btn in DEPRESSED state */
1004 if (hit == HTLEFT || hit == HTTOP)
1006 repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED;
1007 infoPtr->TLbtnState = PGF_DEPRESSED;
1009 else if (hit == HTRIGHT || hit == HTBOTTOM)
1011 repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED;
1012 infoPtr->BRbtnState = PGF_DEPRESSED;
1015 if (repaintBtns)
1016 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
1018 switch(hit)
1020 case HTLEFT:
1021 TRACE("[%04x] PGF_SCROLLLEFT\n", hwnd);
1022 PAGER_Scroll(hwnd, PGF_SCROLLLEFT);
1023 break;
1024 case HTTOP:
1025 TRACE("[%04x] PGF_SCROLLUP\n", hwnd);
1026 PAGER_Scroll(hwnd, PGF_SCROLLUP);
1027 break;
1028 case HTRIGHT:
1029 TRACE("[%04x] PGF_SCROLLRIGHT\n", hwnd);
1030 PAGER_Scroll(hwnd, PGF_SCROLLRIGHT);
1031 break;
1032 case HTBOTTOM:
1033 TRACE("[%04x] PGF_SCROLLDOWN\n", hwnd);
1034 PAGER_Scroll(hwnd, PGF_SCROLLDOWN);
1035 break;
1036 default:
1037 break;
1040 return TRUE;
1043 static LRESULT
1044 PAGER_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
1046 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1047 TRACE("[%04x]\n", hwnd);
1049 /* make PRESSED btns NORMAL but don't hide gray btns */
1050 PAGER_UpdateBtns(hwnd, infoPtr, -1, FALSE);
1052 return 0;
1055 static LRESULT
1056 PAGER_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
1058 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1059 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
1060 RECT rect;
1062 GetClientRect (hwnd, &rect);
1063 FillRect ((HDC)wParam, &rect, hBrush);
1065 /* background color of the child should be the same as the pager */
1066 if (infoPtr->hwndChild)
1068 GetClientRect (infoPtr->hwndChild, &rect);
1069 FillRect ((HDC)wParam, &rect, hBrush);
1072 DeleteObject (hBrush);
1073 return TRUE;
1077 static LRESULT
1078 PAGER_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1080 /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */
1082 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1083 TRACE("[%04x] %dx%d\n", hwnd, LOWORD(lParam), HIWORD(lParam));
1085 if (PAGER_IsHorizontal(hwnd))
1086 infoPtr->nHeight = HIWORD(lParam);
1087 else
1088 infoPtr->nWidth = LOWORD(lParam);
1090 return PAGER_RecalcSize(hwnd);
1094 static LRESULT WINAPI
1095 PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1097 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1099 if (!infoPtr && (uMsg != WM_CREATE))
1100 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1102 switch (uMsg)
1104 case PGM_FORWARDMOUSE:
1105 return PAGER_ForwardMouse (hwnd, wParam);
1107 case PGM_GETBKCOLOR:
1108 return PAGER_GetBkColor(hwnd);
1110 case PGM_GETBORDER:
1111 return PAGER_GetBorder(hwnd);
1113 case PGM_GETBUTTONSIZE:
1114 return PAGER_GetButtonSize(hwnd);
1116 case PGM_GETPOS:
1117 return PAGER_GetPos(hwnd);
1119 case PGM_GETBUTTONSTATE:
1120 return PAGER_GetButtonState (hwnd, wParam, lParam);
1122 /* case PGM_GETDROPTARGET: */
1124 case PGM_RECALCSIZE:
1125 return PAGER_RecalcSize(hwnd);
1127 case PGM_SETBKCOLOR:
1128 return PAGER_SetBkColor (hwnd, wParam, lParam);
1130 case PGM_SETBORDER:
1131 return PAGER_SetBorder (hwnd, wParam, lParam);
1133 case PGM_SETBUTTONSIZE:
1134 return PAGER_SetButtonSize (hwnd, wParam, lParam);
1136 case PGM_SETCHILD:
1137 return PAGER_SetChild (hwnd, wParam, lParam);
1139 case PGM_SETPOS:
1140 return PAGER_SetPos(hwnd, (INT)lParam, FALSE);
1142 case WM_CREATE:
1143 return PAGER_Create (hwnd, wParam, lParam);
1145 case WM_DESTROY:
1146 return PAGER_Destroy (hwnd, wParam, lParam);
1148 case WM_SIZE:
1149 return PAGER_Size (hwnd, wParam, lParam);
1151 case WM_NCPAINT:
1152 return PAGER_NCPaint (hwnd, wParam, lParam);
1154 case WM_WINDOWPOSCHANGING:
1155 return PAGER_HandleWindowPosChanging (hwnd, (WINDOWPOS*)lParam);
1157 case WM_NCCALCSIZE:
1158 return PAGER_NCCalcSize (hwnd, wParam, lParam);
1160 case WM_NCHITTEST:
1161 return PAGER_NCHitTest (hwnd, wParam, lParam);
1163 case WM_SETCURSOR:
1165 if (hwnd == (HWND)wParam)
1166 return PAGER_SetCursor(hwnd, wParam, lParam);
1167 else /* its for the child */
1168 return 0;
1171 case WM_MOUSEMOVE:
1172 if (infoPtr->bForward && infoPtr->hwndChild)
1173 PostMessageA(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam);
1174 return TRUE;
1176 case WM_MOUSELEAVE:
1177 return PAGER_MouseLeave (hwnd, wParam, lParam);
1179 case WM_LBUTTONDOWN:
1180 return PAGER_LButtonDown (hwnd, wParam, lParam);
1182 case WM_LBUTTONUP:
1183 return PAGER_LButtonUp (hwnd, wParam, lParam);
1185 case WM_ERASEBKGND:
1186 return PAGER_EraseBackground (hwnd, wParam, lParam);
1188 case WM_PAINT:
1189 return PAGER_Paint (hwnd, wParam);
1191 case WM_NOTIFY:
1192 case WM_COMMAND:
1193 return SendMessageA (GetParent (hwnd), uMsg, wParam, lParam);
1195 default:
1196 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1199 return 0;
1203 VOID
1204 PAGER_Register (void)
1206 WNDCLASSA wndClass;
1208 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1209 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS;
1210 wndClass.lpfnWndProc = (WNDPROC)PAGER_WindowProc;
1211 wndClass.cbClsExtra = 0;
1212 wndClass.cbWndExtra = sizeof(PAGER_INFO *);
1213 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
1214 wndClass.hbrBackground = 0;
1215 wndClass.lpszClassName = WC_PAGESCROLLERA;
1217 RegisterClassA (&wndClass);
1221 VOID
1222 PAGER_Unregister (void)
1224 UnregisterClassA (WC_PAGESCROLLERA, (HINSTANCE)NULL);