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
21 * Tested primarily with the controlspy Pager application.
22 * Susan Farley (susan@codeweavers.com)
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.
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(pager);
40 HWND hwndChild; /* handle of the contained wnd */
41 BOOL bNoResize; /* set when created with CCS_NORESIZE */
42 COLORREF clrBk; /* background color */
43 INT nBorder; /* border size for the control */
44 INT nButtonSize;/* size of the pager btns */
45 INT nPos; /* scroll position */
46 INT nWidth; /* from child wnd's response to PGN_CALCSIZE */
47 INT nHeight; /* from child wnd's response to PGN_CALCSIZE */
48 BOOL bForward; /* forward WM_MOUSEMOVE msgs to the contained wnd */
49 INT TLbtnState; /* state of top or left btn */
50 INT BRbtnState; /* state of bottom or right btn */
51 INT direction; /* direction of the scroll, (e.g. PGF_SCROLLUP) */
54 #define PAGER_GetInfoPtr(hwnd) ((PAGER_INFO *)GetWindowLongA(hwnd, 0))
55 #define PAGER_IsHorizontal(hwnd) ((GetWindowLongA (hwnd, GWL_STYLE) & PGS_HORZ))
57 #define MIN_ARROW_WIDTH 8
58 #define MIN_ARROW_HEIGHT 5
62 #define INITIAL_DELAY 500
63 #define REPEAT_DELAY 50
65 /* the horizontal arrows are:
78 PAGER_DrawHorzArrow (HDC hdc, RECT r, INT colorRef, BOOL left)
83 w = r.right - r.left + 1;
84 h = r.bottom - r.top + 1;
85 if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
86 return; /* refuse to draw partial arrow */
88 hOldPen = SelectObject ( hdc, GetSysColorPen (colorRef));
91 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 3;
92 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
93 MoveToEx (hdc, x, y, NULL);
94 LineTo (hdc, x--, y+5); y++;
95 MoveToEx (hdc, x, y, NULL);
96 LineTo (hdc, x--, y+3); y++;
97 MoveToEx (hdc, x, y, NULL);
102 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
103 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
104 MoveToEx (hdc, x, y, NULL);
105 LineTo (hdc, x++, y+5); y++;
106 MoveToEx (hdc, x, y, NULL);
107 LineTo (hdc, x++, y+3); y++;
108 MoveToEx (hdc, x, y, NULL);
109 LineTo (hdc, x, y+1);
112 SelectObject( hdc, hOldPen );
115 /* the vertical arrows are:
125 PAGER_DrawVertArrow (HDC hdc, RECT r, INT colorRef, BOOL up)
130 w = r.right - r.left + 1;
131 h = r.bottom - r.top + 1;
132 if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT))
133 return; /* refuse to draw partial arrow */
135 hOldPen = SelectObject ( hdc, GetSysColorPen (colorRef));
138 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
139 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 3;
140 MoveToEx (hdc, x, y, NULL);
141 LineTo (hdc, x+5, y--); x++;
142 MoveToEx (hdc, x, y, NULL);
143 LineTo (hdc, x+3, y--); x++;
144 MoveToEx (hdc, x, y, NULL);
145 LineTo (hdc, x+1, y);
149 x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1;
150 y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1;
151 MoveToEx (hdc, x, y, NULL);
152 LineTo (hdc, x+5, y++); x++;
153 MoveToEx (hdc, x, y, NULL);
154 LineTo (hdc, x+3, y++); x++;
155 MoveToEx (hdc, x, y, NULL);
156 LineTo (hdc, x+1, y);
159 SelectObject( hdc, hOldPen );
163 PAGER_DrawButton(HDC hdc, COLORREF clrBk, RECT arrowRect,
164 BOOL horz, BOOL topLeft, INT btnState)
166 HBRUSH hBrush, hOldBrush;
169 if (!btnState) /* PGF_INVISIBLE */
172 if ((rc.right - rc.left <= 0) || (rc.bottom - rc.top <= 0))
175 hBrush = CreateSolidBrush(clrBk);
176 hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
178 FillRect(hdc, &rc, hBrush);
180 if (btnState == PGF_HOT)
182 DrawEdge( hdc, &rc, BDR_RAISEDINNER, BF_RECT);
184 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
186 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
188 else if (btnState == PGF_NORMAL)
190 DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
192 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
194 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
196 else if (btnState == PGF_DEPRESSED)
198 DrawEdge( hdc, &rc, BDR_SUNKENOUTER, BF_RECT);
200 PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
202 PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft);
204 else if (btnState == PGF_GRAYED)
206 DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT);
209 PAGER_DrawHorzArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
210 rc.left++, rc.top++; rc.right++, rc.bottom++;
211 PAGER_DrawHorzArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
215 PAGER_DrawVertArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft);
216 rc.left++, rc.top++; rc.right++, rc.bottom++;
217 PAGER_DrawVertArrow(hdc, rc, COLOR_3DSHADOW, topLeft);
221 SelectObject( hdc, hOldBrush );
222 DeleteObject(hBrush);
225 /* << PAGER_GetDropTarget >> */
227 static inline LRESULT
228 PAGER_ForwardMouse (HWND hwnd, WPARAM wParam)
230 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
231 TRACE("[%04x]\n", hwnd);
233 infoPtr->bForward = (BOOL)wParam;
238 static inline LRESULT
239 PAGER_GetButtonState (HWND hwnd, WPARAM wParam, LPARAM lParam)
241 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
242 LRESULT btnState = PGF_INVISIBLE;
243 INT btn = (INT)lParam;
244 TRACE("[%04x]\n", hwnd);
246 if (btn == PGB_TOPORLEFT)
247 btnState = infoPtr->TLbtnState;
248 else if (btn == PGB_BOTTOMORRIGHT)
249 btnState = infoPtr->BRbtnState;
255 static inline LRESULT
256 PAGER_GetPos(HWND hwnd)
258 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
259 TRACE("[%04x] returns %d\n", hwnd, infoPtr->nPos);
260 return (LRESULT)infoPtr->nPos;
263 static inline LRESULT
264 PAGER_GetButtonSize(HWND hwnd)
266 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
267 TRACE("[%04x] returns %d\n", hwnd, infoPtr->nButtonSize);
268 return (LRESULT)infoPtr->nButtonSize;
271 static inline LRESULT
272 PAGER_GetBorder(HWND hwnd)
274 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
275 TRACE("[%04x] returns %d\n", hwnd, infoPtr->nBorder);
276 return (LRESULT)infoPtr->nBorder;
279 static inline LRESULT
280 PAGER_GetBkColor(HWND hwnd)
282 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
283 TRACE("[%04x] returns %06lx\n", hwnd, infoPtr->clrBk);
284 return (LRESULT)infoPtr->clrBk;
288 PAGER_CalcSize (HWND hwnd, INT* size, BOOL getWidth)
291 ZeroMemory (&nmpgcs, sizeof (NMPGCALCSIZE));
292 nmpgcs.hdr.hwndFrom = hwnd;
293 nmpgcs.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
294 nmpgcs.hdr.code = PGN_CALCSIZE;
295 nmpgcs.dwFlag = getWidth ? PGF_CALCWIDTH : PGF_CALCHEIGHT;
296 nmpgcs.iWidth = getWidth ? *size : 0;
297 nmpgcs.iHeight = getWidth ? 0 : *size;
298 SendMessageA (GetParent (hwnd), WM_NOTIFY,
299 (WPARAM)nmpgcs.hdr.idFrom, (LPARAM)&nmpgcs);
301 *size = getWidth ? nmpgcs.iWidth : nmpgcs.iHeight;
303 TRACE("[%04x] PGN_CALCSIZE returns %s=%d\n", hwnd,
304 getWidth ? "width" : "height", *size);
308 PAGER_PositionChildWnd(HWND hwnd, PAGER_INFO* infoPtr)
310 if (infoPtr->hwndChild)
313 int nPos = infoPtr->nPos;
315 /* compensate for a grayed btn, which will soon become invisible */
316 if (infoPtr->TLbtnState == PGF_GRAYED)
317 nPos += infoPtr->nButtonSize;
319 GetClientRect(hwnd, &rcClient);
321 if (PAGER_IsHorizontal(hwnd))
323 int wndSize = max(0, rcClient.right - rcClient.left);
324 if (infoPtr->nWidth < wndSize)
325 infoPtr->nWidth = 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,
337 int wndSize = max(0, rcClient.bottom - rcClient.top);
338 if (infoPtr->nHeight < wndSize)
339 infoPtr->nHeight = wndSize;
341 TRACE("[%04x] SWP %dx%d at (%d,%d)\n", hwnd,
342 infoPtr->nWidth, infoPtr->nHeight,
344 SetWindowPos(infoPtr->hwndChild, 0,
346 infoPtr->nWidth, infoPtr->nHeight,
350 InvalidateRect(infoPtr->hwndChild, NULL, TRUE);
355 PAGER_GetScrollRange(HWND hwnd, PAGER_INFO* infoPtr)
359 if (infoPtr->hwndChild)
361 INT wndSize, childSize;
363 GetWindowRect(hwnd, &wndRect);
365 if (PAGER_IsHorizontal(hwnd))
367 wndSize = wndRect.right - wndRect.left;
368 PAGER_CalcSize(hwnd, &infoPtr->nWidth, TRUE);
369 childSize = infoPtr->nWidth;
373 wndSize = wndRect.bottom - wndRect.top;
374 PAGER_CalcSize(hwnd, &infoPtr->nHeight, FALSE);
375 childSize = infoPtr->nHeight;
378 TRACE("childSize = %d, wndSize = %d\n", childSize, wndSize);
379 if (childSize > wndSize)
380 scrollRange = childSize - wndSize + infoPtr->nButtonSize;
383 TRACE("[%04x] returns %d\n", hwnd, scrollRange);
388 PAGER_GrayAndRestoreBtns(PAGER_INFO* infoPtr, INT scrollRange,
389 BOOL* needsResize, BOOL* needsRepaint)
391 if (infoPtr->nPos > 0)
393 *needsResize |= !infoPtr->TLbtnState; /* PGF_INVISIBLE */
394 if (infoPtr->TLbtnState != PGF_DEPRESSED)
395 infoPtr->TLbtnState = PGF_NORMAL;
399 *needsRepaint |= (infoPtr->TLbtnState != PGF_GRAYED);
400 infoPtr->TLbtnState = PGF_GRAYED;
403 if (scrollRange <= 0)
405 *needsRepaint |= (infoPtr->TLbtnState != PGF_GRAYED);
406 infoPtr->TLbtnState = PGF_GRAYED;
407 *needsRepaint |= (infoPtr->BRbtnState != PGF_GRAYED);
408 infoPtr->BRbtnState = PGF_GRAYED;
410 else if (infoPtr->nPos < scrollRange)
412 *needsResize |= !infoPtr->BRbtnState; /* PGF_INVISIBLE */
413 if (infoPtr->BRbtnState != PGF_DEPRESSED)
414 infoPtr->BRbtnState = PGF_NORMAL;
418 *needsRepaint |= (infoPtr->BRbtnState != PGF_GRAYED);
419 infoPtr->BRbtnState = PGF_GRAYED;
425 PAGER_NormalizeBtns(PAGER_INFO* infoPtr, BOOL* needsRepaint)
427 if (infoPtr->TLbtnState & (PGF_HOT | PGF_DEPRESSED))
429 infoPtr->TLbtnState = PGF_NORMAL;
430 *needsRepaint = TRUE;
433 if (infoPtr->BRbtnState & (PGF_HOT | PGF_DEPRESSED))
435 infoPtr->BRbtnState = PGF_NORMAL;
436 *needsRepaint = TRUE;
441 PAGER_HideGrayBtns(PAGER_INFO* infoPtr, BOOL* needsResize)
443 if (infoPtr->TLbtnState == PGF_GRAYED)
445 infoPtr->TLbtnState = PGF_INVISIBLE;
449 if (infoPtr->BRbtnState == PGF_GRAYED)
451 infoPtr->BRbtnState = PGF_INVISIBLE;
457 PAGER_UpdateBtns(HWND hwnd, PAGER_INFO *infoPtr,
458 INT scrollRange, BOOL hideGrayBtns)
460 BOOL resizeClient = FALSE;
461 BOOL repaintBtns = FALSE;
464 PAGER_NormalizeBtns(infoPtr, &repaintBtns);
466 PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
469 PAGER_HideGrayBtns(infoPtr, &resizeClient);
471 if (resizeClient) /* initiate NCCalcSize to resize client wnd */ {
472 SetWindowPos(hwnd, 0,0,0,0,0,
473 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
474 SWP_NOZORDER | SWP_NOACTIVATE);
478 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
482 PAGER_SetPos(HWND hwnd, INT newPos, BOOL fromBtnPress)
484 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
485 INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr);
486 INT oldPos = infoPtr->nPos;
488 if ((scrollRange <= 0) || (newPos < 0))
490 else if (newPos > scrollRange)
491 infoPtr->nPos = scrollRange;
493 infoPtr->nPos = newPos;
495 TRACE("[%04x] pos=%d\n", hwnd, infoPtr->nPos);
497 if (infoPtr->nPos != oldPos)
499 /* gray and restore btns, and if from WM_SETPOS, hide the gray btns */
500 PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, !fromBtnPress);
501 PAGER_PositionChildWnd(hwnd, infoPtr);
508 PAGER_HandleWindowPosChanging(HWND hwnd, WPARAM wParam, WINDOWPOS *winpos)
510 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
512 if (infoPtr->bNoResize && !(winpos->flags & SWP_NOSIZE))
514 /* don't let the app resize the nonscrollable dimension of a control
515 * that was created with CCS_NORESIZE style
516 * (i.e. height for a horizontal pager, or width for a vertical one) */
518 /* except if the current dimension is 0 and app is setting for
519 * first time, then save amount as dimension. - GA 8/01 */
521 if (PAGER_IsHorizontal(hwnd))
522 if (!infoPtr->nHeight && winpos->cy)
523 infoPtr->nHeight = winpos->cy;
525 winpos->cy = infoPtr->nHeight;
527 if (!infoPtr->nWidth && winpos->cx)
528 infoPtr->nWidth = winpos->cx;
530 winpos->cx = infoPtr->nWidth;
534 DefWindowProcA (hwnd, WM_WINDOWPOSCHANGING, wParam, (LPARAM)winpos);
540 PAGER_SetFixedWidth(HWND hwnd, PAGER_INFO* infoPtr)
542 /* Must set the non-scrollable dimension to be less than the full height/width
543 * so that NCCalcSize is called. The Msoft docs mention 3/4 factor for button
544 * size, and experimentation shows that affect is almost right. */
548 GetWindowRect(hwnd, &wndRect);
550 /* see what the app says for btn width */
551 PAGER_CalcSize(hwnd, &infoPtr->nWidth, TRUE);
553 if (infoPtr->bNoResize)
555 delta = wndRect.right - wndRect.left - infoPtr->nWidth;
556 if (delta > infoPtr->nButtonSize)
557 infoPtr->nWidth += 4 * infoPtr->nButtonSize / 3;
559 infoPtr->nWidth += infoPtr->nButtonSize / 3;
562 h = wndRect.bottom - wndRect.top + infoPtr->nButtonSize;
564 TRACE("[%04x] infoPtr->nWidth set to %d\n",
565 hwnd, infoPtr->nWidth);
571 PAGER_SetFixedHeight(HWND hwnd, PAGER_INFO* infoPtr)
573 /* Must set the non-scrollable dimension to be less than the full height/width
574 * so that NCCalcSize is called. The Msoft docs mention 3/4 factor for button
575 * size, and experimentation shows that affect is almost right. */
579 GetWindowRect(hwnd, &wndRect);
581 /* see what the app says for btn height */
582 PAGER_CalcSize(hwnd, &infoPtr->nHeight, FALSE);
584 if (infoPtr->bNoResize)
586 delta = wndRect.bottom - wndRect.top - infoPtr->nHeight;
587 if (delta > infoPtr->nButtonSize)
588 infoPtr->nHeight += infoPtr->nButtonSize;
590 infoPtr->nHeight += infoPtr->nButtonSize / 3;
593 w = wndRect.right - wndRect.left + infoPtr->nButtonSize;
595 TRACE("[%04x] infoPtr->nHeight set to %d\n",
596 hwnd, infoPtr->nHeight);
602 PAGER_RecalcSize(HWND hwnd)
604 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
606 TRACE("[%04x]\n", hwnd);
608 if (infoPtr->hwndChild)
610 INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr);
612 if (scrollRange <= 0)
615 PAGER_SetPos(hwnd, 0, FALSE);
619 PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, TRUE);
620 PAGER_PositionChildWnd(hwnd, infoPtr);
629 PAGER_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam)
631 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
632 COLORREF clrTemp = infoPtr->clrBk;
634 infoPtr->clrBk = (COLORREF)lParam;
635 TRACE("[%04x] %06lx\n", hwnd, infoPtr->clrBk);
637 PAGER_RecalcSize(hwnd);
638 SendMessageA(hwnd, WM_NCPAINT, (WPARAM)0, (LPARAM)0);
640 return (LRESULT)clrTemp;
645 PAGER_SetBorder (HWND hwnd, WPARAM wParam, LPARAM lParam)
647 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
648 INT nTemp = infoPtr->nBorder;
650 infoPtr->nBorder = (INT)lParam;
651 TRACE("[%04x] %d\n", hwnd, infoPtr->nBorder);
653 PAGER_RecalcSize(hwnd);
655 return (LRESULT)nTemp;
660 PAGER_SetButtonSize (HWND hwnd, WPARAM wParam, LPARAM lParam)
662 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
663 INT nTemp = infoPtr->nButtonSize;
665 infoPtr->nButtonSize = (INT)lParam;
666 TRACE("[%04x] %d\n", hwnd, infoPtr->nButtonSize);
668 PAGER_RecalcSize(hwnd);
670 return (LRESULT)nTemp;
675 PAGER_SetChild (HWND hwnd, WPARAM wParam, LPARAM lParam)
677 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
680 infoPtr->hwndChild = IsWindow ((HWND)lParam) ? (HWND)lParam : 0;
682 if (infoPtr->hwndChild)
684 TRACE("[%04x] hwndChild=%04x\n", hwnd, infoPtr->hwndChild);
686 if (PAGER_IsHorizontal(hwnd)) {
687 hw = PAGER_SetFixedHeight(hwnd, infoPtr);
688 /* adjust non-scrollable dimension to fit the child */
689 SetWindowPos(hwnd, 0, 0,0, hw, infoPtr->nHeight,
690 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
691 SWP_NOSIZE | SWP_NOACTIVATE);
694 hw = PAGER_SetFixedWidth(hwnd, infoPtr);
695 /* adjust non-scrollable dimension to fit the child */
696 SetWindowPos(hwnd, 0, 0,0, infoPtr->nWidth, hw,
697 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER |
698 SWP_NOSIZE | SWP_NOACTIVATE);
701 /* position child within the page scroller */
702 SetWindowPos(infoPtr->hwndChild, HWND_TOP,
704 SWP_SHOWWINDOW | SWP_NOSIZE); /* native is 0 */
707 PAGER_SetPos(hwnd, 0, FALSE);
714 PAGER_Scroll(HWND hwnd, INT dir)
716 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
717 NMPGSCROLL nmpgScroll;
720 if (infoPtr->hwndChild)
722 ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL));
723 nmpgScroll.hdr.hwndFrom = hwnd;
724 nmpgScroll.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
725 nmpgScroll.hdr.code = PGN_SCROLL;
727 GetWindowRect(hwnd, &rcWnd);
728 GetClientRect(hwnd, &nmpgScroll.rcParent);
729 nmpgScroll.iXpos = nmpgScroll.iYpos = 0;
730 nmpgScroll.iDir = dir;
732 if (PAGER_IsHorizontal(hwnd))
734 nmpgScroll.iScroll = rcWnd.right - rcWnd.left;
735 nmpgScroll.iXpos = infoPtr->nPos;
739 nmpgScroll.iScroll = rcWnd.bottom - rcWnd.top;
740 nmpgScroll.iYpos = infoPtr->nPos;
742 nmpgScroll.iScroll -= 2*infoPtr->nButtonSize;
744 SendMessageA (hwnd, WM_NOTIFY,
745 (WPARAM)nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll);
747 TRACE("[%04x] PGN_SCROLL returns iScroll=%d\n", hwnd, nmpgScroll.iScroll);
749 if (nmpgScroll.iScroll > 0)
751 infoPtr->direction = dir;
753 if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP)
754 PAGER_SetPos(hwnd, infoPtr->nPos - nmpgScroll.iScroll, TRUE);
756 PAGER_SetPos(hwnd, infoPtr->nPos + nmpgScroll.iScroll, TRUE);
759 infoPtr->direction = -1;
764 PAGER_FmtLines(HWND hwnd)
766 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
768 /* initiate NCCalcSize to resize client wnd and get size */
769 SetWindowPos(hwnd, 0, 0,0,0,0,
770 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
771 SWP_NOZORDER | SWP_NOACTIVATE);
773 SetWindowPos(infoPtr->hwndChild, 0,
774 0,0,infoPtr->nWidth,infoPtr->nHeight,
777 return DefWindowProcA (hwnd, EM_FMTLINES, 0, 0);
781 PAGER_Create (HWND hwnd, WPARAM wParam, LPARAM lParam)
784 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
786 /* allocate memory for info structure */
787 infoPtr = (PAGER_INFO *)COMCTL32_Alloc (sizeof(PAGER_INFO));
788 SetWindowLongA (hwnd, 0, (DWORD)infoPtr);
790 /* set default settings */
791 infoPtr->hwndChild = (HWND)NULL;
792 infoPtr->bNoResize = dwStyle & CCS_NORESIZE;
793 infoPtr->clrBk = GetSysColor(COLOR_BTNFACE);
794 infoPtr->nBorder = 0;
795 infoPtr->nButtonSize = 12;
798 infoPtr->nHeight = 0;
799 infoPtr->bForward = FALSE;
800 infoPtr->TLbtnState = PGF_INVISIBLE;
801 infoPtr->BRbtnState = PGF_INVISIBLE;
802 infoPtr->direction = -1;
804 if (dwStyle & PGS_AUTOSCROLL)
805 FIXME("[%04x] Autoscroll style is not implemented yet.\n", hwnd);
806 if (dwStyle & PGS_DRAGNDROP)
807 FIXME("[%04x] Drag and Drop style is not implemented yet.\n", hwnd);
809 * If neither horizontal nor vertical style specified, default to vertical.
810 * This is probably not necessary, since the style may be set later on as
811 * the control is initialized, but just in case it isn't, set it here.
813 if (!(dwStyle & PGS_HORZ) && !(dwStyle & PGS_VERT))
816 SetWindowLongA(hwnd, GWL_STYLE, dwStyle);
824 PAGER_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam)
826 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
827 /* free pager info data */
828 COMCTL32_Free (infoPtr);
829 SetWindowLongA (hwnd, 0, 0);
834 PAGER_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam)
836 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
837 LPRECT lpRect = (LPRECT)lParam;
838 RECT rcChildw, rcmyw, wnrc, lbrc, rbrc;
840 BOOL resizeClient = FALSE;
841 BOOL repaintBtns = FALSE;
845 * lParam points to a RECT struct. On entry, the struct
846 * contains the proposed wnd rectangle for the window.
847 * On exit, the struct should contain the screen
848 * coordinates of the corresponding window's client area.
851 DefWindowProcA (hwnd, WM_NCCALCSIZE, wParam, lParam);
853 if (PAGER_IsHorizontal(hwnd))
855 infoPtr->nWidth = lpRect->right - lpRect->left;
856 PAGER_CalcSize (hwnd, &infoPtr->nWidth, TRUE);
857 GetWindowRect (infoPtr->hwndChild, &rcChildw);
858 MapWindowPoints (0, hwnd, (LPPOINT)&rcChildw, 2);
859 GetCursorPos (&cursor);
860 GetWindowRect (hwnd, &rcmyw);
862 /* Reset buttons and hide any grey ones */
863 scrollRange = infoPtr->nWidth - (rcmyw.right - rcmyw.bottom);
865 TRACE("nPos=%d, scrollrange=%d, nWidth=%d, myw=(%d,%d)-(%d,%d)\n",
866 infoPtr->nPos, scrollRange, infoPtr->nWidth,
867 rcmyw.left, rcmyw.top, rcmyw.right, rcmyw.bottom);
868 PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
869 PAGER_HideGrayBtns(infoPtr, &resizeClient);
871 if (PtInRect (&rcmyw, cursor)) {
872 GetWindowRect (hwnd, &wnrc);
874 lbrc.right = lbrc.left + infoPtr->nButtonSize;
876 rbrc.left = rbrc.right - infoPtr->nButtonSize;
877 TRACE("horz lb rect=(%d,%d)-(%d,%d), rb rect=(%d,%d)-(%d,%d)\n",
878 lbrc.left, lbrc.top, lbrc.right, lbrc.bottom,
879 rbrc.left, rbrc.top, rbrc.right, rbrc.bottom);
880 if (PtInRect (&lbrc, cursor) && infoPtr->TLbtnState)
881 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
882 if (PtInRect (&rbrc, cursor) && infoPtr->BRbtnState)
883 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
885 if (infoPtr->TLbtnState) /* != PGF_INVISIBLE */
886 lpRect->left += infoPtr->nButtonSize;
887 if (infoPtr->BRbtnState)
888 lpRect->right -= infoPtr->nButtonSize;
892 /* native does: (from trace of IE4 opening "Favorites" frame)
894 * WM_NOITFY PGN_CALCSIZE w/ dwFlag=2
895 * GetWindowRect (child, &rc)
896 * MapWindowPoints (0, syspager, &rc, 2)
897 * GetCursorPos( &cur )
898 * GetWindowRect (syspager, &rc2)
899 * PtInRect (&rc2, cur.x, cur.y) rtns 0
900 * returns with rect empty
902 infoPtr->nHeight = lpRect->bottom - lpRect->top;
903 PAGER_CalcSize (hwnd, &infoPtr->nHeight, FALSE);
904 GetWindowRect (infoPtr->hwndChild, &rcChildw);
905 MapWindowPoints (0, hwnd, (LPPOINT)&rcChildw, 2);
906 GetCursorPos (&cursor);
907 GetWindowRect (hwnd, &rcmyw);
909 /* Reset buttons and hide any grey ones */
910 scrollRange = infoPtr->nHeight - (rcmyw.bottom - rcmyw.top);
912 TRACE("nPos=%d, scrollrange=%d, nHeigth=%d, myw=(%d,%d)-(%d,%d)\n",
913 infoPtr->nPos, scrollRange, infoPtr->nHeight,
914 rcmyw.left, rcmyw.top,
915 rcmyw.right, rcmyw.bottom);
916 PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns);
917 PAGER_HideGrayBtns(infoPtr, &resizeClient);
919 if (PtInRect (&rcmyw, cursor)) {
922 * GetWindowRect(pager, &rc)
923 * PtInRect(btn-left????, cur.x, cur.y)
925 * PtInRect(btn-right????, cur.x, cur.y)
927 * RedrawWindow(pager, 0, 0, 5)
931 GetWindowRect (hwnd, &wnrc);
933 lbrc.right = lbrc.left + infoPtr->nButtonSize;
935 rbrc.left = rbrc.right - infoPtr->nButtonSize;
936 TRACE("vert lb rect=(%d,%d)-(%d,%d), rb rect=(%d,%d)-(%d,%d)\n",
937 lbrc.left, lbrc.top, lbrc.right, lbrc.bottom,
938 rbrc.left, rbrc.top, rbrc.right, rbrc.bottom);
939 if (PtInRect (&lbrc, cursor) && infoPtr->TLbtnState)
940 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
941 if (PtInRect (&rbrc, cursor) && infoPtr->BRbtnState)
942 RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE);
944 if (infoPtr->TLbtnState)
945 lpRect->top += infoPtr->nButtonSize;
946 if (infoPtr->BRbtnState)
947 lpRect->bottom -= infoPtr->nButtonSize;
949 if ((lpRect->bottom < 0) || (lpRect->bottom > infoPtr->nHeight))
950 lpRect->bottom = infoPtr->nHeight;
953 TRACE("[%04x] client rect set to %dx%d at (%d,%d) BtnState[%d,%d]\n",
954 hwnd, lpRect->right-lpRect->left, lpRect->bottom-lpRect->top,
955 lpRect->left, lpRect->top,
956 infoPtr->TLbtnState, infoPtr->BRbtnState);
962 PAGER_NCPaint (HWND hwnd, WPARAM wParam, LPARAM lParam)
964 PAGER_INFO* infoPtr = PAGER_GetInfoPtr(hwnd);
965 DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE);
966 RECT rcWindow, rcBottomRight, rcTopLeft;
968 BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
970 if (dwStyle & WS_MINIMIZE)
973 DefWindowProcA (hwnd, WM_NCPAINT, wParam, lParam);
975 if (!(hdc = GetDCEx (hwnd, 0, DCX_USESTYLE | DCX_WINDOW)))
978 GetWindowRect (hwnd, &rcWindow);
979 OffsetRect (&rcWindow, -rcWindow.left, -rcWindow.top);
981 rcTopLeft = rcBottomRight = rcWindow;
984 rcTopLeft.right = rcTopLeft.left + infoPtr->nButtonSize;
985 rcBottomRight.left = rcBottomRight.right - infoPtr->nButtonSize;
989 rcTopLeft.bottom = rcTopLeft.top + infoPtr->nButtonSize;
990 rcBottomRight.top = rcBottomRight.bottom - infoPtr->nButtonSize;
993 PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft,
994 bHorizontal, TRUE, infoPtr->TLbtnState);
995 PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight,
996 bHorizontal, FALSE, infoPtr->BRbtnState);
998 ReleaseDC( hwnd, hdc );
1003 PAGER_HitTest (HWND hwnd, LPPOINT pt)
1005 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1007 BOOL bHorizontal = PAGER_IsHorizontal(hwnd);
1009 GetClientRect (hwnd, &clientRect);
1011 if (PtInRect(&clientRect, *pt))
1013 /* TRACE("HTCLIENT\n"); */
1017 if (infoPtr->TLbtnState && infoPtr->TLbtnState != PGF_GRAYED)
1021 if (pt->x < clientRect.left)
1023 /* TRACE("HTLEFT\n"); */
1029 if (pt->y < clientRect.top)
1031 /* TRACE("HTTOP\n"); */
1037 if (infoPtr->BRbtnState && infoPtr->BRbtnState != PGF_GRAYED)
1041 if (pt->x > clientRect.right)
1043 /* TRACE("HTRIGHT\n"); */
1049 if (pt->y > clientRect.bottom)
1051 /* TRACE("HTBOTTOM\n"); */
1057 /* TRACE("HTNOWHERE\n"); */
1062 PAGER_NCHitTest (HWND hwnd, WPARAM wParam, LPARAM lParam)
1064 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
1065 ScreenToClient (hwnd, &pt);
1066 return PAGER_HitTest(hwnd, &pt);
1070 PAGER_SetCursor( HWND hwnd, WPARAM wParam, LPARAM lParam )
1072 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1073 BOOL notCaptured = FALSE;
1075 switch(LOWORD(lParam))
1079 if ((notCaptured = infoPtr->TLbtnState != PGF_HOT))
1080 infoPtr->TLbtnState = PGF_HOT;
1084 if ((notCaptured = infoPtr->BRbtnState != PGF_HOT))
1085 infoPtr->BRbtnState = PGF_HOT;
1093 TRACKMOUSEEVENT trackinfo;
1095 TRACE("[%04x] SetCapture\n", hwnd);
1098 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
1099 trackinfo.dwFlags = TME_QUERY;
1100 trackinfo.hwndTrack = hwnd;
1101 trackinfo.dwHoverTime = HOVER_DEFAULT;
1103 /* call _TrackMouseEvent to see if we are currently tracking for this hwnd */
1104 _TrackMouseEvent(&trackinfo);
1106 /* Make sure tracking is enabled so we receive a WM_MOUSELEAVE message */
1107 if(!(trackinfo.dwFlags & TME_LEAVE)) {
1108 trackinfo.dwFlags = TME_LEAVE; /* notify upon leaving */
1110 /* call TRACKMOUSEEVENT so we receive a WM_MOUSELEAVE message */
1111 /* and can properly deactivate the hot button */
1112 _TrackMouseEvent(&trackinfo);
1115 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
1122 PAGER_MouseLeave (HWND hwnd, WPARAM wParam, LPARAM lParam)
1124 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1126 KillTimer (hwnd, TIMERID1);
1127 KillTimer (hwnd, TIMERID2);
1129 TRACE("[%04x] ReleaseCapture\n", hwnd);
1132 /* Notify parent of released mouse capture */
1135 ZeroMemory (&nmhdr, sizeof (NMHDR));
1136 nmhdr.hwndFrom = hwnd;
1137 nmhdr.idFrom = GetWindowLongA (hwnd, GWL_ID);
1138 nmhdr.code = NM_RELEASEDCAPTURE;
1139 SendMessageA (GetParent(hwnd), WM_NOTIFY,
1140 (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr);
1143 /* make HOT btns NORMAL and hide gray btns */
1144 PAGER_UpdateBtns(hwnd, infoPtr, -1, TRUE);
1150 PAGER_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam)
1152 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1153 BOOL repaintBtns = FALSE;
1154 POINT pt = { SLOWORD(lParam), SHIWORD(lParam) };
1157 TRACE("[%04x]\n", hwnd);
1159 hit = PAGER_HitTest(hwnd, &pt);
1161 /* put btn in DEPRESSED state */
1162 if (hit == HTLEFT || hit == HTTOP)
1164 repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED;
1165 infoPtr->TLbtnState = PGF_DEPRESSED;
1166 SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0);
1168 else if (hit == HTRIGHT || hit == HTBOTTOM)
1170 repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED;
1171 infoPtr->BRbtnState = PGF_DEPRESSED;
1172 SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0);
1176 SendMessageA(hwnd, WM_NCPAINT, 0, 0);
1181 TRACE("[%04x] PGF_SCROLLLEFT\n", hwnd);
1182 PAGER_Scroll(hwnd, PGF_SCROLLLEFT);
1185 TRACE("[%04x] PGF_SCROLLUP\n", hwnd);
1186 PAGER_Scroll(hwnd, PGF_SCROLLUP);
1189 TRACE("[%04x] PGF_SCROLLRIGHT\n", hwnd);
1190 PAGER_Scroll(hwnd, PGF_SCROLLRIGHT);
1193 TRACE("[%04x] PGF_SCROLLDOWN\n", hwnd);
1194 PAGER_Scroll(hwnd, PGF_SCROLLDOWN);
1204 PAGER_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam)
1206 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1207 TRACE("[%04x]\n", hwnd);
1209 KillTimer (hwnd, TIMERID1);
1210 KillTimer (hwnd, TIMERID2);
1212 /* make PRESSED btns NORMAL but don't hide gray btns */
1213 PAGER_UpdateBtns(hwnd, infoPtr, -1, FALSE);
1219 PAGER_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam)
1222 HDC hdc = (HDC)wParam;
1226 * parent = GetParent(pager)
1227 * pt.x=0; pt.y=0; ?????
1228 * MapWindowPoints(pager, parent, &pt, 1)
1229 * OffsetWindowOrgEx(hdc, pt.x, pt.y, &ptorg)
1230 * SendMessageA(parent, WM_ERASEBKGND, hdc, 0)
1231 * SetWindowOrgEx(hdc, 0, 0, 0)
1236 parent = GetParent(hwnd);
1237 MapWindowPoints(hwnd, parent, &pt, 1);
1238 OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig);
1239 SendMessageA (parent, WM_ERASEBKGND, wParam, lParam);
1240 SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0);
1244 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1245 HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
1248 GetClientRect (hwnd, &rect);
1249 FillRect ((HDC)wParam, &rect, hBrush);
1251 /* background color of the child should be the same as the pager */
1252 if (infoPtr->hwndChild)
1254 GetClientRect (infoPtr->hwndChild, &rect);
1255 FillRect ((HDC)wParam, &rect, hBrush);
1258 DeleteObject (hBrush);
1266 PAGER_Size (HWND hwnd, WPARAM wParam, LPARAM lParam)
1268 /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */
1270 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1271 TRACE("[%04x] %dx%d\n", hwnd, LOWORD(lParam), HIWORD(lParam));
1273 if (PAGER_IsHorizontal(hwnd))
1274 infoPtr->nHeight = HIWORD(lParam);
1276 infoPtr->nWidth = LOWORD(lParam);
1278 return PAGER_RecalcSize(hwnd);
1282 static LRESULT WINAPI
1283 PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
1285 PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd);
1287 if (!infoPtr && (uMsg != WM_CREATE))
1288 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1293 return PAGER_FmtLines(hwnd);
1295 case PGM_FORWARDMOUSE:
1296 return PAGER_ForwardMouse (hwnd, wParam);
1298 case PGM_GETBKCOLOR:
1299 return PAGER_GetBkColor(hwnd);
1302 return PAGER_GetBorder(hwnd);
1304 case PGM_GETBUTTONSIZE:
1305 return PAGER_GetButtonSize(hwnd);
1308 return PAGER_GetPos(hwnd);
1310 case PGM_GETBUTTONSTATE:
1311 return PAGER_GetButtonState (hwnd, wParam, lParam);
1313 /* case PGM_GETDROPTARGET: */
1315 case PGM_RECALCSIZE:
1316 return PAGER_RecalcSize(hwnd);
1318 case PGM_SETBKCOLOR:
1319 return PAGER_SetBkColor (hwnd, wParam, lParam);
1322 return PAGER_SetBorder (hwnd, wParam, lParam);
1324 case PGM_SETBUTTONSIZE:
1325 return PAGER_SetButtonSize (hwnd, wParam, lParam);
1328 return PAGER_SetChild (hwnd, wParam, lParam);
1331 return PAGER_SetPos(hwnd, (INT)lParam, FALSE);
1334 return PAGER_Create (hwnd, wParam, lParam);
1337 return PAGER_Destroy (hwnd, wParam, lParam);
1340 return PAGER_Size (hwnd, wParam, lParam);
1343 return PAGER_NCPaint (hwnd, wParam, lParam);
1345 case WM_WINDOWPOSCHANGING:
1346 return PAGER_HandleWindowPosChanging (hwnd, wParam, (WINDOWPOS*)lParam);
1349 return PAGER_NCCalcSize (hwnd, wParam, lParam);
1352 return PAGER_NCHitTest (hwnd, wParam, lParam);
1356 if (hwnd == (HWND)wParam)
1357 return PAGER_SetCursor(hwnd, wParam, lParam);
1358 else /* its for the child */
1363 if (infoPtr->bForward && infoPtr->hwndChild)
1364 PostMessageA(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam);
1368 return PAGER_MouseLeave (hwnd, wParam, lParam);
1370 case WM_LBUTTONDOWN:
1371 return PAGER_LButtonDown (hwnd, wParam, lParam);
1374 return PAGER_LButtonUp (hwnd, wParam, lParam);
1377 return PAGER_EraseBackground (hwnd, wParam, lParam);
1380 return PAGER_Paint (hwnd, wParam);
1383 /* if initial timer, kill it and start the repeat timer */
1384 if (wParam == TIMERID1)
1386 KillTimer(hwnd, TIMERID1);
1387 SetTimer(hwnd, TIMERID2, REPEAT_DELAY, 0);
1390 KillTimer(hwnd, TIMERID2);
1391 if (infoPtr->direction > 0)
1393 PAGER_Scroll(hwnd, infoPtr->direction);
1394 SetTimer(hwnd, TIMERID2, REPEAT_DELAY, 0);
1400 return SendMessageA (GetParent (hwnd), uMsg, wParam, lParam);
1403 return DefWindowProcA (hwnd, uMsg, wParam, lParam);
1411 PAGER_Register (void)
1415 ZeroMemory (&wndClass, sizeof(WNDCLASSA));
1416 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS;
1417 wndClass.lpfnWndProc = (WNDPROC)PAGER_WindowProc;
1418 wndClass.cbClsExtra = 0;
1419 wndClass.cbWndExtra = sizeof(PAGER_INFO *);
1420 wndClass.hCursor = LoadCursorA (0, IDC_ARROWA);
1421 wndClass.hbrBackground = 0;
1422 wndClass.lpszClassName = WC_PAGESCROLLERA;
1424 RegisterClassA (&wndClass);
1429 PAGER_Unregister (void)
1431 UnregisterClassA (WC_PAGESCROLLERA, (HINSTANCE)NULL);