|  | /* | 
|  | * Pager control | 
|  | * | 
|  | * Copyright 1998, 1999 Eric Kohl | 
|  | * | 
|  | * NOTES | 
|  | *    Tested primarily with the controlspy Pager application. | 
|  | *       Susan Farley (susan@codeweavers.com) | 
|  | * | 
|  | * TODO: | 
|  | *    Implement repetitive button press. | 
|  | *    Adjust arrow size relative to size of button. | 
|  | *    Allow border size changes. | 
|  | *    Implement drag and drop style. | 
|  | */ | 
|  |  | 
|  | #include <string.h> | 
|  | #include "winbase.h" | 
|  | #include "commctrl.h" | 
|  | #include "debugtools.h" | 
|  |  | 
|  | DEFAULT_DEBUG_CHANNEL(pager); | 
|  |  | 
|  | typedef struct | 
|  | { | 
|  | HWND   hwndChild;  /* handle of the contained wnd */ | 
|  | BOOL   bNoResize;  /* set when created with CCS_NORESIZE */ | 
|  | COLORREF clrBk;    /* background color */ | 
|  | INT    nBorder;    /* border size for the control */ | 
|  | INT    nButtonSize;/* size of the pager btns */ | 
|  | INT    nPos;       /* scroll position */ | 
|  | 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 */ | 
|  | INT    direction;  /* direction of the scroll, (e.g. PGF_SCROLLUP) */ | 
|  | } PAGER_INFO; | 
|  |  | 
|  | #define PAGER_GetInfoPtr(hwnd) ((PAGER_INFO *)GetWindowLongA(hwnd, 0)) | 
|  | #define PAGER_IsHorizontal(hwnd) ((GetWindowLongA (hwnd, GWL_STYLE) & PGS_HORZ)) | 
|  |  | 
|  | #define MIN_ARROW_WIDTH  8 | 
|  | #define MIN_ARROW_HEIGHT 5 | 
|  |  | 
|  | #define TIMERID1         1 | 
|  | #define TIMERID2         2 | 
|  | #define INITIAL_DELAY    500 | 
|  | #define REPEAT_DELAY     50 | 
|  |  | 
|  | /* 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) | 
|  | { | 
|  | DrawEdge( hdc, &rc, BDR_RAISEDINNER, BF_RECT); | 
|  | if (horz) | 
|  | PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft); | 
|  | else | 
|  | PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft); | 
|  | } | 
|  | 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) | 
|  | { | 
|  | DrawEdge( hdc, &rc, BDR_SUNKENOUTER, BF_RECT); | 
|  | if (horz) | 
|  | PAGER_DrawHorzArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft); | 
|  | else | 
|  | PAGER_DrawVertArrow(hdc, rc, COLOR_WINDOWFRAME, topLeft); | 
|  | } | 
|  | 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); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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_GetButtonState (HWND hwnd, WPARAM wParam, LPARAM lParam) | 
|  | { | 
|  | PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); | 
|  | LRESULT btnState = PGF_INVISIBLE; | 
|  | INT btn = (INT)lParam; | 
|  | TRACE("[%04x]\n", hwnd); | 
|  |  | 
|  | if (btn == PGB_TOPORLEFT) | 
|  | btnState = infoPtr->TLbtnState; | 
|  | else if (btn == PGB_BOTTOMORRIGHT) | 
|  | btnState = infoPtr->BRbtnState; | 
|  |  | 
|  | return btnState; | 
|  | } | 
|  |  | 
|  |  | 
|  | static inline LRESULT | 
|  | PAGER_GetPos(HWND hwnd) | 
|  | { | 
|  | 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) | 
|  | { | 
|  | PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); | 
|  | TRACE("[%04x] returns %d\n", hwnd, infoPtr->nButtonSize); | 
|  | return (LRESULT)infoPtr->nButtonSize; | 
|  | } | 
|  |  | 
|  | static inline LRESULT | 
|  | PAGER_GetBorder(HWND hwnd) | 
|  | { | 
|  | PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); | 
|  | TRACE("[%04x] returns %d\n", hwnd, infoPtr->nBorder); | 
|  | return (LRESULT)infoPtr->nBorder; | 
|  | } | 
|  |  | 
|  | static inline LRESULT | 
|  | PAGER_GetBkColor(HWND hwnd) | 
|  | { | 
|  | PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); | 
|  | TRACE("[%04x] returns %06lx\n", hwnd, infoPtr->clrBk); | 
|  | return (LRESULT)infoPtr->clrBk; | 
|  | } | 
|  |  | 
|  | 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 (GetParent (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) | 
|  | { | 
|  | RECT rcClient; | 
|  | int nPos = infoPtr->nPos; | 
|  |  | 
|  | /* compensate for a grayed btn, which will soon become invisible */ | 
|  | if (infoPtr->TLbtnState == PGF_GRAYED) | 
|  | nPos += infoPtr->nButtonSize; | 
|  |  | 
|  | GetClientRect(hwnd, &rcClient); | 
|  |  | 
|  | if (PAGER_IsHorizontal(hwnd)) | 
|  | { | 
|  | int wndSize = max(0, rcClient.right - rcClient.left); | 
|  | if (infoPtr->nWidth < wndSize) | 
|  | infoPtr->nWidth = wndSize; | 
|  |  | 
|  | 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 | 
|  | { | 
|  | int wndSize = max(0, rcClient.bottom - rcClient.top); | 
|  | if (infoPtr->nHeight < wndSize) | 
|  | infoPtr->nHeight = wndSize; | 
|  |  | 
|  | 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, TRUE); | 
|  | } | 
|  | } | 
|  |  | 
|  | static INT | 
|  | PAGER_GetScrollRange(HWND hwnd, PAGER_INFO* infoPtr) | 
|  | { | 
|  | INT scrollRange = 0; | 
|  |  | 
|  | if (infoPtr->hwndChild) | 
|  | { | 
|  | INT wndSize, childSize; | 
|  | RECT wndRect; | 
|  | GetWindowRect(hwnd, &wndRect); | 
|  |  | 
|  | if (PAGER_IsHorizontal(hwnd)) | 
|  | { | 
|  | 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; | 
|  | } | 
|  |  | 
|  | TRACE("childSize = %d,  wndSize = %d\n", childSize, wndSize); | 
|  | 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 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); | 
|  | INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr); | 
|  | INT oldPos = infoPtr->nPos; | 
|  |  | 
|  | if ((scrollRange <= 0) || (newPos < 0)) | 
|  | infoPtr->nPos = 0; | 
|  | else if (newPos > scrollRange) | 
|  | infoPtr->nPos = scrollRange; | 
|  | else | 
|  | infoPtr->nPos = newPos; | 
|  |  | 
|  | TRACE("[%04x] pos=%d\n", hwnd, infoPtr->nPos); | 
|  |  | 
|  | if (infoPtr->nPos != oldPos) | 
|  | { | 
|  | /* gray and restore btns, and if from WM_SETPOS, hide the gray btns */ | 
|  | PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, !fromBtnPress); | 
|  | PAGER_PositionChildWnd(hwnd, infoPtr); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static LRESULT | 
|  | PAGER_HandleWindowPosChanging(HWND hwnd, WPARAM wParam, WINDOWPOS *winpos) | 
|  | { | 
|  | PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); | 
|  |  | 
|  | if (infoPtr->bNoResize && !(winpos->flags & SWP_NOSIZE)) | 
|  | { | 
|  | /* don't let the app resize the nonscrollable dimension of a control | 
|  | * that was created with CCS_NORESIZE style | 
|  | * (i.e. height for a horizontal pager, or width for a vertical one) */ | 
|  |  | 
|  | /* except if the current dimension is 0 and app is setting for | 
|  | * first time, then save amount as dimension. - GA 8/01 */ | 
|  |  | 
|  | if (PAGER_IsHorizontal(hwnd)) | 
|  | if (!infoPtr->nHeight && winpos->cy) | 
|  | infoPtr->nHeight = winpos->cy; | 
|  | else | 
|  | winpos->cy = infoPtr->nHeight; | 
|  | else | 
|  | if (!infoPtr->nWidth && winpos->cx) | 
|  | infoPtr->nWidth = winpos->cx; | 
|  | else | 
|  | winpos->cx = infoPtr->nWidth; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | DefWindowProcA (hwnd, WM_WINDOWPOSCHANGING, wParam, (LPARAM)winpos); | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static INT | 
|  | PAGER_SetFixedWidth(HWND hwnd, PAGER_INFO* infoPtr) | 
|  | { | 
|  | /* Must set the non-scrollable dimension to be less than the full height/width | 
|  | * so that NCCalcSize is called.  The Msoft docs mention 3/4 factor for button | 
|  | * size, and experimentation shows that affect is almost right. */ | 
|  |  | 
|  | RECT wndRect; | 
|  | INT delta, h; | 
|  | GetWindowRect(hwnd, &wndRect); | 
|  |  | 
|  | /* see what the app says for btn width */ | 
|  | PAGER_CalcSize(hwnd, &infoPtr->nWidth, TRUE); | 
|  |  | 
|  | if (infoPtr->bNoResize) | 
|  | { | 
|  | delta = wndRect.right - wndRect.left - infoPtr->nWidth; | 
|  | if (delta > infoPtr->nButtonSize) | 
|  | infoPtr->nWidth += 4 * infoPtr->nButtonSize / 3; | 
|  | else if (delta > 0) | 
|  | infoPtr->nWidth +=  infoPtr->nButtonSize / 3; | 
|  | } | 
|  |  | 
|  | h = wndRect.bottom - wndRect.top + infoPtr->nButtonSize; | 
|  |  | 
|  | TRACE("[%04x] infoPtr->nWidth set to %d\n", | 
|  | hwnd, infoPtr->nWidth); | 
|  |  | 
|  | return h; | 
|  | } | 
|  |  | 
|  | static INT | 
|  | PAGER_SetFixedHeight(HWND hwnd, PAGER_INFO* infoPtr) | 
|  | { | 
|  | /* Must set the non-scrollable dimension to be less than the full height/width | 
|  | * so that NCCalcSize is called.  The Msoft docs mention 3/4 factor for button | 
|  | * size, and experimentation shows that affect is almost right. */ | 
|  |  | 
|  | RECT wndRect; | 
|  | INT delta, w; | 
|  | GetWindowRect(hwnd, &wndRect); | 
|  |  | 
|  | /* see what the app says for btn height */ | 
|  | PAGER_CalcSize(hwnd, &infoPtr->nHeight, FALSE); | 
|  |  | 
|  | if (infoPtr->bNoResize) | 
|  | { | 
|  | delta = wndRect.bottom - wndRect.top - infoPtr->nHeight; | 
|  | if (delta > infoPtr->nButtonSize) | 
|  | infoPtr->nHeight += infoPtr->nButtonSize; | 
|  | else if (delta > 0) | 
|  | infoPtr->nHeight +=  infoPtr->nButtonSize / 3; | 
|  | } | 
|  |  | 
|  | w = wndRect.right - wndRect.left + infoPtr->nButtonSize; | 
|  |  | 
|  | TRACE("[%04x] infoPtr->nHeight set to %d\n", | 
|  | hwnd, infoPtr->nHeight); | 
|  |  | 
|  | return w; | 
|  | } | 
|  |  | 
|  | static LRESULT | 
|  | PAGER_RecalcSize(HWND hwnd) | 
|  | { | 
|  | PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); | 
|  |  | 
|  | TRACE("[%04x]\n", hwnd); | 
|  |  | 
|  | if (infoPtr->hwndChild) | 
|  | { | 
|  | INT scrollRange = PAGER_GetScrollRange(hwnd, infoPtr); | 
|  |  | 
|  | if (scrollRange <= 0) | 
|  | { | 
|  | infoPtr->nPos = -1; | 
|  | PAGER_SetPos(hwnd, 0, FALSE); | 
|  | } | 
|  | else | 
|  | { | 
|  | PAGER_UpdateBtns(hwnd, infoPtr, scrollRange, TRUE); | 
|  | PAGER_PositionChildWnd(hwnd, infoPtr); | 
|  | } | 
|  | } | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | 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); | 
|  |  | 
|  | PAGER_RecalcSize(hwnd); | 
|  | SendMessageA(hwnd, WM_NCPAINT, (WPARAM)0, (LPARAM)0); | 
|  |  | 
|  | return (LRESULT)clrTemp; | 
|  | } | 
|  |  | 
|  |  | 
|  | 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); | 
|  |  | 
|  | PAGER_RecalcSize(hwnd); | 
|  |  | 
|  | return (LRESULT)nTemp; | 
|  | } | 
|  |  | 
|  |  | 
|  | 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); | 
|  |  | 
|  | PAGER_RecalcSize(hwnd); | 
|  |  | 
|  | return (LRESULT)nTemp; | 
|  | } | 
|  |  | 
|  |  | 
|  | static LRESULT | 
|  | PAGER_SetChild (HWND hwnd, WPARAM wParam, LPARAM lParam) | 
|  | { | 
|  | PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); | 
|  | INT hw; | 
|  |  | 
|  | infoPtr->hwndChild = IsWindow ((HWND)lParam) ? (HWND)lParam : 0; | 
|  |  | 
|  | if (infoPtr->hwndChild) | 
|  | { | 
|  | TRACE("[%04x] hwndChild=%04x\n", hwnd, infoPtr->hwndChild); | 
|  |  | 
|  | if (PAGER_IsHorizontal(hwnd)) { | 
|  | hw = PAGER_SetFixedHeight(hwnd, infoPtr); | 
|  | /* adjust non-scrollable dimension to fit the child */ | 
|  | SetWindowPos(hwnd, 0, 0,0, hw, infoPtr->nHeight, | 
|  | SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER | | 
|  | SWP_NOSIZE | SWP_NOACTIVATE); | 
|  | } | 
|  | else { | 
|  | hw = PAGER_SetFixedWidth(hwnd, infoPtr); | 
|  | /* adjust non-scrollable dimension to fit the child */ | 
|  | SetWindowPos(hwnd, 0, 0,0, infoPtr->nWidth, hw, | 
|  | SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER | | 
|  | SWP_NOSIZE | SWP_NOACTIVATE); | 
|  | } | 
|  |  | 
|  | /* position child within the page scroller */ | 
|  | SetWindowPos(infoPtr->hwndChild, HWND_TOP, | 
|  | 0,0,0,0, | 
|  | SWP_SHOWWINDOW | SWP_NOSIZE);  /* native is 0 */ | 
|  |  | 
|  | infoPtr->nPos = -1; | 
|  | PAGER_SetPos(hwnd, 0, FALSE); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | PAGER_Scroll(HWND hwnd, INT dir) | 
|  | { | 
|  | PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); | 
|  | NMPGSCROLL nmpgScroll; | 
|  | RECT rcWnd; | 
|  |  | 
|  | if (infoPtr->hwndChild) | 
|  | { | 
|  | ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL)); | 
|  | nmpgScroll.hdr.hwndFrom = hwnd; | 
|  | nmpgScroll.hdr.idFrom   = GetWindowLongA (hwnd, GWL_ID); | 
|  | nmpgScroll.hdr.code = PGN_SCROLL; | 
|  |  | 
|  | GetWindowRect(hwnd, &rcWnd); | 
|  | GetClientRect(hwnd, &nmpgScroll.rcParent); | 
|  | nmpgScroll.iXpos = nmpgScroll.iYpos = 0; | 
|  | nmpgScroll.iDir = dir; | 
|  |  | 
|  | if (PAGER_IsHorizontal(hwnd)) | 
|  | { | 
|  | nmpgScroll.iScroll = rcWnd.right - rcWnd.left; | 
|  | nmpgScroll.iXpos = infoPtr->nPos; | 
|  | } | 
|  | else | 
|  | { | 
|  | nmpgScroll.iScroll = rcWnd.bottom - rcWnd.top; | 
|  | nmpgScroll.iYpos = infoPtr->nPos; | 
|  | } | 
|  | nmpgScroll.iScroll -= 2*infoPtr->nButtonSize; | 
|  |  | 
|  | SendMessageA (hwnd, WM_NOTIFY, | 
|  | (WPARAM)nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll); | 
|  |  | 
|  | TRACE("[%04x] PGN_SCROLL returns iScroll=%d\n", hwnd, nmpgScroll.iScroll); | 
|  |  | 
|  | if (nmpgScroll.iScroll > 0) | 
|  | { | 
|  | infoPtr->direction = dir; | 
|  |  | 
|  | if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP) | 
|  | PAGER_SetPos(hwnd, infoPtr->nPos - nmpgScroll.iScroll, TRUE); | 
|  | else | 
|  | PAGER_SetPos(hwnd, infoPtr->nPos + nmpgScroll.iScroll, TRUE); | 
|  | } | 
|  | else | 
|  | infoPtr->direction = -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | static LRESULT | 
|  | PAGER_Create (HWND hwnd, WPARAM wParam, LPARAM lParam) | 
|  | { | 
|  | PAGER_INFO *infoPtr; | 
|  | DWORD dwStyle = GetWindowLongA (hwnd, GWL_STYLE); | 
|  |  | 
|  | /* allocate memory for info structure */ | 
|  | infoPtr = (PAGER_INFO *)COMCTL32_Alloc (sizeof(PAGER_INFO)); | 
|  | SetWindowLongA (hwnd, 0, (DWORD)infoPtr); | 
|  |  | 
|  | /* set default settings */ | 
|  | infoPtr->hwndChild = (HWND)NULL; | 
|  | infoPtr->bNoResize = dwStyle & CCS_NORESIZE; | 
|  | infoPtr->clrBk = GetSysColor(COLOR_BTNFACE); | 
|  | infoPtr->nBorder = 0; | 
|  | infoPtr->nButtonSize = 12; | 
|  | infoPtr->nPos = 0; | 
|  | infoPtr->nWidth = 0; | 
|  | infoPtr->nHeight = 0; | 
|  | infoPtr->bForward = FALSE; | 
|  | infoPtr->TLbtnState = PGF_INVISIBLE; | 
|  | infoPtr->BRbtnState = PGF_INVISIBLE; | 
|  | infoPtr->direction = -1; | 
|  |  | 
|  | 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 neither horizontal nor vertical style specified, default to vertical. | 
|  | * This is probably not necessary, since the style may be set later on as | 
|  | * the control is initialized, but just in case it isn't, set it here. | 
|  | */ | 
|  | if (!(dwStyle & PGS_HORZ) && !(dwStyle & PGS_VERT)) | 
|  | { | 
|  | dwStyle |= PGS_VERT; | 
|  | SetWindowLongA(hwnd, GWL_STYLE, dwStyle); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | 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); | 
|  | LPRECT lpRect = (LPRECT)lParam; | 
|  | RECT rcChildw, rcmyw, wnrc, lbrc, rbrc; | 
|  | POINT cursor; | 
|  |  | 
|  | /* | 
|  | * 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. | 
|  | */ | 
|  |  | 
|  | DefWindowProcA (hwnd, WM_NCCALCSIZE, wParam, lParam); | 
|  |  | 
|  | if (PAGER_IsHorizontal(hwnd)) | 
|  | { | 
|  | infoPtr->nWidth = lpRect->right - lpRect->left; | 
|  | PAGER_CalcSize (hwnd, &infoPtr->nWidth, TRUE); | 
|  | GetWindowRect (infoPtr->hwndChild, &rcChildw); | 
|  | MapWindowPoints (0, hwnd, (LPPOINT)&rcChildw, 2); | 
|  | GetCursorPos (&cursor); | 
|  | GetWindowRect (hwnd, &rcmyw); | 
|  | if (PtInRect (&rcmyw, cursor)) { | 
|  | GetWindowRect (hwnd, &wnrc); | 
|  | lbrc = wnrc; | 
|  | lbrc.right = lbrc.left + infoPtr->nButtonSize; | 
|  | rbrc = wnrc; | 
|  | rbrc.left = rbrc.right - infoPtr->nButtonSize; | 
|  | TRACE("horz lb rect=(%d,%d)-(%d,%d), rb rect=(%d,%d)-(%d,%d)\n", | 
|  | lbrc.left, lbrc.top, lbrc.right, lbrc.bottom, | 
|  | rbrc.left, rbrc.top, rbrc.right, rbrc.bottom); | 
|  | if (PtInRect (&lbrc, cursor) && infoPtr->TLbtnState) | 
|  | RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE); | 
|  | if (PtInRect (&rbrc, cursor) && infoPtr->BRbtnState) | 
|  | RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE); | 
|  | } | 
|  | if (infoPtr->TLbtnState) /* != PGF_INVISIBLE */ | 
|  | lpRect->left += infoPtr->nButtonSize; | 
|  | if (infoPtr->BRbtnState) | 
|  | lpRect->right -= infoPtr->nButtonSize; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* native does: (from trace of IE4 opening "Favorites" frame) | 
|  | *        DefWindowProc | 
|  | *        WM_NOITFY  PGN_CALCSIZE w/ dwFlag=2 | 
|  | *        GetWindowRect (child, &rc) | 
|  | *        MapWindowPoints (0, syspager, &rc, 2) | 
|  | *        GetCursorPos( &cur ) | 
|  | *        GetWindowRect (syspager, &rc2) | 
|  | *        PtInRect (&rc2, cur.x, cur.y) rtns 0 | 
|  | *        returns with rect empty | 
|  | */ | 
|  | infoPtr->nHeight = lpRect->bottom - lpRect->top; | 
|  | PAGER_CalcSize (hwnd, &infoPtr->nHeight, FALSE); | 
|  | GetWindowRect (infoPtr->hwndChild, &rcChildw); | 
|  | MapWindowPoints (0, hwnd, (LPPOINT)&rcChildw, 2); | 
|  | GetCursorPos (&cursor); | 
|  | GetWindowRect (hwnd, &rcmyw); | 
|  | if (PtInRect (&rcmyw, cursor)) { | 
|  |  | 
|  | /* native does: | 
|  | *    GetWindowRect(pager, &rc) | 
|  | *    PtInRect(btn-left????, cur.x, cur.y) | 
|  | *    if true -> ??? | 
|  | *    PtInRect(btn-right????, cur.x, cur.y) | 
|  | *    if true | 
|  | *      RedrawWindow(pager, 0, 0, 5) | 
|  | *      return TRUE | 
|  | */ | 
|  |  | 
|  | GetWindowRect (hwnd, &wnrc); | 
|  | lbrc = wnrc; | 
|  | lbrc.right = lbrc.left + infoPtr->nButtonSize; | 
|  | rbrc = wnrc; | 
|  | rbrc.left = rbrc.right - infoPtr->nButtonSize; | 
|  | TRACE("vert lb rect=(%d,%d)-(%d,%d), rb rect=(%d,%d)-(%d,%d)\n", | 
|  | lbrc.left, lbrc.top, lbrc.right, lbrc.bottom, | 
|  | rbrc.left, rbrc.top, rbrc.right, rbrc.bottom); | 
|  | if (PtInRect (&lbrc, cursor) && infoPtr->TLbtnState) | 
|  | RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE); | 
|  | if (PtInRect (&rbrc, cursor) && infoPtr->BRbtnState) | 
|  | RedrawWindow (hwnd, 0, 0, RDW_INVALIDATE | RDW_ERASE); | 
|  | } | 
|  | if (infoPtr->TLbtnState) | 
|  | lpRect->top += infoPtr->nButtonSize; | 
|  | if (infoPtr->BRbtnState) | 
|  | lpRect->bottom -= infoPtr->nButtonSize; | 
|  | /* ???? */ | 
|  | if ((lpRect->bottom < 0) || (lpRect->bottom > infoPtr->nHeight)) | 
|  | lpRect->bottom = infoPtr->nHeight; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | BOOL bHorizontal = PAGER_IsHorizontal(hwnd); | 
|  |  | 
|  | 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 (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, | 
|  | bHorizontal, TRUE, infoPtr->TLbtnState); | 
|  | PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight, | 
|  | 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; | 
|  | BOOL bHorizontal = PAGER_IsHorizontal(hwnd); | 
|  |  | 
|  | GetClientRect (hwnd, &clientRect); | 
|  |  | 
|  | if (PtInRect(&clientRect, *pt)) | 
|  | { | 
|  | /* TRACE("HTCLIENT\n"); */ | 
|  | return HTCLIENT; | 
|  | } | 
|  |  | 
|  | if (infoPtr->TLbtnState && infoPtr->TLbtnState != PGF_GRAYED) | 
|  | { | 
|  | if (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 (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 receive a WM_MOUSELEAVE message */ | 
|  | if(!(trackinfo.dwFlags & TME_LEAVE)) { | 
|  | trackinfo.dwFlags = TME_LEAVE; /* notify upon leaving */ | 
|  |  | 
|  | /* call TRACKMOUSEEVENT so we receive 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); | 
|  |  | 
|  | KillTimer (hwnd, TIMERID1); | 
|  | KillTimer (hwnd, TIMERID2); | 
|  |  | 
|  | TRACE("[%04x] ReleaseCapture\n", hwnd); | 
|  | ReleaseCapture(); | 
|  |  | 
|  | /* Notify parent of released mouse capture */ | 
|  | { | 
|  | NMHDR nmhdr; | 
|  | ZeroMemory (&nmhdr, sizeof (NMHDR)); | 
|  | nmhdr.hwndFrom = hwnd; | 
|  | nmhdr.idFrom   = GetWindowLongA (hwnd, GWL_ID); | 
|  | nmhdr.code = NM_RELEASEDCAPTURE; | 
|  | SendMessageA (GetParent(hwnd), 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); | 
|  |  | 
|  | hit = PAGER_HitTest(hwnd, &pt); | 
|  |  | 
|  | /* put btn in DEPRESSED state */ | 
|  | if (hit == HTLEFT || hit == HTTOP) | 
|  | { | 
|  | repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED; | 
|  | infoPtr->TLbtnState = PGF_DEPRESSED; | 
|  | SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0); | 
|  | } | 
|  | else if (hit == HTRIGHT || hit == HTBOTTOM) | 
|  | { | 
|  | repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED; | 
|  | infoPtr->BRbtnState = PGF_DEPRESSED; | 
|  | SetTimer(hwnd, TIMERID1, INITIAL_DELAY, 0); | 
|  | } | 
|  |  | 
|  | 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); | 
|  |  | 
|  | KillTimer (hwnd, TIMERID1); | 
|  | KillTimer (hwnd, TIMERID2); | 
|  |  | 
|  | /* 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) | 
|  | { | 
|  | POINT pt, ptorig; | 
|  | HDC hdc = (HDC)wParam; | 
|  | HWND parent; | 
|  |  | 
|  | /* native does: | 
|  | *   parent = GetParent(pager) | 
|  | *   pt.x=0; pt.y=0; ????? | 
|  | *   MapWindowPoints(pager, parent, &pt, 1) | 
|  | *   OffsetWindowOrgEx(hdc, pt.x, pt.y, &ptorg) | 
|  | *   SendMessageA(parent, WM_ERASEBKGND, hdc, 0) | 
|  | *   SetWindowOrgEx(hdc, 0, 0, 0) | 
|  | */ | 
|  |  | 
|  | pt.x = 0; | 
|  | pt.y = 0; | 
|  | parent = GetParent(hwnd); | 
|  | MapWindowPoints(hwnd, parent, &pt, 1); | 
|  | OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig); | 
|  | SendMessageA (parent, WM_ERASEBKGND, wParam, lParam); | 
|  | SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0); | 
|  |  | 
|  |  | 
|  | #if 0 | 
|  | PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); | 
|  | HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk); | 
|  | RECT rect; | 
|  |  | 
|  | GetClientRect (hwnd, &rect); | 
|  | FillRect ((HDC)wParam, &rect, hBrush); | 
|  |  | 
|  | /* background color of the child should be the same as the pager */ | 
|  | if (infoPtr->hwndChild) | 
|  | { | 
|  | GetClientRect (infoPtr->hwndChild, &rect); | 
|  | FillRect ((HDC)wParam, &rect, hBrush); | 
|  | } | 
|  |  | 
|  | DeleteObject (hBrush); | 
|  | #endif | 
|  |  | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  |  | 
|  | static LRESULT | 
|  | PAGER_Size (HWND hwnd, WPARAM wParam, LPARAM lParam) | 
|  | { | 
|  | /* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */ | 
|  |  | 
|  | PAGER_INFO *infoPtr = PAGER_GetInfoPtr (hwnd); | 
|  | TRACE("[%04x] %dx%d\n", hwnd, LOWORD(lParam), HIWORD(lParam)); | 
|  |  | 
|  | if (PAGER_IsHorizontal(hwnd)) | 
|  | infoPtr->nHeight = HIWORD(lParam); | 
|  | else | 
|  | infoPtr->nWidth = LOWORD(lParam); | 
|  |  | 
|  | return PAGER_RecalcSize(hwnd); | 
|  | } | 
|  |  | 
|  |  | 
|  | static LRESULT WINAPI | 
|  | PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM 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_GETBKCOLOR: | 
|  | return PAGER_GetBkColor(hwnd); | 
|  |  | 
|  | case PGM_GETBORDER: | 
|  | return PAGER_GetBorder(hwnd); | 
|  |  | 
|  | case PGM_GETBUTTONSIZE: | 
|  | return PAGER_GetButtonSize(hwnd); | 
|  |  | 
|  | case PGM_GETPOS: | 
|  | return PAGER_GetPos(hwnd); | 
|  |  | 
|  | case PGM_GETBUTTONSTATE: | 
|  | return PAGER_GetButtonState (hwnd, wParam, lParam); | 
|  |  | 
|  | /*      case PGM_GETDROPTARGET: */ | 
|  |  | 
|  | case PGM_RECALCSIZE: | 
|  | return PAGER_RecalcSize(hwnd); | 
|  |  | 
|  | case PGM_SETBKCOLOR: | 
|  | return PAGER_SetBkColor (hwnd, wParam, lParam); | 
|  |  | 
|  | case PGM_SETBORDER: | 
|  | return PAGER_SetBorder (hwnd, wParam, lParam); | 
|  |  | 
|  | case PGM_SETBUTTONSIZE: | 
|  | return PAGER_SetButtonSize (hwnd, wParam, lParam); | 
|  |  | 
|  | case PGM_SETCHILD: | 
|  | return PAGER_SetChild (hwnd, wParam, lParam); | 
|  |  | 
|  | case PGM_SETPOS: | 
|  | return PAGER_SetPos(hwnd, (INT)lParam, FALSE); | 
|  |  | 
|  | case WM_CREATE: | 
|  | return PAGER_Create (hwnd, wParam, lParam); | 
|  |  | 
|  | case WM_DESTROY: | 
|  | return PAGER_Destroy (hwnd, wParam, lParam); | 
|  |  | 
|  | case WM_SIZE: | 
|  | return PAGER_Size (hwnd, wParam, lParam); | 
|  |  | 
|  | case WM_NCPAINT: | 
|  | return PAGER_NCPaint (hwnd, wParam, lParam); | 
|  |  | 
|  | case WM_WINDOWPOSCHANGING: | 
|  | return PAGER_HandleWindowPosChanging (hwnd, wParam, (WINDOWPOS*)lParam); | 
|  |  | 
|  | case WM_NCCALCSIZE: | 
|  | return PAGER_NCCalcSize (hwnd, wParam, lParam); | 
|  |  | 
|  | case WM_NCHITTEST: | 
|  | return PAGER_NCHitTest (hwnd, wParam, lParam); | 
|  |  | 
|  | case WM_SETCURSOR: | 
|  | { | 
|  | if (hwnd == (HWND)wParam) | 
|  | return PAGER_SetCursor(hwnd, wParam, lParam); | 
|  | else /* its for the child */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | case WM_MOUSEMOVE: | 
|  | if (infoPtr->bForward && infoPtr->hwndChild) | 
|  | PostMessageA(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam); | 
|  | return TRUE; | 
|  |  | 
|  | 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_TIMER: | 
|  | /* if initial timer, kill it and start the repeat timer */ | 
|  | if (wParam == TIMERID1) | 
|  | { | 
|  | KillTimer(hwnd, TIMERID1); | 
|  | SetTimer(hwnd, TIMERID2, REPEAT_DELAY, 0); | 
|  | } | 
|  |  | 
|  | KillTimer(hwnd, TIMERID2); | 
|  | if (infoPtr->direction > 0) | 
|  | { | 
|  | PAGER_Scroll(hwnd, infoPtr->direction); | 
|  | SetTimer(hwnd, TIMERID2, REPEAT_DELAY, 0); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case WM_NOTIFY: | 
|  | case WM_COMMAND: | 
|  | return SendMessageA (GetParent (hwnd), uMsg, wParam, lParam); | 
|  |  | 
|  | default: | 
|  | return DefWindowProcA (hwnd, uMsg, wParam, lParam); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | VOID | 
|  | PAGER_Register (void) | 
|  | { | 
|  | WNDCLASSA wndClass; | 
|  |  | 
|  | ZeroMemory (&wndClass, sizeof(WNDCLASSA)); | 
|  | wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_SAVEBITS; | 
|  | wndClass.lpfnWndProc   = (WNDPROC)PAGER_WindowProc; | 
|  | wndClass.cbClsExtra    = 0; | 
|  | wndClass.cbWndExtra    = sizeof(PAGER_INFO *); | 
|  | wndClass.hCursor       = LoadCursorA (0, IDC_ARROWA); | 
|  | wndClass.hbrBackground = 0; | 
|  | wndClass.lpszClassName = WC_PAGESCROLLERA; | 
|  |  | 
|  | RegisterClassA (&wndClass); | 
|  | } | 
|  |  | 
|  |  | 
|  | VOID | 
|  | PAGER_Unregister (void) | 
|  | { | 
|  | UnregisterClassA (WC_PAGESCROLLERA, (HINSTANCE)NULL); | 
|  | } | 
|  |  |