diff --git a/dlls/comctl32/pager.c b/dlls/comctl32/pager.c index 94d40862e4b..8f8bf3c2933 100644 --- a/dlls/comctl32/pager.c +++ b/dlls/comctl32/pager.c @@ -4,13 +4,14 @@ * Copyright 1998, 1999 Eric Kohl * * NOTES - * This is just a dummy control. An author is needed! Any volunteers? - * I will only improve this control once in a while. - * Eric + * Tested primarily with the controlspy Pager application. + * Susan Farley (susan@codeweavers.com) * * TODO: - * - All messages. - * - All notifications. + * Implement repetitive button press. + * Adjust arrow size relative to size of button. + * Allow border size changes. + * Implement drag and drop style. */ #include "winbase.h" @@ -21,199 +22,618 @@ DEFAULT_DEBUG_CHANNEL(pager); typedef struct { - HWND hwndChild; - COLORREF clrBk; - INT nBorder; - INT nButtonSize; - INT nPos; - BOOL bForward; - - INT nChildSize; + HWND hwndChild; /* handle of the contained wnd */ + BOOL bHorizontal;/* orientation of the control */ + COLORREF clrBk; /* background color */ + INT nBorder; /* border size for the control */ + INT nButtonSize;/* size of the pager btns */ + INT nPos; /* scroll position */ + INT nDelta; /* scroll delta */ + INT nWidth; /* from child wnd's response to PGN_CALCSIZE */ + INT nHeight; /* from child wnd's response to PGN_CALCSIZE */ + BOOL bForward; /* forward WM_MOUSEMOVE msgs to the contained wnd */ + INT TLbtnState; /* state of top or left btn */ + INT BRbtnState; /* state of bottom or right btn */ } PAGER_INFO; #define PAGER_GetInfoPtr(hwnd) ((PAGER_INFO *)GetWindowLongA(hwnd, 0)) +#define MIN_ARROW_WIDTH 8 +#define MIN_ARROW_HEIGHT 5 + +/* the horizontal arrows are: + * + * 01234 01234 + * 1 * * + * 2 ** ** + * 3*** *** + * 4*** *** + * 5 ** ** + * 6 * * + * 7 + * + */ +static void +PAGER_DrawHorzArrow (HDC hdc, RECT r, INT colorRef, BOOL left) +{ + INT x, y, w, h; + HPEN hOldPen; + + w = r.right - r.left + 1; + h = r.bottom - r.top + 1; + if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT)) + return; /* refuse to draw partial arrow */ + + hOldPen = SelectObject ( hdc, GetSysColorPen (colorRef)); + if (left) + { + x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 3; + y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1; + MoveToEx (hdc, x, y, NULL); + LineTo (hdc, x--, y+5); y++; + MoveToEx (hdc, x, y, NULL); + LineTo (hdc, x--, y+3); y++; + MoveToEx (hdc, x, y, NULL); + LineTo (hdc, x, y+1); + } + else + { + x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1; + y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1; + MoveToEx (hdc, x, y, NULL); + LineTo (hdc, x++, y+5); y++; + MoveToEx (hdc, x, y, NULL); + LineTo (hdc, x++, y+3); y++; + MoveToEx (hdc, x, y, NULL); + LineTo (hdc, x, y+1); + } + + SelectObject( hdc, hOldPen ); +} + +/* the vertical arrows are: + * + * 01234567 01234567 + * 1****** ** + * 2 **** **** + * 3 ** ****** + * 4 + * + */ +static void +PAGER_DrawVertArrow (HDC hdc, RECT r, INT colorRef, BOOL up) +{ + INT x, y, w, h; + HPEN hOldPen; + + w = r.right - r.left + 1; + h = r.bottom - r.top + 1; + if ((h < MIN_ARROW_WIDTH) || (w < MIN_ARROW_HEIGHT)) + return; /* refuse to draw partial arrow */ + + hOldPen = SelectObject ( hdc, GetSysColorPen (colorRef)); + if (up) + { + x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1; + y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 3; + MoveToEx (hdc, x, y, NULL); + LineTo (hdc, x+5, y--); x++; + MoveToEx (hdc, x, y, NULL); + LineTo (hdc, x+3, y--); x++; + MoveToEx (hdc, x, y, NULL); + LineTo (hdc, x+1, y); + } + else + { + x = r.left + ((w - MIN_ARROW_HEIGHT) / 2) + 1; + y = r.top + ((h - MIN_ARROW_WIDTH) / 2) + 1; + MoveToEx (hdc, x, y, NULL); + LineTo (hdc, x+5, y++); x++; + MoveToEx (hdc, x, y, NULL); + LineTo (hdc, x+3, y++); x++; + MoveToEx (hdc, x, y, NULL); + LineTo (hdc, x+1, y); + } + + SelectObject( hdc, hOldPen ); +} + +static void +PAGER_DrawButton(HDC hdc, COLORREF clrBk, RECT arrowRect, + BOOL horz, BOOL topLeft, INT btnState) +{ + HBRUSH hBrush, hOldBrush; + RECT rc = arrowRect; + + if (!btnState) /* PGF_INVISIBLE */ + return; + + if ((rc.right - rc.left <= 0) || (rc.bottom - rc.top <= 0)) + return; + + hBrush = CreateSolidBrush(clrBk); + hOldBrush = (HBRUSH)SelectObject(hdc, hBrush); + + FillRect(hdc, &rc, hBrush); + + if (btnState == PGF_HOT) + { + rc.left++, rc.top++; rc.right++, rc.bottom++; + DrawEdge( hdc, &rc, EDGE_RAISED, BF_RECT); + if (horz) + PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft); + else + PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft); + rc.left--, rc.top--; rc.right--, rc.bottom--; + } + else if (btnState == PGF_NORMAL) + { + DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT); + if (horz) + PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft); + else + PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft); + } + else if (btnState == PGF_DEPRESSED) + { + rc.left++, rc.top++; + DrawEdge( hdc, &rc, BDR_SUNKENOUTER, BF_RECT); + if (horz) + PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft); + else + PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft); + rc.left--, rc.top--; + } + else if (btnState == PGF_GRAYED) + { + DrawEdge (hdc, &rc, BDR_OUTER, BF_FLAT); + if (horz) + { + PAGER_DrawHorzArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft); + rc.left++, rc.top++; rc.right++, rc.bottom++; + PAGER_DrawHorzArrow(hdc, rc, COLOR_3DSHADOW, topLeft); + } + else + { + PAGER_DrawVertArrow(hdc, rc, COLOR_3DHIGHLIGHT, topLeft); + rc.left++, rc.top++; rc.right++, rc.bottom++; + PAGER_DrawVertArrow(hdc, rc, COLOR_3DSHADOW, topLeft); + } + rc.left--, rc.top--; rc.right--, rc.bottom--; + } + + SelectObject( hdc, hOldBrush ); + DeleteObject(hBrush); +} + +/* << PAGER_GetDropTarget >> */ static inline LRESULT PAGER_ForwardMouse (HWND hwnd, WPARAM wParam) { PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + TRACE("[%04x]\n", hwnd); infoPtr->bForward = (BOOL)wParam; return 0; } - static inline LRESULT -PAGER_GetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam) +PAGER_GetButtonState (HWND hwnd, WPARAM wParam, LPARAM lParam) { - PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + LRESULT btnState = PGF_INVISIBLE; + INT btn = (INT)lParam; + TRACE("[%04x]\n", hwnd); - return (LRESULT)infoPtr->clrBk; + if (btn == PGB_TOPORLEFT) + btnState = infoPtr->TLbtnState; + else if (btn == PGB_BOTTOMORRIGHT) + btnState = infoPtr->BRbtnState; + + return btnState; } static inline LRESULT -PAGER_GetBorder (HWND hwnd, WPARAM wParam, LPARAM lParam) +PAGER_GetPos(HWND hwnd) { - PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); - - return (LRESULT)infoPtr->nBorder; + PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + TRACE("[%04x] returns %d\n", hwnd, infoPtr->nPos); + return (LRESULT)infoPtr->nPos; } - static inline LRESULT -PAGER_GetButtonSize (HWND hwnd, WPARAM wParam, LPARAM lParam) +PAGER_GetButtonSize(HWND hwnd) { - PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); - + PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + TRACE("[%04x] returns %d\n", hwnd, infoPtr->nButtonSize); return (LRESULT)infoPtr->nButtonSize; } - -static LRESULT -PAGER_GetButtonState (HWND hwnd, WPARAM wParam, LPARAM lParam) +static inline LRESULT +PAGER_GetBorder(HWND hwnd) { - /* PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); */ - - FIXME("empty stub!\n"); - - return PGF_INVISIBLE; + PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + TRACE("[%04x] returns %d\n", hwnd, infoPtr->nBorder); + return (LRESULT)infoPtr->nBorder; } - -/* << PAGER_GetDropTarget >> */ - - static inline LRESULT -PAGER_GetPos (HWND hwnd, WPARAM wParam, LPARAM lParam) +PAGER_GetBkColor(HWND hwnd) { - PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + TRACE("[%04x] returns %06lx\n", hwnd, infoPtr->clrBk); + return (LRESULT)infoPtr->clrBk; +} - return infoPtr->nPos; +static void +PAGER_CalcSize (HWND hwnd, INT* size, BOOL getWidth) +{ + NMPGCALCSIZE nmpgcs; + ZeroMemory (&nmpgcs, sizeof (NMPGCALCSIZE)); + nmpgcs.hdr.hwndFrom = hwnd; + nmpgcs.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID); + nmpgcs.hdr.code = PGN_CALCSIZE; + nmpgcs.dwFlag = getWidth ? PGF_CALCWIDTH : PGF_CALCHEIGHT; + nmpgcs.iWidth = getWidth ? *size : 0; + nmpgcs.iHeight = getWidth ? 0 : *size; + SendMessageA (hwnd, WM_NOTIFY, + (WPARAM)nmpgcs.hdr.idFrom, (LPARAM)&nmpgcs); + + *size = getWidth ? nmpgcs.iWidth : nmpgcs.iHeight; + + TRACE("[%04x] PGN_CALCSIZE returns %s=%d\n", hwnd, + getWidth ? "width" : "height", *size); +} + +static void +PAGER_PositionChildWnd(HWND hwnd, PAGER_INFO* infoPtr) +{ + if (infoPtr->hwndChild) + { + int nPos = infoPtr->nPos; + + /* compensate for a grayed btn, which will soon become invisible */ + if (infoPtr->TLbtnState == PGF_GRAYED) + nPos += infoPtr->nButtonSize; + + if (infoPtr->bHorizontal) + { + TRACE("[%04x] SWP %dx%d at (%d,%d)\n", hwnd, + infoPtr->nWidth, infoPtr->nHeight, + -nPos, 0); + SetWindowPos(infoPtr->hwndChild, 0, + -nPos, 0, + infoPtr->nWidth, infoPtr->nHeight, + SWP_NOZORDER); + } + else + { + TRACE("[%04x] SWP %dx%d at (%d,%d)\n", hwnd, + infoPtr->nWidth, infoPtr->nHeight, + 0, -nPos); + SetWindowPos(infoPtr->hwndChild, 0, + 0, -nPos, + infoPtr->nWidth, infoPtr->nHeight, + SWP_NOZORDER); + } + + InvalidateRect(infoPtr->hwndChild, NULL, FALSE); + } +} + +static INT +PAGER_GetScrollRange(HWND hwnd, PAGER_INFO* infoPtr) +{ + INT scrollRange = 0; + + if (infoPtr->hwndChild) + { + INT wndSize, childSize; + RECT wndRect; + GetWindowRect(hwnd, &wndRect); + + if (infoPtr->bHorizontal) + { + wndSize = wndRect.right - wndRect.left; + PAGER_CalcSize(hwnd, &infoPtr->nWidth, TRUE); + childSize = infoPtr->nWidth; + } + else + { + wndSize = wndRect.bottom - wndRect.top; + PAGER_CalcSize(hwnd, &infoPtr->nHeight, FALSE); + childSize = infoPtr->nHeight; + } + + if (childSize > wndSize) + scrollRange = childSize - wndSize + infoPtr->nButtonSize; + } + + TRACE("[%04x] returns %d\n", hwnd, scrollRange); + return scrollRange; +} + +static void +PAGER_GrayAndRestoreBtns(PAGER_INFO* infoPtr, INT scrollRange, + BOOL* needsResize, BOOL* needsRepaint) +{ + if (infoPtr->nPos > 0) + { + *needsResize |= !infoPtr->TLbtnState; /* PGF_INVISIBLE */ + if (infoPtr->TLbtnState != PGF_DEPRESSED) + infoPtr->TLbtnState = PGF_NORMAL; + } + else + { + *needsRepaint |= (infoPtr->TLbtnState != PGF_GRAYED); + infoPtr->TLbtnState = PGF_GRAYED; + } + + if (scrollRange <= 0) + { + *needsRepaint |= (infoPtr->TLbtnState != PGF_GRAYED); + infoPtr->TLbtnState = PGF_GRAYED; + *needsRepaint |= (infoPtr->BRbtnState != PGF_GRAYED); + infoPtr->BRbtnState = PGF_GRAYED; + } + else if (infoPtr->nPos < scrollRange) + { + *needsResize |= !infoPtr->BRbtnState; /* PGF_INVISIBLE */ + if (infoPtr->BRbtnState != PGF_DEPRESSED) + infoPtr->BRbtnState = PGF_NORMAL; + } + else + { + *needsRepaint |= (infoPtr->BRbtnState != PGF_GRAYED); + infoPtr->BRbtnState = PGF_GRAYED; + } } -static LRESULT -PAGER_RecalcSize (HWND hwnd, WPARAM wParam, LPARAM lParam) +static void +PAGER_NormalizeBtns(PAGER_INFO* infoPtr, BOOL* needsRepaint) +{ + if (infoPtr->TLbtnState & (PGF_HOT | PGF_DEPRESSED)) + { + infoPtr->TLbtnState = PGF_NORMAL; + *needsRepaint = TRUE; + } + + if (infoPtr->BRbtnState & (PGF_HOT | PGF_DEPRESSED)) + { + infoPtr->BRbtnState = PGF_NORMAL; + *needsRepaint = TRUE; + } +} + +static void +PAGER_HideGrayBtns(PAGER_INFO* infoPtr, BOOL* needsResize) +{ + if (infoPtr->TLbtnState == PGF_GRAYED) + { + infoPtr->TLbtnState = PGF_INVISIBLE; + *needsResize = TRUE; + } + + if (infoPtr->BRbtnState == PGF_GRAYED) + { + infoPtr->BRbtnState = PGF_INVISIBLE; + *needsResize = TRUE; + } +} + +static void +PAGER_UpdateBtns(HWND hwnd, PAGER_INFO *infoPtr, + INT scrollRange, BOOL hideGrayBtns) +{ + BOOL resizeClient = FALSE; + BOOL repaintBtns = FALSE; + + if (scrollRange < 0) + PAGER_NormalizeBtns(infoPtr, &repaintBtns); + else + PAGER_GrayAndRestoreBtns(infoPtr, scrollRange, &resizeClient, &repaintBtns); + + if (hideGrayBtns) + PAGER_HideGrayBtns(infoPtr, &resizeClient); + + if (resizeClient) /* initiate NCCalcSize to resize client wnd */ + SetWindowPos(hwnd, 0,0,0,0,0, + SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | + SWP_NOZORDER | SWP_NOACTIVATE); + + if (repaintBtns) + SendMessageA(hwnd, WM_NCPAINT, 0, 0); +} + +static LRESULT +PAGER_SetPos(HWND hwnd, INT newPos, BOOL fromBtnPress) { PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); - DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE); - NMPGCALCSIZE nmpgcs; + INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr); - if (infoPtr->hwndChild) { - ZeroMemory (&nmpgcs, sizeof (NMPGCALCSIZE)); - nmpgcs.hdr.hwndFrom = hwnd; - nmpgcs.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID); - nmpgcs.hdr.code = PGN_CALCSIZE; - nmpgcs.dwFlag = (dwStyle & PGS_HORZ) ? PGF_CALCWIDTH : PGF_CALCHEIGHT; - SendMessageA (GetParent (hwnd), WM_NOTIFY, - (WPARAM)nmpgcs.hdr.idFrom, (LPARAM)&nmpgcs); + if (scrollRange <= 0) + newPos = 0; + else if (newPos < 0) + newPos = 0; + else if (newPos > scrollRange) + newPos = scrollRange; - infoPtr->nChildSize = (dwStyle & PGS_HORZ) ? nmpgcs.iWidth : nmpgcs.iHeight; + if (newPos != infoPtr->nPos) + { + infoPtr->nPos = newPos; + TRACE("[%04x] pos=%d\n", hwnd, infoPtr->nPos); + /* gray and restore btns, and if from WM_SETPOS, hide the gray btns */ + PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, !fromBtnPress); - FIXME("Child size %d\n", infoPtr->nChildSize); + PAGER_PositionChildWnd(hwnd, infoPtr); + } + return 0; +} +static LRESULT +PAGER_RecalcSize(HWND hwnd) +{ + PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + INT scrollRange; + + TRACE("[%04x]\n", hwnd); + scrollRange = PAGER_GetScrollRange(hwnd, infoPtr); + + if (scrollRange <= 0) + PAGER_SetPos(hwnd, 0, FALSE); + else + { + PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, TRUE); + PAGER_PositionChildWnd(hwnd, infoPtr); } return 0; } -static inline LRESULT +static LRESULT PAGER_SetBkColor (HWND hwnd, WPARAM wParam, LPARAM lParam) { PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); COLORREF clrTemp = infoPtr->clrBk; infoPtr->clrBk = (COLORREF)lParam; + TRACE("[%04x] %06lx\n", hwnd, infoPtr->clrBk); - /* FIXME: redraw */ + PAGER_RecalcSize(hwnd); return (LRESULT)clrTemp; } -static inline LRESULT +static LRESULT PAGER_SetBorder (HWND hwnd, WPARAM wParam, LPARAM lParam) { PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); INT nTemp = infoPtr->nBorder; infoPtr->nBorder = (INT)lParam; + TRACE("[%04x] %d\n", hwnd, infoPtr->nBorder); - /* FIXME: redraw */ + PAGER_RecalcSize(hwnd); return (LRESULT)nTemp; } -static inline LRESULT +static LRESULT PAGER_SetButtonSize (HWND hwnd, WPARAM wParam, LPARAM lParam) { PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); INT nTemp = infoPtr->nButtonSize; infoPtr->nButtonSize = (INT)lParam; + TRACE("[%04x] %d\n", hwnd, infoPtr->nButtonSize); - FIXME("size=%d\n", infoPtr->nButtonSize); - - /* FIXME: redraw */ + PAGER_RecalcSize(hwnd); return (LRESULT)nTemp; } -static inline LRESULT + +static LRESULT PAGER_SetChild (HWND hwnd, WPARAM wParam, LPARAM lParam) { PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); infoPtr->hwndChild = IsWindow ((HWND)lParam) ? (HWND)lParam : 0; - FIXME("hwnd=%x\n", infoPtr->hwndChild); + if (infoPtr->hwndChild) + { + RECT wndRect; + INT wndSizeScrollable; - /* FIXME: redraw */ - if (infoPtr->hwndChild) { - RECT rect; + TRACE("[%04x] hwndChild=%04x\n", hwnd, infoPtr->hwndChild); - GetClientRect (hwnd, &rect); - SetParent (infoPtr->hwndChild, hwnd); - SetWindowPos (infoPtr->hwndChild, HWND_TOP, - 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE); + GetWindowRect(hwnd, &wndRect); + wndSizeScrollable = infoPtr->bHorizontal ? + wndRect.right - wndRect.left : + wndRect.bottom - wndRect.top; - MoveWindow (infoPtr->hwndChild, 0, 0, rect.right, rect.bottom, TRUE); + infoPtr->nPos = 0; + infoPtr->nDelta = wndSizeScrollable; + + PAGER_CalcSize(hwnd, &infoPtr->nWidth, TRUE); + PAGER_CalcSize(hwnd, &infoPtr->nHeight, FALSE); + + /* adjust non-scrollable dimension to fit the child */ + SetWindowPos(hwnd, 0, + 0,0, + infoPtr->bHorizontal ? wndSizeScrollable : infoPtr->nWidth, + infoPtr->bHorizontal ? infoPtr->nHeight : wndSizeScrollable, + SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER); + + /* position child within the page scroller */ + SetWindowPos(infoPtr->hwndChild, HWND_TOP, + 0,0,0,0, + SWP_SHOWWINDOW | SWP_NOSIZE); + + PAGER_SetPos(hwnd, 0, FALSE); } return 0; } - -static inline LRESULT -PAGER_SetPos (HWND hwnd, WPARAM wParam, LPARAM lParam) +static void +PAGER_Scroll(HWND hwnd, INT dir) { PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + NMPGSCROLL nmpgScroll; - infoPtr->nPos = (INT)lParam; + if (infoPtr->hwndChild) + { + ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL)); + nmpgScroll.hdr.hwndFrom = hwnd; + nmpgScroll.hdr.idFrom = GetWindowLongA (hwnd, GWL_ID); + nmpgScroll.hdr.code = PGN_SCROLL; - FIXME("pos=%d\n", infoPtr->nPos); + GetClientRect(hwnd, &nmpgScroll.rcParent); + nmpgScroll.iXpos = nmpgScroll.iYpos = 0; + nmpgScroll.iDir = dir; + nmpgScroll.iScroll = infoPtr->nDelta; - /* FIXME: redraw */ - SetWindowPos (infoPtr->hwndChild, HWND_TOP, - 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE); + if (infoPtr->bHorizontal) + nmpgScroll.iXpos = infoPtr->nPos; + else + nmpgScroll.iYpos = infoPtr->nPos; - return 0; + TRACE("[%04x] sending PGN_SCROLL\n", hwnd); + SendMessageA (hwnd, WM_NOTIFY, + (WPARAM)nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll); + + if (infoPtr->nDelta != nmpgScroll.iScroll) + { + TRACE("delta changing from %d to %d\n", + infoPtr->nDelta, nmpgScroll.iScroll); + infoPtr->nDelta = nmpgScroll.iScroll; + } + + if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP) + PAGER_SetPos(hwnd, infoPtr->nPos - infoPtr->nDelta, TRUE); + else + PAGER_SetPos(hwnd, infoPtr->nPos + infoPtr->nDelta, TRUE); + } } - static LRESULT PAGER_Create (HWND hwnd, WPARAM wParam, LPARAM lParam) { PAGER_INFO *infoPtr; + DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE); + SetWindowLongA(hwnd, GWL_STYLE, dwStyle & WS_CLIPCHILDREN); /* allocate memory for info structure */ infoPtr = (PAGER_INFO *)COMCTL32_Alloc (sizeof(PAGER_INFO)); @@ -221,11 +641,33 @@ PAGER_Create (HWND hwnd, WPARAM wParam, LPARAM lParam) /* set default settings */ infoPtr->hwndChild = (HWND)NULL; - infoPtr->clrBk = GetSysColor (COLOR_BTNFACE); + infoPtr->clrBk = GetSysColor(COLOR_BTNFACE); infoPtr->nBorder = 0; - infoPtr->nButtonSize = 0; + infoPtr->nButtonSize = 12; infoPtr->nPos = 0; + infoPtr->nWidth = 0; + infoPtr->nHeight = 0; + infoPtr->bForward = FALSE; + infoPtr->TLbtnState = PGF_INVISIBLE; + infoPtr->BRbtnState = PGF_INVISIBLE; + if (dwStyle & PGS_AUTOSCROLL) + FIXME("[%04x] Autoscroll style is not implemented yet.\n", hwnd); + if (dwStyle & PGS_DRAGNDROP) + FIXME("[%04x] Drag and Drop style is not implemented yet.\n", hwnd); + if ((dwStyle & PGS_HORZ) && (dwStyle & PGS_VERT)) + { + ERR("[%04x] Cannot have both horizontal and vertical styles.\n", hwnd); + ERR("[%04x] Defaulting to vertical.\n", hwnd); + dwStyle &= ~PGS_HORZ; + } + else if (!(dwStyle & PGS_HORZ) && !(dwStyle & PGS_VERT)) + dwStyle |= PGS_VERT; /* the default according to MSDN */ + + infoPtr->bHorizontal = dwStyle & PGS_HORZ; + + TRACE("[%04x] orientation = %s\n", hwnd, + infoPtr->bHorizontal ? "PGS_HORZ" : "PGS_VERT"); return 0; } @@ -235,143 +677,436 @@ static LRESULT PAGER_Destroy (HWND hwnd, WPARAM wParam, LPARAM lParam) { PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); - - - - /* free pager info data */ COMCTL32_Free (infoPtr); SetWindowLongA (hwnd, 0, 0); return 0; } +static LRESULT +PAGER_NCCalcSize(HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + /* + * lParam points to a RECT struct. On entry, the struct + * contains the proposed wnd rectangle for the window. + * On exit, the struct should contain the screen + * coordinates of the corresponding window's client area. + */ + LPRECT lpRect = (LPRECT)lParam; + + if (infoPtr->bHorizontal) + { + if (infoPtr->TLbtnState) /* != PGF_INVISIBLE */ + lpRect->left += infoPtr->nButtonSize; + if (infoPtr->BRbtnState) + lpRect->right -= infoPtr->nButtonSize; + } + else + { + if (infoPtr->TLbtnState) + lpRect->top += infoPtr->nButtonSize; + if (infoPtr->BRbtnState) + lpRect->bottom -= infoPtr->nButtonSize; + } + + TRACE("[%04x] client rect set to %dx%d at (%d,%d)\n", hwnd, + lpRect->right-lpRect->left, + lpRect->bottom-lpRect->top, + lpRect->left, lpRect->top); + + return 0; +} + +static LRESULT +PAGER_NCPaint (HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + PAGER_INFO* infoPtr = PAGER_GetInfoPtr(hwnd); + DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE); + RECT rcWindow, rcBottomRight, rcTopLeft; + HDC hdc; + + if (dwStyle & WS_MINIMIZE) + return 0; + + DefWindowProcA (hwnd, WM_NCPAINT, wParam, lParam); + + if (!(hdc = GetDCEx (hwnd, 0, DCX_USESTYLE | DCX_WINDOW))) + return 0; + + GetWindowRect (hwnd, &rcWindow); + OffsetRect (&rcWindow, -rcWindow.left, -rcWindow.top); + + rcTopLeft = rcBottomRight = rcWindow; + if (infoPtr->bHorizontal) + { + rcTopLeft.right = rcTopLeft.left + infoPtr->nButtonSize; + rcBottomRight.left = rcBottomRight.right - infoPtr->nButtonSize; + } + else + { + rcTopLeft.bottom = rcTopLeft.top + infoPtr->nButtonSize; + rcBottomRight.top = rcBottomRight.bottom - infoPtr->nButtonSize; + } + + PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft, + infoPtr->bHorizontal, TRUE, infoPtr->TLbtnState); + PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight, + infoPtr->bHorizontal, FALSE, infoPtr->BRbtnState); + + ReleaseDC( hwnd, hdc ); + return 0; +} + +static INT +PAGER_HitTest (HWND hwnd, LPPOINT pt) +{ + PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + RECT clientRect; + + GetClientRect (hwnd, &clientRect); + + if (PtInRect(&clientRect, *pt)) + { + /* TRACE("HTCLIENT\n"); */ + return HTCLIENT; + } + + if (infoPtr->TLbtnState && infoPtr->TLbtnState != PGF_GRAYED) + { + if (infoPtr->bHorizontal) + { + if (pt->x < clientRect.left) + { + /* TRACE("HTLEFT\n"); */ + return HTLEFT; + } + } + else + { + if (pt->y < clientRect.top) + { + /* TRACE("HTTOP\n"); */ + return HTTOP; + } + } + } + + if (infoPtr->BRbtnState && infoPtr->BRbtnState != PGF_GRAYED) + { + if (infoPtr->bHorizontal) + { + if (pt->x > clientRect.right) + { + /* TRACE("HTRIGHT\n"); */ + return HTRIGHT; + } + } + else + { + if (pt->y > clientRect.bottom) + { + /* TRACE("HTBOTTOM\n"); */ + return HTBOTTOM; + } + } + } + + /* TRACE("HTNOWHERE\n"); */ + return HTNOWHERE; +} + +static LRESULT +PAGER_NCHitTest (HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + POINT pt = { SLOWORD(lParam), SHIWORD(lParam) }; + ScreenToClient (hwnd, &pt); + return PAGER_HitTest(hwnd, &pt); +} + +static LRESULT +PAGER_SetCursor( HWND hwnd, WPARAM wParam, LPARAM lParam ) +{ + PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + BOOL notCaptured = FALSE; + + switch(LOWORD(lParam)) + { + case HTLEFT: + case HTTOP: + if ((notCaptured = infoPtr->TLbtnState != PGF_HOT)) + infoPtr->TLbtnState = PGF_HOT; + break; + case HTRIGHT: + case HTBOTTOM: + if ((notCaptured = infoPtr->BRbtnState != PGF_HOT)) + infoPtr->BRbtnState = PGF_HOT; + break; + default: + return FALSE; + } + + if (notCaptured) + { + TRACKMOUSEEVENT trackinfo; + + TRACE("[%04x] SetCapture\n", hwnd); + SetCapture(hwnd); + + trackinfo.cbSize = sizeof(TRACKMOUSEEVENT); + trackinfo.dwFlags = TME_QUERY; + trackinfo.hwndTrack = hwnd; + trackinfo.dwHoverTime = HOVER_DEFAULT; + + /* call _TrackMouseEvent to see if we are currently tracking for this hwnd */ + _TrackMouseEvent(&trackinfo); + + /* Make sure tracking is enabled so we recieve a WM_MOUSELEAVE message */ + if(!(trackinfo.dwFlags & TME_LEAVE)) { + trackinfo.dwFlags = TME_LEAVE; /* notify upon leaving */ + + /* call TRACKMOUSEEVENT so we recieve a WM_MOUSELEAVE message */ + /* and can properly deactivate the hot button */ + _TrackMouseEvent(&trackinfo); + } + + SendMessageA(hwnd, WM_NCPAINT, 0, 0); + } + + return TRUE; +} + +static LRESULT +PAGER_MouseLeave (HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + + TRACE("[%04x] ReleaseCapture\n", hwnd); + ReleaseCapture(); + + /* Notify parent of released mouse capture */ + if (infoPtr->hwndChild) + { + NMHDR nmhdr; + ZeroMemory (&nmhdr, sizeof (NMHDR)); + nmhdr.hwndFrom = hwnd; + nmhdr.idFrom = GetWindowLongA (hwnd, GWL_ID); + nmhdr.code = NM_RELEASEDCAPTURE; + SendMessageA (GetParent (infoPtr->hwndChild), WM_NOTIFY, + (WPARAM)nmhdr.idFrom, (LPARAM)&nmhdr); + } + + /* make HOT btns NORMAL and hide gray btns */ + PAGER_UpdateBtns(hwnd, infoPtr, -1, TRUE); + + return TRUE; +} + +static LRESULT +PAGER_LButtonDown (HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + BOOL repaintBtns = FALSE; + POINT pt = { SLOWORD(lParam), SHIWORD(lParam) }; + INT hit; + + TRACE("[%04x]\n", hwnd); + + if (infoPtr->nDelta <= 0) + return FALSE; + + hit = PAGER_HitTest(hwnd, &pt); + + /* put btn in DEPRESSED state */ + if (hit == HTLEFT || hit == HTTOP) + { + repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED; + infoPtr->TLbtnState = PGF_DEPRESSED; + } + else if (hit == HTRIGHT || hit == HTBOTTOM) + { + repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED; + infoPtr->BRbtnState = PGF_DEPRESSED; + } + + if (repaintBtns) + SendMessageA(hwnd, WM_NCPAINT, 0, 0); + + switch(hit) + { + case HTLEFT: + TRACE("[%04x] PGF_SCROLLLEFT\n", hwnd); + PAGER_Scroll(hwnd, PGF_SCROLLLEFT); + break; + case HTTOP: + TRACE("[%04x] PGF_SCROLLUP\n", hwnd); + PAGER_Scroll(hwnd, PGF_SCROLLUP); + break; + case HTRIGHT: + TRACE("[%04x] PGF_SCROLLRIGHT\n", hwnd); + PAGER_Scroll(hwnd, PGF_SCROLLRIGHT); + break; + case HTBOTTOM: + TRACE("[%04x] PGF_SCROLLDOWN\n", hwnd); + PAGER_Scroll(hwnd, PGF_SCROLLDOWN); + break; + default: + break; + } + + return TRUE; +} + +static LRESULT +PAGER_LButtonUp (HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + TRACE("[%04x]\n", hwnd); + + /* make PRESSED btns NORMAL but don't hide gray btns */ + PAGER_UpdateBtns(hwnd, infoPtr, -1, FALSE); + + return 0; +} static LRESULT PAGER_EraseBackground (HWND hwnd, WPARAM wParam, LPARAM lParam) { PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); - HBRUSH hBrush = CreateSolidBrush (infoPtr->clrBk); + HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk); RECT rect; - GetClientRect (hwnd, &rect); + FillRect ((HDC)wParam, &rect, hBrush); DeleteObject (hBrush); - -/* return TRUE; */ - return FALSE; -} - - -static LRESULT -PAGER_MouseMove (HWND hwnd, WPARAM wParam, LPARAM lParam) -{ - /* PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); */ - - TRACE("stub!\n"); - - return 0; -} - - -/* << PAGER_Paint >> */ - - -static LRESULT -PAGER_Size (HWND hwnd, WPARAM wParam, LPARAM lParam) -{ - PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); - RECT rect; - - GetClientRect (hwnd, &rect); - if (infoPtr->hwndChild) { - SetWindowPos (infoPtr->hwndChild, HWND_TOP, rect.left, rect.top, - rect.right - rect.left, rect.bottom - rect.top, - SWP_SHOWWINDOW); -/* MoveWindow (infoPtr->hwndChild, 1, 1, rect.right - 2, rect.bottom-2, TRUE); */ -/* UpdateWindow (infoPtr->hwndChild); */ - - } -/* FillRect ((HDC)wParam, &rect, hBrush); */ -/* DeleteObject (hBrush); */ return TRUE; } +static LRESULT +PAGER_Size (HWND hwnd) +{ + /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */ + + PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + RECT wndRect; + GetWindowRect(hwnd, &wndRect); + + infoPtr->nDelta = infoPtr->bHorizontal ? + wndRect.right - wndRect.left : + wndRect.bottom - wndRect.top; + infoPtr->nDelta -= 2*infoPtr->nButtonSize; + + TRACE("[%04x] nDelta=%d\n", hwnd, infoPtr->nDelta); + + PAGER_PositionChildWnd(hwnd, infoPtr); + + return TRUE; +} + static LRESULT WINAPI PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - TRACE("hwnd=%x msg=%x wparam=%x lparam=%lx\n", hwnd, uMsg, wParam, lParam); - if (!PAGER_GetInfoPtr (hwnd) && (uMsg != WM_CREATE)) - return DefWindowProcA (hwnd, uMsg, wParam, lParam); + PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); + + if (!infoPtr && (uMsg != WM_CREATE)) + return DefWindowProcA (hwnd, uMsg, wParam, lParam); + switch (uMsg) { - case PGM_FORWARDMOUSE: - return PAGER_ForwardMouse (hwnd, wParam); + case PGM_FORWARDMOUSE: + return PAGER_ForwardMouse (hwnd, wParam); - case PGM_GETBKCOLOR: - return PAGER_GetBkColor (hwnd, wParam, lParam); + case PGM_GETBKCOLOR: + return PAGER_GetBkColor(hwnd); - case PGM_GETBORDER: - return PAGER_GetBorder (hwnd, wParam, lParam); + case PGM_GETBORDER: + return PAGER_GetBorder(hwnd); - case PGM_GETBUTTONSIZE: - return PAGER_GetButtonSize (hwnd, wParam, lParam); + case PGM_GETBUTTONSIZE: + return PAGER_GetButtonSize(hwnd); - case PGM_GETBUTTONSTATE: - return PAGER_GetButtonState (hwnd, wParam, lParam); + case PGM_GETPOS: + return PAGER_GetPos(hwnd); -/* case PGM_GETDROPTARGET: */ + case PGM_GETBUTTONSTATE: + return PAGER_GetButtonState (hwnd, wParam, lParam); - case PGM_GETPOS: - return PAGER_SetPos (hwnd, wParam, lParam); +/* case PGM_GETDROPTARGET: */ - case PGM_RECALCSIZE: - return PAGER_RecalcSize (hwnd, wParam, lParam); + case PGM_RECALCSIZE: + return PAGER_RecalcSize(hwnd); + + case PGM_SETBKCOLOR: + return PAGER_SetBkColor (hwnd, wParam, lParam); - case PGM_SETBKCOLOR: - return PAGER_SetBkColor (hwnd, wParam, lParam); + case PGM_SETBORDER: + return PAGER_SetBorder (hwnd, wParam, lParam); - case PGM_SETBORDER: - return PAGER_SetBorder (hwnd, wParam, lParam); + case PGM_SETBUTTONSIZE: + return PAGER_SetButtonSize (hwnd, wParam, lParam); - case PGM_SETBUTTONSIZE: - return PAGER_SetButtonSize (hwnd, wParam, lParam); + case PGM_SETCHILD: + return PAGER_SetChild (hwnd, wParam, lParam); - case PGM_SETCHILD: - return PAGER_SetChild (hwnd, wParam, lParam); + case PGM_SETPOS: + return PAGER_SetPos(hwnd, (INT)lParam, FALSE); - case PGM_SETPOS: - return PAGER_SetPos (hwnd, wParam, lParam); + case WM_CREATE: + return PAGER_Create (hwnd, wParam, lParam); - case WM_CREATE: - return PAGER_Create (hwnd, wParam, lParam); + case WM_DESTROY: + return PAGER_Destroy (hwnd, wParam, lParam); - case WM_DESTROY: - return PAGER_Destroy (hwnd, wParam, lParam); + case WM_SIZE: + return PAGER_Size (hwnd); - case WM_ERASEBKGND: - return PAGER_EraseBackground (hwnd, wParam, lParam); + case WM_NCPAINT: + return PAGER_NCPaint (hwnd, wParam, lParam); - case WM_MOUSEMOVE: - return PAGER_MouseMove (hwnd, wParam, lParam); + case WM_NCCALCSIZE: + return PAGER_NCCalcSize (hwnd, wParam, lParam); - case WM_NOTIFY: - case WM_COMMAND: - return SendMessageA (GetParent (hwnd), uMsg, wParam, lParam); + case WM_NCHITTEST: + return PAGER_NCHitTest (hwnd, wParam, lParam); -/* case WM_PAINT: */ -/* return PAGER_Paint (hwnd, wParam); */ + case WM_SETCURSOR: + { + if (hwnd == (HWND)wParam) + return PAGER_SetCursor(hwnd, wParam, lParam); + else /* its for the child */ + return 0; + } - case WM_SIZE: - return PAGER_Size (hwnd, wParam, lParam); + case WM_MOUSEMOVE: + if (infoPtr->bForward && infoPtr->hwndChild) + PostMessageA(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam); + return TRUE; - default: - if (uMsg >= WM_USER) - ERR("unknown msg %04x wp=%08x lp=%08lx\n", - uMsg, wParam, lParam); - return DefWindowProcA (hwnd, uMsg, wParam, lParam); + case WM_MOUSELEAVE: + return PAGER_MouseLeave (hwnd, wParam, lParam); + + case WM_LBUTTONDOWN: + return PAGER_LButtonDown (hwnd, wParam, lParam); + + case WM_LBUTTONUP: + return PAGER_LButtonUp (hwnd, wParam, lParam); + + case WM_ERASEBKGND: + return PAGER_EraseBackground (hwnd, wParam, lParam); +/* + case WM_PAINT: + return PAGER_Paint (hwnd, wParam); +*/ + case WM_NOTIFY: + case WM_COMMAND: + return SendMessageA (GetParent (hwnd), uMsg, wParam, lParam); + + default: + return DefWindowProcA (hwnd, uMsg, wParam, lParam); } + return 0; }