4 * Copyright 1998, 1999 Eric Kohl
7 * Tested primarily with the controlspy Pager application.
8 * Susan Farley (susan@codeweavers.com)
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.
20 #include "debugtools.h"
22 DEFAULT_DEBUG_CHANNEL(pager);
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 */
37 INT direction; /* direction of the scroll, (e.g. PGF_SCROLLUP) */
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
48 #define INITIAL_DELAY 500
49 #define REPEAT_DELAY 50
51 /* the horizontal arrows are:
64 PAGER_DrawHorzArrow (HDC hdc, RECT r, INT colorRef, BOOL left)
69 w = r.right - r.left + 1;
70 h = r.bottom - r.top + 1;
71 if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
72 return; /* refuse to draw partial arrow */
74 hOldPen = SelectObject ( hdc, GetSysColorPen (colorRef));
77 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 3;
78 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
79 MoveToEx (hdc, x, y, NULL);
80 LineTo (hdc, x--, y+5); y++;
81 MoveToEx (hdc, x, y, NULL);
82 LineTo (hdc, x--, y+3); y++;
83 MoveToEx (hdc, x, y, NULL);
88 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
89 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
90 MoveToEx (hdc, x, y, NULL);
91 LineTo (hdc, x++, y+5); y++;
92 MoveToEx (hdc, x, y, NULL);
93 LineTo (hdc, x++, y+3); y++;
94 MoveToEx (hdc, x, y, NULL);
98 SelectObject( hdc, hOldPen );
101 /* the vertical arrows are:
111 PAGER_DrawVertArrow (HDC hdc, RECT r, INT colorRef, BOOL up)
116 w = r.right - r.left + 1;
117 h = r.bottom - r.top + 1;
118 if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
119 return; /* refuse to draw partial arrow */
121 hOldPen = SelectObject ( hdc, GetSysColorPen (colorRef));
124 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
125 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 3;
126 MoveToEx (hdc, x, y, NULL);
127 LineTo (hdc, x+5, y--); x++;
128 MoveToEx (hdc, x, y, NULL);
129 LineTo (hdc, x+3, y--); x++;
130 MoveToEx (hdc, x, y, NULL);
131 LineTo (hdc, x+1, y);
135 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
136 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
137 MoveToEx (hdc, x, y, NULL);
138 LineTo (hdc, x+5, y++); x++;
139 MoveToEx (hdc, x, y, NULL);
140 LineTo (hdc, x+3, y++); x++;
141 MoveToEx (hdc, x, y, NULL);
142 LineTo (hdc, x+1, y);
145 SelectObject( hdc, hOldPen );
149 PAGER_DrawButton(HDC hdc, COLORREF clrBk, RECT arrowRect,
150 BOOL horz, BOOL topLeft, INT btnState)
152 HBRUSH hBrush, hOldBrush;
155 if (!btnState) /* PGF_INVISIBLE */
158 if ((rc.right - rc.left <= 0) || (rc.bottom - rc.top <= 0))
161 hBrush = CreateSolidBrush(clrBk);
162 hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
164 FillRect(hdc, &rc, hBrush);
166 if (btnState == PGF_HOT)
168 DrawEdge( hdc, &rc, BDR_RAISEDINNER, BF_RECT);
170 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
172 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
174 else if (btnState == PGF_NORMAL)
176 DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
178 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
180 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
182 else if (btnState == PGF_DEPRESSED)
184 DrawEdge( hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
186 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
188 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
190 else if (btnState == PGF_GRAYED)
192 DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
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);
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);
207 SelectObject( hdc, hOldBrush );
208 DeleteObject(hBrush);
211 /* << PAGER_GetDropTarget >> */
213 static inline LRESULT
214 PAGER_ForwardMouse (HWND hwnd, WPARAM wParam)
216 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
217 TRACE("[%04x]\n", hwnd);
219 infoPtr->bForward = (BOOL)wParam;
224 static inline LRESULT
225 PAGER_GetButtonState (HWND hwnd, WPARAM wParam, LPARAM lParam)
227 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
228 LRESULT btnState = PGF_INVISIBLE;
229 INT btn = (INT)lParam;
230 TRACE("[%04x]\n", hwnd);
232 if (btn == PGB_TOPORLEFT)
233 btnState = infoPtr->TLbtnState;
234 else if (btn == PGB_BOTTOMORRIGHT)
235 btnState = infoPtr->BRbtnState;
241 static inline LRESULT
242 PAGER_GetPos(HWND hwnd)
244 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
245 TRACE("[%04x] returns %d\n", hwnd, infoPtr->nPos);
246 return (LRESULT)infoPtr->nPos;
249 static inline LRESULT
250 PAGER_GetButtonSize(HWND hwnd)
252 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
253 TRACE("[%04x] returns %d\n", hwnd, infoPtr->nButtonSize);
254 return (LRESULT)infoPtr->nButtonSize;
257 static inline LRESULT
258 PAGER_GetBorder(HWND hwnd)
260 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
261 TRACE("[%04x] returns %d\n", hwnd, infoPtr->nBorder);
262 return (LRESULT)infoPtr->nBorder;
265 static inline LRESULT
266 PAGER_GetBkColor(HWND hwnd)
268 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
269 TRACE("[%04x] returns %06lx\n", hwnd, infoPtr->clrBk);
270 return (LRESULT)infoPtr->clrBk;
274 PAGER_CalcSize (HWND hwnd, INT* size, BOOL getWidth)
277 ZeroMemory (&nmpgcs, sizeof (NMPGCALCSIZE));
278 nmpgcs.hdr.hwndFrom = hwnd;
279 nmpgcs.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
280 nmpgcs.hdr.code = PGN_CALCSIZE;
281 nmpgcs.dwFlag = getWidth ? PGF_CALCWIDTH : PGF_CALCHEIGHT;
282 nmpgcs.iWidth = getWidth ? *size : 0;
283 nmpgcs.iHeight = getWidth ? 0 : *size;
284 SendMessageA (GetParent (hwnd), WM_NOTIFY,
285 (WPARAM)nmpgcs.hdr.idFrom, (LPARAM)&nmpgcs);
287 *size = getWidth ? nmpgcs.iWidth : nmpgcs.iHeight;
289 TRACE("[%04x] PGN_CALCSIZE returns %s=%d\n", hwnd,
290 getWidth ? "width" : "height", *size);
294 PAGER_PositionChildWnd(HWND hwnd, PAGER_INFO* infoPtr)
296 if (infoPtr->hwndChild)
299 int nPos = infoPtr->nPos;
301 /* compensate for a grayed btn, which will soon become invisible */
302 if (infoPtr->TLbtnState == PGF_GRAYED)
303 nPos += infoPtr->nButtonSize;
305 GetClientRect(hwnd, &rcClient);
307 if (PAGER_IsHorizontal(hwnd))
309 int wndSize = max(0, rcClient.right - rcClient.left);
310 if (infoPtr->nWidth < wndSize)
311 infoPtr->nWidth = wndSize;
313 TRACE("[%04x] SWP %dx%d at (%d,%d)\n", hwnd,
314 infoPtr->nWidth, infoPtr->nHeight,
316 SetWindowPos(infoPtr->hwndChild, 0,
318 infoPtr->nWidth, infoPtr->nHeight,
323 int wndSize = max(0, rcClient.bottom - rcClient.top);
324 if (infoPtr->nHeight < wndSize)
325 infoPtr->nHeight = wndSize;
327 TRACE("[%04x] SWP %dx%d at (%d,%d)\n", hwnd,
328 infoPtr->nWidth, infoPtr->nHeight,
330 SetWindowPos(infoPtr->hwndChild, 0,
332 infoPtr->nWidth, infoPtr->nHeight,
336 InvalidateRect(infoPtr->hwndChild, NULL, TRUE);
341 PAGER_GetScrollRange(HWND hwnd, PAGER_INFO* infoPtr)
345 if (infoPtr->hwndChild)
347 INT wndSize, childSize;
349 GetWindowRect(hwnd, &wndRect);
351 if (PAGER_IsHorizontal(hwnd))
353 wndSize = wndRect.right - wndRect.left;
354 PAGER_CalcSize(hwnd, &infoPtr->nWidth, TRUE);
355 childSize = infoPtr->nWidth;
359 wndSize = wndRect.bottom - wndRect.top;
360 PAGER_CalcSize(hwnd, &infoPtr->nHeight, FALSE);
361 childSize = infoPtr->nHeight;
364 TRACE("childSize = %d, wndSize = %d\n", childSize, wndSize);
365 if (childSize > wndSize)
366 scrollRange = childSize - wndSize + infoPtr->nButtonSize;
369 TRACE("[%04x] returns %d\n", hwnd, scrollRange);
374 PAGER_GrayAndRestoreBtns(PAGER_INFO* infoPtr, INT scrollRange,
375 BOOL* needsResize, BOOL* needsRepaint)
377 if (infoPtr->nPos > 0)
379 *needsResize |= !infoPtr->TLbtnState; /* PGF_INVISIBLE */
380 if (infoPtr->TLbtnState != PGF_DEPRESSED)
381 infoPtr->TLbtnState = PGF_NORMAL;
385 *needsRepaint |= (infoPtr->TLbtnState != PGF_GRAYED);
386 infoPtr->TLbtnState = PGF_GRAYED;
389 if (scrollRange <= 0)
391 *needsRepaint |= (infoPtr->TLbtnState != PGF_GRAYED);
392 infoPtr->TLbtnState = PGF_GRAYED;
393 *needsRepaint |= (infoPtr->BRbtnState != PGF_GRAYED);
394 infoPtr->BRbtnState = PGF_GRAYED;
396 else if (infoPtr->nPos < scrollRange)
398 *needsResize |= !infoPtr->BRbtnState; /* PGF_INVISIBLE */
399 if (infoPtr->BRbtnState != PGF_DEPRESSED)
400 infoPtr->BRbtnState = PGF_NORMAL;
404 *needsRepaint |= (infoPtr->BRbtnState != PGF_GRAYED);
405 infoPtr->BRbtnState = PGF_GRAYED;
411 PAGER_NormalizeBtns(PAGER_INFO* infoPtr, BOOL* needsRepaint)
413 if (infoPtr->TLbtnState & (PGF_HOT | PGF_DEPRESSED))
415 infoPtr->TLbtnState = PGF_NORMAL;
416 *needsRepaint = TRUE;
419 if (infoPtr->BRbtnState & (PGF_HOT | PGF_DEPRESSED))
421 infoPtr->BRbtnState = PGF_NORMAL;
422 *needsRepaint = TRUE;
427 PAGER_HideGrayBtns(PAGER_INFO* infoPtr, BOOL* needsResize)
429 if (infoPtr->TLbtnState == PGF_GRAYED)
431 infoPtr->TLbtnState = PGF_INVISIBLE;
435 if (infoPtr->BRbtnState == PGF_GRAYED)
437 infoPtr->BRbtnState = PGF_INVISIBLE;
443 PAGER_UpdateBtns(HWND hwnd, PAGER_INFO *infoPtr,
444 INT scrollRange, BOOL hideGrayBtns)
446 BOOL resizeClient = FALSE;
447 BOOL repaintBtns = FALSE;
450 PAGER_NormalizeBtns(infoPtr, &repaintBtns);
452 PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
455 PAGER_HideGrayBtns(infoPtr, &resizeClient);
457 if (resizeClient) /* initiate NCCalcSize to resize client wnd */ {
458 SetWindowPos(hwnd, 0,0,0,0,0,
459 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
460 SWP_NOZORDER | SWP_NOACTIVATE);
464 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
468 PAGER_SetPos(HWND hwnd, INT newPos, BOOL fromBtnPress)
470 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
471 INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr);
472 INT oldPos = infoPtr->nPos;
474 if ((scrollRange <= 0) || (newPos < 0))
476 else if (newPos > scrollRange)
477 infoPtr->nPos = scrollRange;
479 infoPtr->nPos = newPos;
481 TRACE("[%04x] pos=%d\n", hwnd, infoPtr->nPos);
483 if (infoPtr->nPos != oldPos)
485 /* gray and restore btns, and if from WM_SETPOS, hide the gray btns */
486 PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, !fromBtnPress);
487 PAGER_PositionChildWnd(hwnd, infoPtr);
494 PAGER_HandleWindowPosChanging(HWND hwnd, WPARAM wParam, WINDOWPOS *winpos)
496 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
498 if (infoPtr->bNoResize && !(winpos->flags & SWP_NOSIZE))
500 /* don't let the app resize the nonscrollable dimension of a control
501 * that was created with CCS_NORESIZE style
502 * (i.e. height for a horizontal pager, or width for a vertical one) */
504 /* except if the current dimension is 0 and app is setting for
505 * first time, then save amount as dimension. - GA 8/01 */
507 if (PAGER_IsHorizontal(hwnd))
508 if (!infoPtr->nHeight && winpos->cy)
509 infoPtr->nHeight = winpos->cy;
511 winpos->cy = infoPtr->nHeight;
513 if (!infoPtr->nWidth && winpos->cx)
514 infoPtr->nWidth = winpos->cx;
516 winpos->cx = infoPtr->nWidth;
520 DefWindowProcA (hwnd, WM_WINDOWPOSCHANGING, wParam, (LPARAM)winpos);
526 PAGER_SetFixedWidth(HWND hwnd, PAGER_INFO* infoPtr)
528 /* Must set the non-scrollable dimension to be less than the full height/width
529 * so that NCCalcSize is called. The Msoft docs mention 3/4 factor for button
530 * size, and experimentation shows that affect is almost right. */
534 GetWindowRect(hwnd, &wndRect);
536 /* see what the app says for btn width */
537 PAGER_CalcSize(hwnd, &infoPtr->nWidth, TRUE);
539 if (infoPtr->bNoResize)
541 delta = wndRect.right - wndRect.left - infoPtr->nWidth;
542 if (delta > infoPtr->nButtonSize)
543 infoPtr->nWidth += 4 * infoPtr->nButtonSize / 3;
545 infoPtr->nWidth += infoPtr->nButtonSize / 3;
548 h = wndRect.bottom - wndRect.top + infoPtr->nButtonSize;
550 TRACE("[%04x] infoPtr->nWidth set to %d\n",
551 hwnd, infoPtr->nWidth);
557 PAGER_SetFixedHeight(HWND hwnd, PAGER_INFO* infoPtr)
559 /* Must set the non-scrollable dimension to be less than the full height/width
560 * so that NCCalcSize is called. The Msoft docs mention 3/4 factor for button
561 * size, and experimentation shows that affect is almost right. */
565 GetWindowRect(hwnd, &wndRect);
567 /* see what the app says for btn height */
568 PAGER_CalcSize(hwnd, &infoPtr->nHeight, FALSE);
570 if (infoPtr->bNoResize)
572 delta = wndRect.bottom - wndRect.top - infoPtr->nHeight;
573 if (delta > infoPtr->nButtonSize)
574 infoPtr->nHeight += infoPtr->nButtonSize;
576 infoPtr->nHeight += infoPtr->nButtonSize / 3;
579 w = wndRect.right - wndRect.left + infoPtr->nButtonSize;
581 TRACE("[%04x] infoPtr->nHeight set to %d\n",
582 hwnd, infoPtr->nHeight);
588 PAGER_RecalcSize(HWND hwnd)
590 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
592 TRACE("[%04x]\n", hwnd);
594 if (infoPtr->hwndChild)
596 INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr);
598 if (scrollRange <= 0)
601 PAGER_SetPos(hwnd, 0, FALSE);
605 PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, TRUE);
606 PAGER_PositionChildWnd(hwnd, infoPtr);
615 PAGER_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
617 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
618 COLORREF clrTemp = infoPtr->clrBk;
620 infoPtr->clrBk = (COLORREF)lParam;
621 TRACE("[%04x] %06lx\n", hwnd, infoPtr->clrBk);
623 PAGER_RecalcSize(hwnd);
624 SendMessageA(hwnd, WM_NCPAINT, (WPARAM)0, (LPARAM)0);
626 return (LRESULT)clrTemp;
631 PAGER_SetBorder (HWND hwnd, WPARAM wParam, LPARAM lParam)
633 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
634 INT nTemp = infoPtr->nBorder;
636 infoPtr->nBorder = (INT)lParam;
637 TRACE("[%04x] %d\n", hwnd, infoPtr->nBorder);
639 PAGER_RecalcSize(hwnd);
641 return (LRESULT)nTemp;
646 PAGER_SetButtonSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
648 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
649 INT nTemp = infoPtr->nButtonSize;
651 infoPtr->nButtonSize = (INT)lParam;
652 TRACE("[%04x] %d\n", hwnd, infoPtr->nButtonSize);
654 PAGER_RecalcSize(hwnd);
656 return (LRESULT)nTemp;
661 PAGER_SetChild (HWND hwnd, WPARAM wParam, LPARAM lParam)
663 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
666 infoPtr->hwndChild = IsWindow ((HWND)lParam) ? (HWND)lParam : 0;
668 if (infoPtr->hwndChild)
670 TRACE("[%04x] hwndChild=%04x\n", hwnd, infoPtr->hwndChild);
672 if (PAGER_IsHorizontal(hwnd)) {
673 hw = PAGER_SetFixedHeight(hwnd, infoPtr);
674 /* adjust non-scrollable dimension to fit the child */
675 SetWindowPos(hwnd, 0, 0,0, hw, infoPtr->nHeight,
676 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
677 SWP_NOSIZE | SWP_NOACTIVATE);
680 hw = PAGER_SetFixedWidth(hwnd, infoPtr);
681 /* adjust non-scrollable dimension to fit the child */
682 SetWindowPos(hwnd, 0, 0,0, infoPtr->nWidth, hw,
683 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
684 SWP_NOSIZE | SWP_NOACTIVATE);
687 /* position child within the page scroller */
688 SetWindowPos(infoPtr->hwndChild, HWND_TOP,
690 SWP_SHOWWINDOW | SWP_NOSIZE); /* native is 0 */
693 PAGER_SetPos(hwnd, 0, FALSE);
700 PAGER_Scroll(HWND hwnd, INT dir)
702 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
703 NMPGSCROLL nmpgScroll;
706 if (infoPtr->hwndChild)
708 ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL));
709 nmpgScroll.hdr.hwndFrom = hwnd;
710 nmpgScroll.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
711 nmpgScroll.hdr.code = PGN_SCROLL;
713 GetWindowRect(hwnd, &rcWnd);
714 GetClientRect(hwnd, &nmpgScroll.rcParent);
715 nmpgScroll.iXpos = nmpgScroll.iYpos = 0;
716 nmpgScroll.iDir = dir;
718 if (PAGER_IsHorizontal(hwnd))
720 nmpgScroll.iScroll = rcWnd.right - rcWnd.left;
721 nmpgScroll.iXpos = infoPtr->nPos;
725 nmpgScroll.iScroll = rcWnd.bottom - rcWnd.top;
726 nmpgScroll.iYpos = infoPtr->nPos;
728 nmpgScroll.iScroll -= 2*infoPtr->nButtonSize;
730 SendMessageA (hwnd, WM_NOTIFY,
731 (WPARAM)nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll);
733 TRACE("[%04x] PGN_SCROLL returns iScroll=%d\n", hwnd, nmpgScroll.iScroll);
735 if (nmpgScroll.iScroll > 0)
737 infoPtr->direction = dir;
739 if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP)
740 PAGER_SetPos(hwnd, infoPtr->nPos - nmpgScroll.iScroll, TRUE);
742 PAGER_SetPos(hwnd, infoPtr->nPos + nmpgScroll.iScroll, TRUE);
745 infoPtr->direction = -1;
750 PAGER_FmtLines(HWND hwnd)
752 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
754 /* initiate NCCalcSize to resize client wnd and get size */
755 SetWindowPos(hwnd, 0, 0,0,0,0,
756 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
757 SWP_NOZORDER | SWP_NOACTIVATE);
759 SetWindowPos(infoPtr->hwndChild, 0,
760 0,0,infoPtr->nWidth,infoPtr->nHeight,
763 return DefWindowProcA (hwnd, EM_FMTLINES, 0, 0);
767 PAGER_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
770 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
772 /* allocate memory for info structure */
773 infoPtr = (PAGER_INFO *)COMCTL32_Alloc (sizeof(PAGER_INFO));
774 SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
776 /* set default settings */
777 infoPtr->hwndChild = (HWND)NULL;
778 infoPtr->bNoResize = dwStyle & CCS_NORESIZE;
779 infoPtr->clrBk = GetSysColor(COLOR_BTNFACE);
780 infoPtr->nBorder = 0;
781 infoPtr->nButtonSize = 12;
784 infoPtr->nHeight = 0;
785 infoPtr->bForward = FALSE;
786 infoPtr->TLbtnState = PGF_INVISIBLE;
787 infoPtr->BRbtnState = PGF_INVISIBLE;
788 infoPtr->direction = -1;
790 if (dwStyle & PGS_AUTOSCROLL)
791 FIXME("[%04x] Autoscroll style is not implemented yet.\n", hwnd);
792 if (dwStyle & PGS_DRAGNDROP)
793 FIXME("[%04x] Drag and Drop style is not implemented yet.\n", hwnd);
795 * If neither horizontal nor vertical style specified, default to vertical.
796 * This is probably not necessary, since the style may be set later on as
797 * the control is initialized, but just in case it isn't, set it here.
799 if (!(dwStyle & PGS_HORZ) && !(dwStyle & PGS_VERT))
802 SetWindowLongA(hwnd, GWL_STYLE, dwStyle);
810 PAGER_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
812 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
813 /* free pager info data */
814 COMCTL32_Free (infoPtr);
815 SetWindowLongA (hwnd, 0, 0);
820 PAGER_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
822 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
823 LPRECT lpRect = (LPRECT)lParam;
824 RECT rcChildw, rcmyw, wnrc, lbrc, rbrc;
828 * lParam points to a RECT struct. On entry, the struct
829 * contains the proposed wnd rectangle for the window.
830 * On exit, the struct should contain the screen
831 * coordinates of the corresponding window's client area.
834 DefWindowProcA (hwnd, WM_NCCALCSIZE, wParam, lParam);
836 if (PAGER_IsHorizontal(hwnd))
838 infoPtr->nWidth = lpRect->right - lpRect->left;
839 PAGER_CalcSize (hwnd, &infoPtr->nWidth, TRUE);
840 GetWindowRect (infoPtr->hwndChild, &rcChildw);
841 MapWindowPoints (0, hwnd, (LPPOINT)&rcChildw, 2);
842 GetCursorPos (&cursor);
843 GetWindowRect (hwnd, &rcmyw);
844 if (PtInRect (&rcmyw, cursor)) {
845 GetWindowRect (hwnd, &wnrc);
847 lbrc.right = lbrc.left + infoPtr->nButtonSize;
849 rbrc.left = rbrc.right - infoPtr->nButtonSize;
850 TRACE("horz lb rect=(%d,%d)-(%d,%d), rb rect=(%d,%d)-(%d,%d)\n",
851 lbrc.left, lbrc.top, lbrc.right, lbrc.bottom,
852 rbrc.left, rbrc.top, rbrc.right, rbrc.bottom);
853 if (PtInRect (&lbrc, cursor) && infoPtr->TLbtnState)
854 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
855 if (PtInRect (&rbrc, cursor) && infoPtr->BRbtnState)
856 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
858 if (infoPtr->TLbtnState) /* != PGF_INVISIBLE */
859 lpRect->left += infoPtr->nButtonSize;
860 if (infoPtr->BRbtnState)
861 lpRect->right -= infoPtr->nButtonSize;
865 /* native does: (from trace of IE4 opening "Favorites" frame)
867 * WM_NOITFY PGN_CALCSIZE w/ dwFlag=2
868 * GetWindowRect (child, &rc)
869 * MapWindowPoints (0, syspager, &rc, 2)
870 * GetCursorPos( &cur )
871 * GetWindowRect (syspager, &rc2)
872 * PtInRect (&rc2, cur.x, cur.y) rtns 0
873 * returns with rect empty
875 infoPtr->nHeight = lpRect->bottom - lpRect->top;
876 PAGER_CalcSize (hwnd, &infoPtr->nHeight, FALSE);
877 GetWindowRect (infoPtr->hwndChild, &rcChildw);
878 MapWindowPoints (0, hwnd, (LPPOINT)&rcChildw, 2);
879 GetCursorPos (&cursor);
880 GetWindowRect (hwnd, &rcmyw);
881 if (PtInRect (&rcmyw, cursor)) {
884 * GetWindowRect(pager, &rc)
885 * PtInRect(btn-left????, cur.x, cur.y)
887 * PtInRect(btn-right????, cur.x, cur.y)
889 * RedrawWindow(pager, 0, 0, 5)
893 GetWindowRect (hwnd, &wnrc);
895 lbrc.right = lbrc.left + infoPtr->nButtonSize;
897 rbrc.left = rbrc.right - infoPtr->nButtonSize;
898 TRACE("vert lb rect=(%d,%d)-(%d,%d), rb rect=(%d,%d)-(%d,%d)\n",
899 lbrc.left, lbrc.top, lbrc.right, lbrc.bottom,
900 rbrc.left, rbrc.top, rbrc.right, rbrc.bottom);
901 if (PtInRect (&lbrc, cursor) && infoPtr->TLbtnState)
902 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
903 if (PtInRect (&rbrc, cursor) && infoPtr->BRbtnState)
904 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
906 if (infoPtr->TLbtnState)
907 lpRect->top += infoPtr->nButtonSize;
908 if (infoPtr->BRbtnState)
909 lpRect->bottom -= infoPtr->nButtonSize;
911 if ((lpRect->bottom < 0) || (lpRect->bottom > infoPtr->nHeight))
912 lpRect->bottom = infoPtr->nHeight;
915 TRACE("[%04x] client rect set to %dx%d at (%d,%d)\n", hwnd,
916 lpRect->right-lpRect->left,
917 lpRect->bottom-lpRect->top,
918 lpRect->left, lpRect->top);
924 PAGER_NCPaint (HWND hwnd, WPARAM wParam, LPARAM lParam)
926 PAGER_INFO* infoPtr = PAGER_GetInfoPtr(hwnd);
927 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
928 RECT rcWindow, rcBottomRight, rcTopLeft;
930 BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
932 if (dwStyle & WS_MINIMIZE)
935 DefWindowProcA (hwnd, WM_NCPAINT, wParam, lParam);
937 if (!(hdc = GetDCEx (hwnd, 0, DCX_USESTYLE | DCX_WINDOW)))
940 GetWindowRect (hwnd, &rcWindow);
941 OffsetRect (&rcWindow, -rcWindow.left, -rcWindow.top);
943 rcTopLeft = rcBottomRight = rcWindow;
946 rcTopLeft.right = rcTopLeft.left + infoPtr->nButtonSize;
947 rcBottomRight.left = rcBottomRight.right - infoPtr->nButtonSize;
951 rcTopLeft.bottom = rcTopLeft.top + infoPtr->nButtonSize;
952 rcBottomRight.top = rcBottomRight.bottom - infoPtr->nButtonSize;
955 PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft,
956 bHorizontal, TRUE, infoPtr->TLbtnState);
957 PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight,
958 bHorizontal, FALSE, infoPtr->BRbtnState);
960 ReleaseDC( hwnd, hdc );
965 PAGER_HitTest (HWND hwnd, LPPOINT pt)
967 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
969 BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
971 GetClientRect (hwnd, &clientRect);
973 if (PtInRect(&clientRect, *pt))
975 /* TRACE("HTCLIENT\n"); */
979 if (infoPtr->TLbtnState && infoPtr->TLbtnState != PGF_GRAYED)
983 if (pt->x < clientRect.left)
985 /* TRACE("HTLEFT\n"); */
991 if (pt->y < clientRect.top)
993 /* TRACE("HTTOP\n"); */
999 if (infoPtr->BRbtnState && infoPtr->BRbtnState != PGF_GRAYED)
1003 if (pt->x > clientRect.right)
1005 /* TRACE("HTRIGHT\n"); */
1011 if (pt->y > clientRect.bottom)
1013 /* TRACE("HTBOTTOM\n"); */
1019 /* TRACE("HTNOWHERE\n"); */
1024 PAGER_NCHitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
1026 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
1027 ScreenToClient (hwnd, &pt);
1028 return PAGER_HitTest(hwnd, &pt);
1032 PAGER_SetCursor( HWND hwnd, WPARAM wParam, LPARAM lParam )
1034 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1035 BOOL notCaptured = FALSE;
1037 switch(LOWORD(lParam))
1041 if ((notCaptured = infoPtr->TLbtnState != PGF_HOT))
1042 infoPtr->TLbtnState = PGF_HOT;
1046 if ((notCaptured = infoPtr->BRbtnState != PGF_HOT))
1047 infoPtr->BRbtnState = PGF_HOT;
1055 TRACKMOUSEEVENT trackinfo;
1057 TRACE("[%04x] SetCapture\n", hwnd);
1060 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
1061 trackinfo.dwFlags = TME_QUERY;
1062 trackinfo.hwndTrack = hwnd;
1063 trackinfo.dwHoverTime = HOVER_DEFAULT;
1065 /* call _TrackMouseEvent to see if we are currently tracking for this hwnd */
1066 _TrackMouseEvent(&trackinfo);
1068 /* Make sure tracking is enabled so we receive a WM_MOUSELEAVE message */
1069 if(!(trackinfo.dwFlags & TME_LEAVE)) {
1070 trackinfo.dwFlags = TME_LEAVE; /* notify upon leaving */
1072 /* call TRACKMOUSEEVENT so we receive a WM_MOUSELEAVE message */
1073 /* and can properly deactivate the hot button */
1074 _TrackMouseEvent(&trackinfo);
1077 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
1084 PAGER_MouseLeave (HWND hwnd, WPARAM wParam, LPARAM lParam)
1086 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1088 KillTimer (hwnd, TIMERID1);
1089 KillTimer (hwnd, TIMERID2);
1091 TRACE("[%04x] ReleaseCapture\n", hwnd);
1094 /* Notify parent of released mouse capture */
1097 ZeroMemory (&nmhdr, sizeof (NMHDR));
1098 nmhdr.hwndFrom = hwnd;
1099 nmhdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
1100 nmhdr.code = NM_RELEASEDCAPTURE;
1101 SendMessageA (GetParent(hwnd), WM_NOTIFY,
1102 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1105 /* make HOT btns NORMAL and hide gray btns */
1106 PAGER_UpdateBtns(hwnd, infoPtr, -1, TRUE);
1112 PAGER_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
1114 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1115 BOOL repaintBtns = FALSE;
1116 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
1119 TRACE("[%04x]\n", hwnd);
1121 hit = PAGER_HitTest(hwnd, &pt);
1123 /* put btn in DEPRESSED state */
1124 if (hit == HTLEFT || hit == HTTOP)
1126 repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED;
1127 infoPtr->TLbtnState = PGF_DEPRESSED;
1128 SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0);
1130 else if (hit == HTRIGHT || hit == HTBOTTOM)
1132 repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED;
1133 infoPtr->BRbtnState = PGF_DEPRESSED;
1134 SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0);
1138 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
1143 TRACE("[%04x] PGF_SCROLLLEFT\n", hwnd);
1144 PAGER_Scroll(hwnd, PGF_SCROLLLEFT);
1147 TRACE("[%04x] PGF_SCROLLUP\n", hwnd);
1148 PAGER_Scroll(hwnd, PGF_SCROLLUP);
1151 TRACE("[%04x] PGF_SCROLLRIGHT\n", hwnd);
1152 PAGER_Scroll(hwnd, PGF_SCROLLRIGHT);
1155 TRACE("[%04x] PGF_SCROLLDOWN\n", hwnd);
1156 PAGER_Scroll(hwnd, PGF_SCROLLDOWN);
1166 PAGER_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
1168 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1169 TRACE("[%04x]\n", hwnd);
1171 KillTimer (hwnd, TIMERID1);
1172 KillTimer (hwnd, TIMERID2);
1174 /* make PRESSED btns NORMAL but don't hide gray btns */
1175 PAGER_UpdateBtns(hwnd, infoPtr, -1, FALSE);
1181 PAGER_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
1184 HDC hdc = (HDC)wParam;
1188 * parent = GetParent(pager)
1189 * pt.x=0; pt.y=0; ?????
1190 * MapWindowPoints(pager, parent, &pt, 1)
1191 * OffsetWindowOrgEx(hdc, pt.x, pt.y, &ptorg)
1192 * SendMessageA(parent, WM_ERASEBKGND, hdc, 0)
1193 * SetWindowOrgEx(hdc, 0, 0, 0)
1198 parent = GetParent(hwnd);
1199 MapWindowPoints(hwnd, parent, &pt, 1);
1200 OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig);
1201 SendMessageA (parent, WM_ERASEBKGND, wParam, lParam);
1202 SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0);
1206 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1207 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
1210 GetClientRect (hwnd, &rect);
1211 FillRect ((HDC)wParam, &rect, hBrush);
1213 /* background color of the child should be the same as the pager */
1214 if (infoPtr->hwndChild)
1216 GetClientRect (infoPtr->hwndChild, &rect);
1217 FillRect ((HDC)wParam, &rect, hBrush);
1220 DeleteObject (hBrush);
1228 PAGER_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1230 /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */
1232 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1233 TRACE("[%04x] %dx%d\n", hwnd, LOWORD(lParam), HIWORD(lParam));
1235 if (PAGER_IsHorizontal(hwnd))
1236 infoPtr->nHeight = HIWORD(lParam);
1238 infoPtr->nWidth = LOWORD(lParam);
1240 return PAGER_RecalcSize(hwnd);
1244 static LRESULT WINAPI
1245 PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1247 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1249 if (!infoPtr && (uMsg != WM_CREATE))
1250 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1255 return PAGER_FmtLines(hwnd);
1257 case PGM_FORWARDMOUSE:
1258 return PAGER_ForwardMouse (hwnd, wParam);
1260 case PGM_GETBKCOLOR:
1261 return PAGER_GetBkColor(hwnd);
1264 return PAGER_GetBorder(hwnd);
1266 case PGM_GETBUTTONSIZE:
1267 return PAGER_GetButtonSize(hwnd);
1270 return PAGER_GetPos(hwnd);
1272 case PGM_GETBUTTONSTATE:
1273 return PAGER_GetButtonState (hwnd, wParam, lParam);
1275 /* case PGM_GETDROPTARGET: */
1277 case PGM_RECALCSIZE:
1278 return PAGER_RecalcSize(hwnd);
1280 case PGM_SETBKCOLOR:
1281 return PAGER_SetBkColor (hwnd, wParam, lParam);
1284 return PAGER_SetBorder (hwnd, wParam, lParam);
1286 case PGM_SETBUTTONSIZE:
1287 return PAGER_SetButtonSize (hwnd, wParam, lParam);
1290 return PAGER_SetChild (hwnd, wParam, lParam);
1293 return PAGER_SetPos(hwnd, (INT)lParam, FALSE);
1296 return PAGER_Create (hwnd, wParam, lParam);
1299 return PAGER_Destroy (hwnd, wParam, lParam);
1302 return PAGER_Size (hwnd, wParam, lParam);
1305 return PAGER_NCPaint (hwnd, wParam, lParam);
1307 case WM_WINDOWPOSCHANGING:
1308 return PAGER_HandleWindowPosChanging (hwnd, wParam, (WINDOWPOS*)lParam);
1311 return PAGER_NCCalcSize (hwnd, wParam, lParam);
1314 return PAGER_NCHitTest (hwnd, wParam, lParam);
1318 if (hwnd == (HWND)wParam)
1319 return PAGER_SetCursor(hwnd, wParam, lParam);
1320 else /* its for the child */
1325 if (infoPtr->bForward && infoPtr->hwndChild)
1326 PostMessageA(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam);
1330 return PAGER_MouseLeave (hwnd, wParam, lParam);
1332 case WM_LBUTTONDOWN:
1333 return PAGER_LButtonDown (hwnd, wParam, lParam);
1336 return PAGER_LButtonUp (hwnd, wParam, lParam);
1339 return PAGER_EraseBackground (hwnd, wParam, lParam);
1342 return PAGER_Paint (hwnd, wParam);
1345 /* if initial timer, kill it and start the repeat timer */
1346 if (wParam == TIMERID1)
1348 KillTimer(hwnd, TIMERID1);
1349 SetTimer(hwnd, TIMERID2, REPEAT_DELAY, 0);
1352 KillTimer(hwnd, TIMERID2);
1353 if (infoPtr->direction > 0)
1355 PAGER_Scroll(hwnd, infoPtr->direction);
1356 SetTimer(hwnd, TIMERID2, REPEAT_DELAY, 0);
1362 return SendMessageA (GetParent (hwnd), uMsg, wParam, lParam);
1365 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1373 PAGER_Register (void)
1377 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1378 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS;
1379 wndClass.lpfnWndProc = (WNDPROC)PAGER_WindowProc;
1380 wndClass.cbClsExtra = 0;
1381 wndClass.cbWndExtra = sizeof(PAGER_INFO *);
1382 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
1383 wndClass.hbrBackground = 0;
1384 wndClass.lpszClassName = WC_PAGESCROLLERA;
1386 RegisterClassA (&wndClass);
1391 PAGER_Unregister (void)
1393 UnregisterClassA (WC_PAGESCROLLERA, (HINSTANCE)NULL);