1
0
mirror of https://github.com/wine-mirror/wine synced 2024-07-01 07:14:31 +00:00
wine/dlls/comctl32/pager.c
Nikolay Sivov df65aee926 comctl32: Switch back to internal allocation function.
We are still using exported string functions internally,
and that caused mismatches after recent incomplete switching to crt functions.

There is also no evidence that crt functions are used at all there,
so for now switch back to fix mismatching calls.

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
2022-12-09 17:52:25 +01:00

1593 lines
50 KiB
C

/*
* Pager control
*
* Copyright 1998, 1999 Eric Kohl
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* NOTES
*
* This code was audited for completeness against the documented features
* of Comctl32.dll version 6.0 on Sep. 18, 2004, by Robert Shearman.
*
* Unless otherwise noted, we believe this code to be complete, as per
* the specification mentioned above.
* If you discover missing features or bugs please note them below.
*
* TODO:
* Implement repetitive button press.
* Adjust arrow size relative to size of button.
* Allow border size changes.
* Styles:
* PGS_DRAGNDROP
* Notifications:
* PGN_HOTITEMCHANGE
* Messages:
* WM_PRINT and/or WM_PRINTCLIENT
*
* TESTING:
* Tested primarily with the controlspy Pager application.
* Susan Farley (susan@codeweavers.com)
*
* IMPLEMENTATION NOTES:
* This control uses WM_NCPAINT instead of WM_PAINT to paint itself
* as we need to scroll a child window. In order to do this we move
* the child window in the control's client area, using the clipping
* region that is automatically set around the client area. As the
* entire client area now consists of the child window, we must
* allocate space (WM_NCCALCSIZE) for the buttons and draw them as
* a non-client area (WM_NCPAINT).
* Robert Shearman <rob@codeweavers.com>
*/
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "commctrl.h"
#include "comctl32.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(pager);
typedef struct
{
HWND hwndSelf; /* handle of the control wnd */
HWND hwndChild; /* handle of the contained wnd */
HWND hwndNotify; /* handle of the parent wnd */
BOOL bUnicode; /* send notifications in Unicode */
DWORD dwStyle; /* styles for this control */
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 */
BOOL bCapture; /* we have captured the mouse */
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) */
WCHAR *pwszBuffer;/* text buffer for converted notifications */
INT nBufferSize;/* size of the above buffer */
} PAGER_INFO;
#define TIMERID1 1
#define TIMERID2 2
#define INITIAL_DELAY 500
#define REPEAT_DELAY 50
/* Text field conversion behavior flags for PAGER_SendConvertedNotify() */
enum conversion_flags
{
/* Convert Unicode text to ANSI for parent before sending. If not set, do nothing */
CONVERT_SEND = 0x01,
/* Convert ANSI text from parent back to Unicode for children */
CONVERT_RECEIVE = 0x02,
/* Send empty text to parent if text is NULL. Original text pointer still remains NULL */
SEND_EMPTY_IF_NULL = 0x04,
/* Set text to null after parent received the notification if the required mask is not set before sending notification */
SET_NULL_IF_NO_MASK = 0x08,
/* Zero out the text buffer before sending it to parent */
ZERO_SEND = 0x10
};
static void
PAGER_GetButtonRects(const PAGER_INFO* infoPtr, RECT* prcTopLeft, RECT* prcBottomRight, BOOL bClientCoords)
{
RECT rcWindow;
GetWindowRect (infoPtr->hwndSelf, &rcWindow);
if (bClientCoords)
MapWindowPoints( 0, infoPtr->hwndSelf, (POINT *)&rcWindow, 2 );
else
OffsetRect(&rcWindow, -rcWindow.left, -rcWindow.top);
*prcTopLeft = *prcBottomRight = rcWindow;
if (infoPtr->dwStyle & PGS_HORZ)
{
prcTopLeft->right = prcTopLeft->left + infoPtr->nButtonSize;
prcBottomRight->left = prcBottomRight->right - infoPtr->nButtonSize;
}
else
{
prcTopLeft->bottom = prcTopLeft->top + infoPtr->nButtonSize;
prcBottomRight->top = prcBottomRight->bottom - infoPtr->nButtonSize;
}
}
static void
PAGER_DrawButton(HDC hdc, COLORREF clrBk, RECT rc,
BOOL horz, BOOL topLeft, INT btnState)
{
UINT flags;
TRACE("rc = %s, btnState = %d\n", wine_dbgstr_rect(&rc), btnState);
if (btnState == PGF_INVISIBLE)
return;
if ((rc.right - rc.left <= 0) || (rc.bottom - rc.top <= 0))
return;
if (horz)
flags = topLeft ? DFCS_SCROLLLEFT : DFCS_SCROLLRIGHT;
else
flags = topLeft ? DFCS_SCROLLUP : DFCS_SCROLLDOWN;
switch (btnState)
{
case PGF_HOT:
break;
case PGF_NORMAL:
flags |= DFCS_FLAT;
break;
case PGF_DEPRESSED:
flags |= DFCS_PUSHED;
break;
case PGF_GRAYED:
flags |= DFCS_INACTIVE | DFCS_FLAT;
break;
}
DrawFrameControl( hdc, &rc, DFC_SCROLL, flags );
}
/* << PAGER_GetDropTarget >> */
static inline LRESULT
PAGER_ForwardMouse (PAGER_INFO* infoPtr, BOOL bFwd)
{
TRACE("[%p]\n", infoPtr->hwndSelf);
infoPtr->bForward = bFwd;
return 0;
}
static inline LRESULT
PAGER_GetButtonState (const PAGER_INFO* infoPtr, INT btn)
{
LRESULT btnState = PGF_INVISIBLE;
TRACE("[%p]\n", infoPtr->hwndSelf);
if (btn == PGB_TOPORLEFT)
btnState = infoPtr->TLbtnState;
else if (btn == PGB_BOTTOMORRIGHT)
btnState = infoPtr->BRbtnState;
return btnState;
}
static inline INT
PAGER_GetPos(const PAGER_INFO *infoPtr)
{
TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nPos);
return infoPtr->nPos;
}
static inline INT
PAGER_GetButtonSize(const PAGER_INFO *infoPtr)
{
TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nButtonSize);
return infoPtr->nButtonSize;
}
static inline INT
PAGER_GetBorder(const PAGER_INFO *infoPtr)
{
TRACE("[%p] returns %d\n", infoPtr->hwndSelf, infoPtr->nBorder);
return infoPtr->nBorder;
}
static inline COLORREF
PAGER_GetBkColor(const PAGER_INFO *infoPtr)
{
TRACE("[%p] returns %#lx\n", infoPtr->hwndSelf, infoPtr->clrBk);
return infoPtr->clrBk;
}
static void
PAGER_CalcSize( PAGER_INFO *infoPtr )
{
NMPGCALCSIZE nmpgcs;
ZeroMemory (&nmpgcs, sizeof (NMPGCALCSIZE));
nmpgcs.hdr.hwndFrom = infoPtr->hwndSelf;
nmpgcs.hdr.idFrom = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
nmpgcs.hdr.code = PGN_CALCSIZE;
nmpgcs.dwFlag = (infoPtr->dwStyle & PGS_HORZ) ? PGF_CALCWIDTH : PGF_CALCHEIGHT;
nmpgcs.iWidth = infoPtr->nWidth;
nmpgcs.iHeight = infoPtr->nHeight;
SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, nmpgcs.hdr.idFrom, (LPARAM)&nmpgcs);
if (infoPtr->dwStyle & PGS_HORZ)
infoPtr->nWidth = nmpgcs.iWidth;
else
infoPtr->nHeight = nmpgcs.iHeight;
TRACE("[%p] PGN_CALCSIZE returns %dx%d\n", infoPtr->hwndSelf, nmpgcs.iWidth, nmpgcs.iHeight );
}
static void
PAGER_PositionChildWnd(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(infoPtr->hwndSelf, &rcClient);
if (infoPtr->dwStyle & PGS_HORZ)
{
int wndSize = max(0, rcClient.right - rcClient.left);
if (infoPtr->nWidth < wndSize)
infoPtr->nWidth = wndSize;
TRACE("[%p] SWP %dx%d at (%d,%d)\n", infoPtr->hwndSelf,
infoPtr->nWidth, infoPtr->nHeight,
-nPos, 0);
SetWindowPos(infoPtr->hwndChild, HWND_TOP,
-nPos, 0,
infoPtr->nWidth, infoPtr->nHeight, 0);
}
else
{
int wndSize = max(0, rcClient.bottom - rcClient.top);
if (infoPtr->nHeight < wndSize)
infoPtr->nHeight = wndSize;
TRACE("[%p] SWP %dx%d at (%d,%d)\n", infoPtr->hwndSelf,
infoPtr->nWidth, infoPtr->nHeight,
0, -nPos);
SetWindowPos(infoPtr->hwndChild, HWND_TOP,
0, -nPos,
infoPtr->nWidth, infoPtr->nHeight, 0);
}
InvalidateRect(infoPtr->hwndChild, NULL, TRUE);
}
}
static INT
PAGER_GetScrollRange(PAGER_INFO* infoPtr, BOOL calc_size)
{
INT scrollRange = 0;
if (infoPtr->hwndChild)
{
INT wndSize, childSize;
RECT wndRect;
GetWindowRect(infoPtr->hwndSelf, &wndRect);
if (calc_size)
PAGER_CalcSize(infoPtr);
if (infoPtr->dwStyle & PGS_HORZ)
{
wndSize = wndRect.right - wndRect.left;
childSize = infoPtr->nWidth;
}
else
{
wndSize = wndRect.bottom - wndRect.top;
childSize = infoPtr->nHeight;
}
TRACE("childSize = %d, wndSize = %d\n", childSize, wndSize);
if (childSize > wndSize)
scrollRange = childSize - wndSize + infoPtr->nButtonSize;
}
TRACE("[%p] returns %d\n", infoPtr->hwndSelf, scrollRange);
return scrollRange;
}
static void
PAGER_UpdateBtns(PAGER_INFO *infoPtr, INT scrollRange, BOOL hideGrayBtns)
{
BOOL resizeClient;
BOOL repaintBtns;
INT oldTLbtnState = infoPtr->TLbtnState;
INT oldBRbtnState = infoPtr->BRbtnState;
POINT pt;
RECT rcTopLeft, rcBottomRight;
/* get button rects */
PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, TRUE);
GetCursorPos(&pt);
ScreenToClient( infoPtr->hwndSelf, &pt );
/* update states based on scroll position */
if (infoPtr->nPos > 0)
{
if (infoPtr->TLbtnState == PGF_INVISIBLE || infoPtr->TLbtnState == PGF_GRAYED)
infoPtr->TLbtnState = PGF_NORMAL;
}
else if (!hideGrayBtns && PtInRect(&rcTopLeft, pt))
infoPtr->TLbtnState = PGF_GRAYED;
else
infoPtr->TLbtnState = PGF_INVISIBLE;
if (scrollRange <= 0)
{
infoPtr->TLbtnState = PGF_INVISIBLE;
infoPtr->BRbtnState = PGF_INVISIBLE;
}
else if (infoPtr->nPos < scrollRange)
{
if (infoPtr->BRbtnState == PGF_INVISIBLE || infoPtr->BRbtnState == PGF_GRAYED)
infoPtr->BRbtnState = PGF_NORMAL;
}
else if (!hideGrayBtns && PtInRect(&rcBottomRight, pt))
infoPtr->BRbtnState = PGF_GRAYED;
else
infoPtr->BRbtnState = PGF_INVISIBLE;
/* only need to resize when entering or leaving PGF_INVISIBLE state */
resizeClient =
((oldTLbtnState == PGF_INVISIBLE) != (infoPtr->TLbtnState == PGF_INVISIBLE)) ||
((oldBRbtnState == PGF_INVISIBLE) != (infoPtr->BRbtnState == PGF_INVISIBLE));
/* initiate NCCalcSize to resize client wnd if necessary */
if (resizeClient)
SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
SWP_NOZORDER | SWP_NOACTIVATE);
/* repaint when changing any state */
repaintBtns = (oldTLbtnState != infoPtr->TLbtnState) ||
(oldBRbtnState != infoPtr->BRbtnState);
if (repaintBtns)
SendMessageW(infoPtr->hwndSelf, WM_NCPAINT, 0, 0);
}
static LRESULT
PAGER_SetPos(PAGER_INFO* infoPtr, INT newPos, BOOL fromBtnPress, BOOL calc_size)
{
INT scrollRange = PAGER_GetScrollRange(infoPtr, calc_size);
INT oldPos = infoPtr->nPos;
if ((scrollRange <= 0) || (newPos < 0))
infoPtr->nPos = 0;
else if (newPos > scrollRange)
infoPtr->nPos = scrollRange;
else
infoPtr->nPos = newPos;
TRACE("[%p] pos=%d, oldpos=%d\n", infoPtr->hwndSelf, infoPtr->nPos, oldPos);
if (infoPtr->nPos != oldPos)
{
/* gray and restore btns, and if from WM_SETPOS, hide the gray btns */
PAGER_UpdateBtns(infoPtr, scrollRange, !fromBtnPress);
PAGER_PositionChildWnd(infoPtr);
}
return 0;
}
/******************************************************************
* For the PGM_RECALCSIZE message (but not the other uses in *
* this module), the native control does only the following: *
* *
* if (some condition) *
* PostMessageW(hwnd, EM_FMTLINES, 0, 0); *
* return DefWindowProcW(hwnd, PGM_RECALCSIZE, 0, 0); *
* *
* When we figure out what the "some condition" is we will *
* implement that for the message processing. *
******************************************************************/
static LRESULT
PAGER_RecalcSize(PAGER_INFO *infoPtr)
{
TRACE("[%p]\n", infoPtr->hwndSelf);
if (infoPtr->hwndChild)
{
INT scrollRange = PAGER_GetScrollRange(infoPtr, TRUE);
if (scrollRange <= 0)
{
infoPtr->nPos = -1;
PAGER_SetPos(infoPtr, 0, FALSE, TRUE);
}
else
PAGER_PositionChildWnd(infoPtr);
}
return 1;
}
static COLORREF
PAGER_SetBkColor (PAGER_INFO* infoPtr, COLORREF clrBk)
{
COLORREF clrTemp = infoPtr->clrBk;
infoPtr->clrBk = clrBk;
TRACE("[%p] %#lx\n", infoPtr->hwndSelf, infoPtr->clrBk);
/* the native control seems to do things this way */
SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
SWP_NOZORDER | SWP_NOACTIVATE);
RedrawWindow(infoPtr->hwndSelf, 0, 0, RDW_ERASE | RDW_INVALIDATE);
return clrTemp;
}
static INT
PAGER_SetBorder (PAGER_INFO* infoPtr, INT iBorder)
{
INT nTemp = infoPtr->nBorder;
infoPtr->nBorder = iBorder;
TRACE("[%p] %d\n", infoPtr->hwndSelf, infoPtr->nBorder);
PAGER_RecalcSize(infoPtr);
return nTemp;
}
static INT
PAGER_SetButtonSize (PAGER_INFO* infoPtr, INT iButtonSize)
{
INT nTemp = infoPtr->nButtonSize;
infoPtr->nButtonSize = iButtonSize;
TRACE("[%p] %d\n", infoPtr->hwndSelf, infoPtr->nButtonSize);
PAGER_RecalcSize(infoPtr);
return nTemp;
}
static LRESULT
PAGER_SetChild (PAGER_INFO* infoPtr, HWND hwndChild)
{
infoPtr->hwndChild = IsWindow (hwndChild) ? hwndChild : 0;
if (infoPtr->hwndChild)
{
TRACE("[%p] hwndChild=%p\n", infoPtr->hwndSelf, infoPtr->hwndChild);
SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
infoPtr->nPos = -1;
PAGER_SetPos(infoPtr, 0, FALSE, FALSE);
}
return 0;
}
static void
PAGER_Scroll(PAGER_INFO* infoPtr, INT dir)
{
NMPGSCROLL nmpgScroll;
RECT rcWnd;
if (infoPtr->hwndChild)
{
ZeroMemory (&nmpgScroll, sizeof (NMPGSCROLL));
nmpgScroll.hdr.hwndFrom = infoPtr->hwndSelf;
nmpgScroll.hdr.idFrom = GetWindowLongPtrW (infoPtr->hwndSelf, GWLP_ID);
nmpgScroll.hdr.code = PGN_SCROLL;
GetWindowRect(infoPtr->hwndSelf, &rcWnd);
GetClientRect(infoPtr->hwndSelf, &nmpgScroll.rcParent);
nmpgScroll.iXpos = nmpgScroll.iYpos = 0;
nmpgScroll.iDir = dir;
if (infoPtr->dwStyle & PGS_HORZ)
{
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;
SendMessageW (infoPtr->hwndNotify, WM_NOTIFY, nmpgScroll.hdr.idFrom, (LPARAM)&nmpgScroll);
TRACE("[%p] PGN_SCROLL returns iScroll=%d\n", infoPtr->hwndSelf, nmpgScroll.iScroll);
if (nmpgScroll.iScroll > 0)
{
infoPtr->direction = dir;
if (dir == PGF_SCROLLLEFT || dir == PGF_SCROLLUP)
PAGER_SetPos(infoPtr, infoPtr->nPos - nmpgScroll.iScroll, TRUE, TRUE);
else
PAGER_SetPos(infoPtr, infoPtr->nPos + nmpgScroll.iScroll, TRUE, TRUE);
}
else
infoPtr->direction = -1;
}
}
static LRESULT
PAGER_FmtLines(const PAGER_INFO *infoPtr)
{
/* initiate NCCalcSize to resize client wnd and get size */
SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
SWP_NOZORDER | SWP_NOACTIVATE);
SetWindowPos(infoPtr->hwndChild, 0,
0,0,infoPtr->nWidth,infoPtr->nHeight,
0);
return DefWindowProcW (infoPtr->hwndSelf, EM_FMTLINES, 0, 0);
}
static LRESULT
PAGER_Create (HWND hwnd, const CREATESTRUCTW *lpcs)
{
PAGER_INFO *infoPtr;
INT ret;
/* allocate memory for info structure */
infoPtr = Alloc(sizeof(*infoPtr));
if (!infoPtr) return -1;
SetWindowLongPtrW (hwnd, 0, (DWORD_PTR)infoPtr);
/* set default settings */
infoPtr->hwndSelf = hwnd;
infoPtr->hwndChild = NULL;
infoPtr->hwndNotify = lpcs->hwndParent;
infoPtr->dwStyle = lpcs->style;
infoPtr->clrBk = GetSysColor(COLOR_BTNFACE);
infoPtr->nBorder = 0;
infoPtr->nButtonSize = 12;
infoPtr->nPos = 0;
infoPtr->nWidth = 0;
infoPtr->nHeight = 0;
infoPtr->bForward = FALSE;
infoPtr->bCapture = FALSE;
infoPtr->TLbtnState = PGF_INVISIBLE;
infoPtr->BRbtnState = PGF_INVISIBLE;
infoPtr->direction = -1;
if (infoPtr->dwStyle & PGS_DRAGNDROP)
FIXME("[%p] Drag and Drop style is not implemented yet.\n", infoPtr->hwndSelf);
ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
infoPtr->bUnicode = (ret == NFR_UNICODE);
return 0;
}
static LRESULT
PAGER_Destroy (PAGER_INFO *infoPtr)
{
SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
Free (infoPtr->pwszBuffer);
Free (infoPtr);
return 0;
}
static LRESULT
PAGER_NCCalcSize(PAGER_INFO* infoPtr, WPARAM wParam, LPRECT lpRect)
{
RECT rcChild, rcWindow;
/*
* lpRect 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.
*/
DefWindowProcW (infoPtr->hwndSelf, WM_NCCALCSIZE, wParam, (LPARAM)lpRect);
TRACE("orig rect=%s\n", wine_dbgstr_rect(lpRect));
GetWindowRect (infoPtr->hwndChild, &rcChild);
MapWindowPoints (0, infoPtr->hwndSelf, (LPPOINT)&rcChild, 2); /* FIXME: RECT != 2 POINTS */
GetWindowRect (infoPtr->hwndSelf, &rcWindow);
infoPtr->nWidth = lpRect->right - lpRect->left;
infoPtr->nHeight = lpRect->bottom - lpRect->top;
PAGER_CalcSize( infoPtr );
if (infoPtr->dwStyle & PGS_HORZ)
{
if (infoPtr->TLbtnState && (lpRect->left + infoPtr->nButtonSize < lpRect->right))
lpRect->left += infoPtr->nButtonSize;
if (infoPtr->BRbtnState && (lpRect->right - infoPtr->nButtonSize > lpRect->left))
lpRect->right -= infoPtr->nButtonSize;
}
else
{
if (infoPtr->TLbtnState && (lpRect->top + infoPtr->nButtonSize < lpRect->bottom))
lpRect->top += infoPtr->nButtonSize;
if (infoPtr->BRbtnState && (lpRect->bottom - infoPtr->nButtonSize > lpRect->top))
lpRect->bottom -= infoPtr->nButtonSize;
}
TRACE("nPos=%d, nHeight=%d, window=%s\n", infoPtr->nPos, infoPtr->nHeight, wine_dbgstr_rect(&rcWindow));
TRACE("[%p] client rect set to %s BtnState[%d,%d]\n", infoPtr->hwndSelf, wine_dbgstr_rect(lpRect),
infoPtr->TLbtnState, infoPtr->BRbtnState);
return 0;
}
static LRESULT
PAGER_NCPaint (const PAGER_INFO* infoPtr, HRGN hRgn)
{
RECT rcBottomRight, rcTopLeft;
HDC hdc;
if (infoPtr->dwStyle & WS_MINIMIZE)
return 0;
DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)hRgn, 0);
if (!(hdc = GetDCEx (infoPtr->hwndSelf, 0, DCX_USESTYLE | DCX_WINDOW)))
return 0;
PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, FALSE);
PAGER_DrawButton(hdc, infoPtr->clrBk, rcTopLeft,
infoPtr->dwStyle & PGS_HORZ, TRUE, infoPtr->TLbtnState);
PAGER_DrawButton(hdc, infoPtr->clrBk, rcBottomRight,
infoPtr->dwStyle & PGS_HORZ, FALSE, infoPtr->BRbtnState);
ReleaseDC( infoPtr->hwndSelf, hdc );
return 0;
}
static INT
PAGER_HitTest (const PAGER_INFO* infoPtr, const POINT * pt)
{
RECT clientRect, rcTopLeft, rcBottomRight;
POINT ptWindow;
GetClientRect (infoPtr->hwndSelf, &clientRect);
if (PtInRect(&clientRect, *pt))
{
TRACE("child\n");
return -1;
}
ptWindow = *pt;
PAGER_GetButtonRects(infoPtr, &rcTopLeft, &rcBottomRight, TRUE);
if ((infoPtr->TLbtnState != PGF_INVISIBLE) && PtInRect(&rcTopLeft, ptWindow))
{
TRACE("PGB_TOPORLEFT\n");
return PGB_TOPORLEFT;
}
else if ((infoPtr->BRbtnState != PGF_INVISIBLE) && PtInRect(&rcBottomRight, ptWindow))
{
TRACE("PGB_BOTTOMORRIGHT\n");
return PGB_BOTTOMORRIGHT;
}
TRACE("nowhere\n");
return -1;
}
static LRESULT
PAGER_NCHitTest (const PAGER_INFO* infoPtr, INT x, INT y)
{
POINT pt;
INT nHit;
pt.x = x;
pt.y = y;
ScreenToClient (infoPtr->hwndSelf, &pt);
nHit = PAGER_HitTest(infoPtr, &pt);
return (nHit < 0) ? HTTRANSPARENT : HTCLIENT;
}
static LRESULT
PAGER_MouseMove (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
{
POINT clpt, pt;
RECT wnrect;
BOOL topLeft = FALSE;
INT btnstate = 0;
INT hit;
HDC hdc;
pt.x = x;
pt.y = y;
TRACE("[%p] to (%d,%d)\n", infoPtr->hwndSelf, x, y);
ClientToScreen(infoPtr->hwndSelf, &pt);
GetWindowRect(infoPtr->hwndSelf, &wnrect);
if (PtInRect(&wnrect, pt)) {
RECT topleft, bottomright, *rect = NULL;
PAGER_GetButtonRects(infoPtr, &topleft, &bottomright, FALSE);
clpt = pt;
MapWindowPoints(0, infoPtr->hwndSelf, &clpt, 1);
hit = PAGER_HitTest(infoPtr, &clpt);
if ((hit == PGB_TOPORLEFT) && (infoPtr->TLbtnState == PGF_NORMAL))
{
topLeft = TRUE;
rect = &topleft;
infoPtr->TLbtnState = PGF_HOT;
btnstate = infoPtr->TLbtnState;
}
else if ((hit == PGB_BOTTOMORRIGHT) && (infoPtr->BRbtnState == PGF_NORMAL))
{
topLeft = FALSE;
rect = &bottomright;
infoPtr->BRbtnState = PGF_HOT;
btnstate = infoPtr->BRbtnState;
}
/* If in one of the buttons the capture and draw buttons */
if (rect)
{
TRACE("[%p] draw btn (%s), Capture %s, style %#lx\n",
infoPtr->hwndSelf, wine_dbgstr_rect(rect),
(infoPtr->bCapture) ? "TRUE" : "FALSE",
infoPtr->dwStyle);
if (!infoPtr->bCapture)
{
TRACE("[%p] SetCapture\n", infoPtr->hwndSelf);
SetCapture(infoPtr->hwndSelf);
infoPtr->bCapture = TRUE;
}
if (infoPtr->dwStyle & PGS_AUTOSCROLL)
SetTimer(infoPtr->hwndSelf, TIMERID1, 0x3e, 0);
hdc = GetWindowDC(infoPtr->hwndSelf);
/* OffsetRect(wnrect, 0 | 1, 0 | 1) */
PAGER_DrawButton(hdc, infoPtr->clrBk, *rect,
infoPtr->dwStyle & PGS_HORZ, topLeft, btnstate);
ReleaseDC(infoPtr->hwndSelf, hdc);
return 0;
}
}
/* If we think we are captured, then do release */
if (infoPtr->bCapture && (WindowFromPoint(pt) != infoPtr->hwndSelf))
{
NMHDR nmhdr;
infoPtr->bCapture = FALSE;
if (GetCapture() == infoPtr->hwndSelf)
{
ReleaseCapture();
if (infoPtr->TLbtnState == PGF_GRAYED)
{
infoPtr->TLbtnState = PGF_INVISIBLE;
SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
SWP_NOZORDER | SWP_NOACTIVATE);
}
else if (infoPtr->TLbtnState == PGF_HOT)
{
infoPtr->TLbtnState = PGF_NORMAL;
/* FIXME: just invalidate button rect */
RedrawWindow(infoPtr->hwndSelf, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
}
if (infoPtr->BRbtnState == PGF_GRAYED)
{
infoPtr->BRbtnState = PGF_INVISIBLE;
SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
SWP_NOZORDER | SWP_NOACTIVATE);
}
else if (infoPtr->BRbtnState == PGF_HOT)
{
infoPtr->BRbtnState = PGF_NORMAL;
/* FIXME: just invalidate button rect */
RedrawWindow(infoPtr->hwndSelf, NULL, NULL, RDW_FRAME | RDW_INVALIDATE);
}
/* Notify parent of released mouse capture */
memset(&nmhdr, 0, sizeof(NMHDR));
nmhdr.hwndFrom = infoPtr->hwndSelf;
nmhdr.idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
nmhdr.code = NM_RELEASEDCAPTURE;
SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmhdr.idFrom, (LPARAM)&nmhdr);
}
if (IsWindow(infoPtr->hwndSelf))
KillTimer(infoPtr->hwndSelf, TIMERID1);
}
return 0;
}
static LRESULT
PAGER_LButtonDown (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
{
BOOL repaintBtns = FALSE;
POINT pt;
INT hit;
pt.x = x;
pt.y = y;
TRACE("[%p] at (%d,%d)\n", infoPtr->hwndSelf, x, y);
hit = PAGER_HitTest(infoPtr, &pt);
/* put btn in DEPRESSED state */
if (hit == PGB_TOPORLEFT)
{
repaintBtns = infoPtr->TLbtnState != PGF_DEPRESSED;
infoPtr->TLbtnState = PGF_DEPRESSED;
SetTimer(infoPtr->hwndSelf, TIMERID1, INITIAL_DELAY, 0);
}
else if (hit == PGB_BOTTOMORRIGHT)
{
repaintBtns = infoPtr->BRbtnState != PGF_DEPRESSED;
infoPtr->BRbtnState = PGF_DEPRESSED;
SetTimer(infoPtr->hwndSelf, TIMERID1, INITIAL_DELAY, 0);
}
if (repaintBtns)
SendMessageW(infoPtr->hwndSelf, WM_NCPAINT, 0, 0);
switch(hit)
{
case PGB_TOPORLEFT:
if (infoPtr->dwStyle & PGS_HORZ)
{
TRACE("[%p] PGF_SCROLLLEFT\n", infoPtr->hwndSelf);
PAGER_Scroll(infoPtr, PGF_SCROLLLEFT);
}
else
{
TRACE("[%p] PGF_SCROLLUP\n", infoPtr->hwndSelf);
PAGER_Scroll(infoPtr, PGF_SCROLLUP);
}
break;
case PGB_BOTTOMORRIGHT:
if (infoPtr->dwStyle & PGS_HORZ)
{
TRACE("[%p] PGF_SCROLLRIGHT\n", infoPtr->hwndSelf);
PAGER_Scroll(infoPtr, PGF_SCROLLRIGHT);
}
else
{
TRACE("[%p] PGF_SCROLLDOWN\n", infoPtr->hwndSelf);
PAGER_Scroll(infoPtr, PGF_SCROLLDOWN);
}
break;
default:
break;
}
return 0;
}
static LRESULT
PAGER_LButtonUp (PAGER_INFO* infoPtr, INT keys, INT x, INT y)
{
TRACE("[%p]\n", infoPtr->hwndSelf);
KillTimer (infoPtr->hwndSelf, TIMERID1);
KillTimer (infoPtr->hwndSelf, TIMERID2);
/* make PRESSED btns NORMAL but don't hide gray btns */
if (infoPtr->TLbtnState & (PGF_HOT | PGF_DEPRESSED))
infoPtr->TLbtnState = PGF_NORMAL;
if (infoPtr->BRbtnState & (PGF_HOT | PGF_DEPRESSED))
infoPtr->BRbtnState = PGF_NORMAL;
return 0;
}
static LRESULT
PAGER_Timer (PAGER_INFO* infoPtr, INT nTimerId)
{
INT dir;
/* if initial timer, kill it and start the repeat timer */
if (nTimerId == TIMERID1) {
if (infoPtr->TLbtnState == PGF_HOT)
dir = (infoPtr->dwStyle & PGS_HORZ) ?
PGF_SCROLLLEFT : PGF_SCROLLUP;
else
dir = (infoPtr->dwStyle & PGS_HORZ) ?
PGF_SCROLLRIGHT : PGF_SCROLLDOWN;
TRACE("[%p] TIMERID1: style=%#lx, dir=%d\n",
infoPtr->hwndSelf, infoPtr->dwStyle, dir);
KillTimer(infoPtr->hwndSelf, TIMERID1);
SetTimer(infoPtr->hwndSelf, TIMERID1, REPEAT_DELAY, 0);
if (infoPtr->dwStyle & PGS_AUTOSCROLL) {
PAGER_Scroll(infoPtr, dir);
SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE |
SWP_NOZORDER | SWP_NOACTIVATE);
}
return 0;
}
TRACE("[%p] TIMERID2: dir=%d\n", infoPtr->hwndSelf, infoPtr->direction);
KillTimer(infoPtr->hwndSelf, TIMERID2);
if (infoPtr->direction > 0) {
PAGER_Scroll(infoPtr, infoPtr->direction);
SetTimer(infoPtr->hwndSelf, TIMERID2, REPEAT_DELAY, 0);
}
return 0;
}
static LRESULT PAGER_ThemeChanged (const PAGER_INFO* infoPtr)
{
InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
return 0;
}
static LRESULT
PAGER_EraseBackground (const PAGER_INFO* infoPtr, HDC hdc)
{
POINT pt, ptorig;
HWND parent;
LRESULT ret;
pt.x = 0;
pt.y = 0;
parent = GetParent(infoPtr->hwndSelf);
MapWindowPoints(infoPtr->hwndSelf, parent, &pt, 1);
OffsetWindowOrgEx (hdc, pt.x, pt.y, &ptorig);
ret = SendMessageW (parent, WM_ERASEBKGND, (WPARAM)hdc, 0);
SetWindowOrgEx (hdc, ptorig.x, ptorig.y, 0);
return ret;
}
static LRESULT
PAGER_Size (PAGER_INFO* infoPtr, INT type, INT x, INT y)
{
/* note that WM_SIZE is sent whenever NCCalcSize resizes the client wnd */
TRACE("[%p] %d,%d\n", infoPtr->hwndSelf, x, y);
if (infoPtr->dwStyle & PGS_HORZ)
infoPtr->nHeight = y;
else
infoPtr->nWidth = x;
return PAGER_RecalcSize(infoPtr);
}
static LRESULT
PAGER_StyleChanged(PAGER_INFO *infoPtr, WPARAM wStyleType, const STYLESTRUCT *lpss)
{
DWORD oldStyle = infoPtr->dwStyle;
TRACE("styletype %Ix, styleOld %#lx, styleNew %#lx\n", wStyleType, lpss->styleOld, lpss->styleNew);
if (wStyleType != GWL_STYLE) return 0;
infoPtr->dwStyle = lpss->styleNew;
if ((oldStyle ^ lpss->styleNew) & (PGS_HORZ | PGS_VERT))
{
PAGER_RecalcSize(infoPtr);
}
return 0;
}
static LRESULT PAGER_NotifyFormat(PAGER_INFO *infoPtr, INT command)
{
INT ret;
switch (command)
{
case NF_REQUERY:
ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
infoPtr->bUnicode = (ret == NFR_UNICODE);
return ret;
case NF_QUERY:
/* Pager always wants Unicode notifications from children */
return NFR_UNICODE;
default:
return 0;
}
}
static UINT PAGER_GetAnsiNtfCode(UINT code)
{
switch (code)
{
/* ComboxBoxEx */
case CBEN_DRAGBEGINW: return CBEN_DRAGBEGINA;
case CBEN_ENDEDITW: return CBEN_ENDEDITA;
case CBEN_GETDISPINFOW: return CBEN_GETDISPINFOA;
/* Date and Time Picker */
case DTN_FORMATW: return DTN_FORMATA;
case DTN_FORMATQUERYW: return DTN_FORMATQUERYA;
case DTN_USERSTRINGW: return DTN_USERSTRINGA;
case DTN_WMKEYDOWNW: return DTN_WMKEYDOWNA;
/* Header */
case HDN_BEGINTRACKW: return HDN_BEGINTRACKA;
case HDN_DIVIDERDBLCLICKW: return HDN_DIVIDERDBLCLICKA;
case HDN_ENDTRACKW: return HDN_ENDTRACKA;
case HDN_GETDISPINFOW: return HDN_GETDISPINFOA;
case HDN_ITEMCHANGEDW: return HDN_ITEMCHANGEDA;
case HDN_ITEMCHANGINGW: return HDN_ITEMCHANGINGA;
case HDN_ITEMCLICKW: return HDN_ITEMCLICKA;
case HDN_ITEMDBLCLICKW: return HDN_ITEMDBLCLICKA;
case HDN_TRACKW: return HDN_TRACKA;
/* List View */
case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
case LVN_INCREMENTALSEARCHW: return LVN_INCREMENTALSEARCHA;
case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
/* Toolbar */
case TBN_GETBUTTONINFOW: return TBN_GETBUTTONINFOA;
case TBN_GETINFOTIPW: return TBN_GETINFOTIPA;
/* Tooltip */
case TTN_GETDISPINFOW: return TTN_GETDISPINFOA;
/* Tree View */
case TVN_BEGINDRAGW: return TVN_BEGINDRAGA;
case TVN_BEGINLABELEDITW: return TVN_BEGINLABELEDITA;
case TVN_BEGINRDRAGW: return TVN_BEGINRDRAGA;
case TVN_DELETEITEMW: return TVN_DELETEITEMA;
case TVN_ENDLABELEDITW: return TVN_ENDLABELEDITA;
case TVN_GETDISPINFOW: return TVN_GETDISPINFOA;
case TVN_GETINFOTIPW: return TVN_GETINFOTIPA;
case TVN_ITEMEXPANDEDW: return TVN_ITEMEXPANDEDA;
case TVN_ITEMEXPANDINGW: return TVN_ITEMEXPANDINGA;
case TVN_SELCHANGEDW: return TVN_SELCHANGEDA;
case TVN_SELCHANGINGW: return TVN_SELCHANGINGA;
case TVN_SETDISPINFOW: return TVN_SETDISPINFOA;
}
return code;
}
static BOOL PAGER_AdjustBuffer(PAGER_INFO *infoPtr, INT size)
{
if (!infoPtr->pwszBuffer)
infoPtr->pwszBuffer = Alloc(size);
else if (infoPtr->nBufferSize < size)
infoPtr->pwszBuffer = ReAlloc(infoPtr->pwszBuffer, size);
if (!infoPtr->pwszBuffer) return FALSE;
if (infoPtr->nBufferSize < size) infoPtr->nBufferSize = size;
return TRUE;
}
/* Convert text to Unicode and return the original text address */
static WCHAR *PAGER_ConvertText(WCHAR **text)
{
WCHAR *oldText = *text;
*text = NULL;
Str_SetPtrWtoA((CHAR **)text, oldText);
return oldText;
}
static void PAGER_RestoreText(WCHAR **text, WCHAR *oldText)
{
if (!oldText) return;
Free(*text);
*text = oldText;
}
static LRESULT PAGER_SendConvertedNotify(PAGER_INFO *infoPtr, NMHDR *hdr, UINT *mask, UINT requiredMask, WCHAR **text,
INT *textMax, DWORD flags)
{
CHAR *sendBuffer = NULL;
CHAR *receiveBuffer;
INT bufferSize;
WCHAR *oldText;
INT oldTextMax;
LRESULT ret = NO_ERROR;
oldText = *text;
oldTextMax = textMax ? *textMax : 0;
hdr->code = PAGER_GetAnsiNtfCode(hdr->code);
if (mask && !(*mask & requiredMask))
{
ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
if (flags & SET_NULL_IF_NO_MASK) oldText = NULL;
goto done;
}
if (oldTextMax < 0) goto done;
if ((*text && flags & (CONVERT_SEND | ZERO_SEND)) || (!*text && flags & SEND_EMPTY_IF_NULL))
{
bufferSize = textMax ? *textMax : lstrlenW(*text) + 1;
sendBuffer = Alloc(bufferSize);
if (!sendBuffer) goto done;
if (!(flags & ZERO_SEND)) WideCharToMultiByte(CP_ACP, 0, *text, -1, sendBuffer, bufferSize, NULL, FALSE);
*text = (WCHAR *)sendBuffer;
}
ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
if (*text && oldText && (flags & CONVERT_RECEIVE))
{
/* MultiByteToWideChar requires that source and destination are not the same buffer */
if (*text == oldText)
{
bufferSize = lstrlenA((CHAR *)*text) + 1;
receiveBuffer = Alloc(bufferSize);
if (!receiveBuffer) goto done;
memcpy(receiveBuffer, *text, bufferSize);
MultiByteToWideChar(CP_ACP, 0, receiveBuffer, bufferSize, oldText, oldTextMax);
Free(receiveBuffer);
}
else
MultiByteToWideChar(CP_ACP, 0, (CHAR *)*text, -1, oldText, oldTextMax);
}
done:
Free(sendBuffer);
*text = oldText;
return ret;
}
static LRESULT PAGER_Notify(PAGER_INFO *infoPtr, NMHDR *hdr)
{
LRESULT ret;
if (infoPtr->bUnicode) return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
switch (hdr->code)
{
/* ComboBoxEx */
case CBEN_GETDISPINFOW:
{
NMCOMBOBOXEXW *nmcbe = (NMCOMBOBOXEXW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, &nmcbe->ceItem.mask, CBEIF_TEXT, &nmcbe->ceItem.pszText,
&nmcbe->ceItem.cchTextMax, ZERO_SEND | SET_NULL_IF_NO_MASK | CONVERT_RECEIVE);
}
case CBEN_DRAGBEGINW:
{
NMCBEDRAGBEGINW *nmdbW = (NMCBEDRAGBEGINW *)hdr;
NMCBEDRAGBEGINA nmdbA = {{0}};
nmdbA.hdr.code = PAGER_GetAnsiNtfCode(nmdbW->hdr.code);
nmdbA.hdr.hwndFrom = nmdbW->hdr.hwndFrom;
nmdbA.hdr.idFrom = nmdbW->hdr.idFrom;
nmdbA.iItemid = nmdbW->iItemid;
WideCharToMultiByte(CP_ACP, 0, nmdbW->szText, ARRAY_SIZE(nmdbW->szText), nmdbA.szText, ARRAY_SIZE(nmdbA.szText),
NULL, FALSE);
return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)&nmdbA);
}
case CBEN_ENDEDITW:
{
NMCBEENDEDITW *nmedW = (NMCBEENDEDITW *)hdr;
NMCBEENDEDITA nmedA = {{0}};
nmedA.hdr.code = PAGER_GetAnsiNtfCode(nmedW->hdr.code);
nmedA.hdr.hwndFrom = nmedW->hdr.hwndFrom;
nmedA.hdr.idFrom = nmedW->hdr.idFrom;
nmedA.fChanged = nmedW->fChanged;
nmedA.iNewSelection = nmedW->iNewSelection;
nmedA.iWhy = nmedW->iWhy;
WideCharToMultiByte(CP_ACP, 0, nmedW->szText, ARRAY_SIZE(nmedW->szText), nmedA.szText, ARRAY_SIZE(nmedA.szText),
NULL, FALSE);
return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)&nmedA);
}
/* Date and Time Picker */
case DTN_FORMATW:
{
NMDATETIMEFORMATW *nmdtf = (NMDATETIMEFORMATW *)hdr;
WCHAR *oldFormat;
INT textLength;
hdr->code = PAGER_GetAnsiNtfCode(hdr->code);
oldFormat = PAGER_ConvertText((WCHAR **)&nmdtf->pszFormat);
ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)nmdtf);
PAGER_RestoreText((WCHAR **)&nmdtf->pszFormat, oldFormat);
if (nmdtf->pszDisplay)
{
textLength = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)nmdtf->pszDisplay, -1, 0, 0);
if (!PAGER_AdjustBuffer(infoPtr, textLength * sizeof(WCHAR))) return ret;
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)nmdtf->pszDisplay, -1, infoPtr->pwszBuffer, textLength);
if (nmdtf->pszDisplay != nmdtf->szDisplay)
nmdtf->pszDisplay = infoPtr->pwszBuffer;
else
{
textLength = min(textLength, ARRAY_SIZE(nmdtf->szDisplay));
memcpy(nmdtf->szDisplay, infoPtr->pwszBuffer, textLength * sizeof(WCHAR));
}
}
return ret;
}
case DTN_FORMATQUERYW:
{
NMDATETIMEFORMATQUERYW *nmdtfq = (NMDATETIMEFORMATQUERYW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, (WCHAR **)&nmdtfq->pszFormat, NULL, CONVERT_SEND);
}
case DTN_WMKEYDOWNW:
{
NMDATETIMEWMKEYDOWNW *nmdtkd = (NMDATETIMEWMKEYDOWNW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, (WCHAR **)&nmdtkd->pszFormat, NULL, CONVERT_SEND);
}
case DTN_USERSTRINGW:
{
NMDATETIMESTRINGW *nmdts = (NMDATETIMESTRINGW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, (WCHAR **)&nmdts->pszUserString, NULL, CONVERT_SEND);
}
/* Header */
case HDN_BEGINTRACKW:
case HDN_DIVIDERDBLCLICKW:
case HDN_ENDTRACKW:
case HDN_ITEMCHANGEDW:
case HDN_ITEMCHANGINGW:
case HDN_ITEMCLICKW:
case HDN_ITEMDBLCLICKW:
case HDN_TRACKW:
{
NMHEADERW *nmh = (NMHEADERW *)hdr;
WCHAR *oldText = NULL, *oldFilterText = NULL;
HD_TEXTFILTERW *tf = NULL;
hdr->code = PAGER_GetAnsiNtfCode(hdr->code);
if (!nmh->pitem) return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
if (nmh->pitem->mask & HDI_TEXT) oldText = PAGER_ConvertText(&nmh->pitem->pszText);
if ((nmh->pitem->mask & HDI_FILTER) && (nmh->pitem->type == HDFT_ISSTRING) && nmh->pitem->pvFilter)
{
tf = (HD_TEXTFILTERW *)nmh->pitem->pvFilter;
oldFilterText = PAGER_ConvertText(&tf->pszText);
}
ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
PAGER_RestoreText(&nmh->pitem->pszText, oldText);
if (tf) PAGER_RestoreText(&tf->pszText, oldFilterText);
return ret;
}
case HDN_GETDISPINFOW:
{
NMHDDISPINFOW *nmhddi = (NMHDDISPINFOW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, &nmhddi->mask, HDI_TEXT, &nmhddi->pszText, &nmhddi->cchTextMax,
SEND_EMPTY_IF_NULL | CONVERT_SEND | CONVERT_RECEIVE);
}
/* List View */
case LVN_BEGINLABELEDITW:
case LVN_ENDLABELEDITW:
case LVN_SETDISPINFOW:
{
NMLVDISPINFOW *nmlvdi = (NMLVDISPINFOW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, &nmlvdi->item.mask, LVIF_TEXT, &nmlvdi->item.pszText,
&nmlvdi->item.cchTextMax, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE);
}
case LVN_GETDISPINFOW:
{
NMLVDISPINFOW *nmlvdi = (NMLVDISPINFOW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, &nmlvdi->item.mask, LVIF_TEXT, &nmlvdi->item.pszText,
&nmlvdi->item.cchTextMax, CONVERT_RECEIVE);
}
case LVN_GETINFOTIPW:
{
NMLVGETINFOTIPW *nmlvgit = (NMLVGETINFOTIPW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, &nmlvgit->pszText, &nmlvgit->cchTextMax,
CONVERT_SEND | CONVERT_RECEIVE);
}
case LVN_INCREMENTALSEARCHW:
case LVN_ODFINDITEMW:
{
NMLVFINDITEMW *nmlvfi = (NMLVFINDITEMW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, &nmlvfi->lvfi.flags, LVFI_STRING | LVFI_SUBSTRING,
(WCHAR **)&nmlvfi->lvfi.psz, NULL, CONVERT_SEND);
}
/* Toolbar */
case TBN_GETBUTTONINFOW:
{
NMTOOLBARW *nmtb = (NMTOOLBARW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, &nmtb->pszText, &nmtb->cchText,
SEND_EMPTY_IF_NULL | CONVERT_SEND | CONVERT_RECEIVE);
}
case TBN_GETINFOTIPW:
{
NMTBGETINFOTIPW *nmtbgit = (NMTBGETINFOTIPW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, &nmtbgit->pszText, &nmtbgit->cchTextMax, CONVERT_RECEIVE);
}
/* Tooltip */
case TTN_GETDISPINFOW:
{
NMTTDISPINFOW *nmttdiW = (NMTTDISPINFOW *)hdr;
NMTTDISPINFOA nmttdiA = {{0}};
INT size;
nmttdiA.hdr.code = PAGER_GetAnsiNtfCode(nmttdiW->hdr.code);
nmttdiA.hdr.hwndFrom = nmttdiW->hdr.hwndFrom;
nmttdiA.hdr.idFrom = nmttdiW->hdr.idFrom;
nmttdiA.hinst = nmttdiW->hinst;
nmttdiA.uFlags = nmttdiW->uFlags;
nmttdiA.lParam = nmttdiW->lParam;
nmttdiA.lpszText = nmttdiA.szText;
WideCharToMultiByte(CP_ACP, 0, nmttdiW->szText, ARRAY_SIZE(nmttdiW->szText), nmttdiA.szText,
ARRAY_SIZE(nmttdiA.szText), NULL, FALSE);
ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)&nmttdiA);
nmttdiW->hinst = nmttdiA.hinst;
nmttdiW->uFlags = nmttdiA.uFlags;
nmttdiW->lParam = nmttdiA.lParam;
MultiByteToWideChar(CP_ACP, 0, nmttdiA.szText, ARRAY_SIZE(nmttdiA.szText), nmttdiW->szText,
ARRAY_SIZE(nmttdiW->szText));
if (!nmttdiA.lpszText)
nmttdiW->lpszText = nmttdiW->szText;
else if (!IS_INTRESOURCE(nmttdiA.lpszText))
{
size = MultiByteToWideChar(CP_ACP, 0, nmttdiA.lpszText, -1, 0, 0);
if (size > ARRAY_SIZE(nmttdiW->szText))
{
if (!PAGER_AdjustBuffer(infoPtr, size * sizeof(WCHAR))) return ret;
MultiByteToWideChar(CP_ACP, 0, nmttdiA.lpszText, -1, infoPtr->pwszBuffer, size);
nmttdiW->lpszText = infoPtr->pwszBuffer;
/* Override content in szText */
memcpy(nmttdiW->szText, nmttdiW->lpszText, min(sizeof(nmttdiW->szText), size * sizeof(WCHAR)));
}
else
{
MultiByteToWideChar(CP_ACP, 0, nmttdiA.lpszText, -1, nmttdiW->szText, ARRAY_SIZE(nmttdiW->szText));
nmttdiW->lpszText = nmttdiW->szText;
}
}
else
{
nmttdiW->szText[0] = 0;
nmttdiW->lpszText = (WCHAR *)nmttdiA.lpszText;
}
return ret;
}
/* Tree View */
case TVN_BEGINDRAGW:
case TVN_BEGINRDRAGW:
case TVN_ITEMEXPANDEDW:
case TVN_ITEMEXPANDINGW:
{
NMTREEVIEWW *nmtv = (NMTREEVIEWW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtv->itemNew.mask, TVIF_TEXT, &nmtv->itemNew.pszText, NULL,
CONVERT_SEND);
}
case TVN_DELETEITEMW:
{
NMTREEVIEWW *nmtv = (NMTREEVIEWW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtv->itemOld.mask, TVIF_TEXT, &nmtv->itemOld.pszText, NULL,
CONVERT_SEND);
}
case TVN_BEGINLABELEDITW:
case TVN_ENDLABELEDITW:
{
NMTVDISPINFOW *nmtvdi = (NMTVDISPINFOW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtvdi->item.mask, TVIF_TEXT, &nmtvdi->item.pszText,
&nmtvdi->item.cchTextMax, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE);
}
case TVN_SELCHANGINGW:
case TVN_SELCHANGEDW:
{
NMTREEVIEWW *nmtv = (NMTREEVIEWW *)hdr;
WCHAR *oldItemOldText = NULL;
WCHAR *oldItemNewText = NULL;
hdr->code = PAGER_GetAnsiNtfCode(hdr->code);
if (!((nmtv->itemNew.mask | nmtv->itemOld.mask) & TVIF_TEXT))
return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
if (nmtv->itemOld.mask & TVIF_TEXT) oldItemOldText = PAGER_ConvertText(&nmtv->itemOld.pszText);
if (nmtv->itemNew.mask & TVIF_TEXT) oldItemNewText = PAGER_ConvertText(&nmtv->itemNew.pszText);
ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
PAGER_RestoreText(&nmtv->itemOld.pszText, oldItemOldText);
PAGER_RestoreText(&nmtv->itemNew.pszText, oldItemNewText);
return ret;
}
case TVN_GETDISPINFOW:
{
NMTVDISPINFOW *nmtvdi = (NMTVDISPINFOW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtvdi->item.mask, TVIF_TEXT, &nmtvdi->item.pszText,
&nmtvdi->item.cchTextMax, ZERO_SEND | CONVERT_RECEIVE);
}
case TVN_SETDISPINFOW:
{
NMTVDISPINFOW *nmtvdi = (NMTVDISPINFOW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtvdi->item.mask, TVIF_TEXT, &nmtvdi->item.pszText,
&nmtvdi->item.cchTextMax, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE);
}
case TVN_GETINFOTIPW:
{
NMTVGETINFOTIPW *nmtvgit = (NMTVGETINFOTIPW *)hdr;
return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, &nmtvgit->pszText, &nmtvgit->cchTextMax, CONVERT_RECEIVE);
}
}
/* Other notifications, no need to convert */
return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
}
static LRESULT WINAPI
PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAGER_INFO *infoPtr = (PAGER_INFO *)GetWindowLongPtrW(hwnd, 0);
TRACE("%p, %#x, %#Ix, %#Ix\n", hwnd, uMsg, wParam, lParam);
if (!infoPtr && (uMsg != WM_CREATE))
return DefWindowProcW (hwnd, uMsg, wParam, lParam);
switch (uMsg)
{
case EM_FMTLINES:
return PAGER_FmtLines(infoPtr);
case PGM_FORWARDMOUSE:
return PAGER_ForwardMouse (infoPtr, (BOOL)wParam);
case PGM_GETBKCOLOR:
return PAGER_GetBkColor(infoPtr);
case PGM_GETBORDER:
return PAGER_GetBorder(infoPtr);
case PGM_GETBUTTONSIZE:
return PAGER_GetButtonSize(infoPtr);
case PGM_GETPOS:
return PAGER_GetPos(infoPtr);
case PGM_GETBUTTONSTATE:
return PAGER_GetButtonState (infoPtr, (INT)lParam);
/* case PGM_GETDROPTARGET: */
case PGM_RECALCSIZE:
return PAGER_RecalcSize(infoPtr);
case PGM_SETBKCOLOR:
return PAGER_SetBkColor (infoPtr, (COLORREF)lParam);
case PGM_SETBORDER:
return PAGER_SetBorder (infoPtr, (INT)lParam);
case PGM_SETBUTTONSIZE:
return PAGER_SetButtonSize (infoPtr, (INT)lParam);
case PGM_SETCHILD:
return PAGER_SetChild (infoPtr, (HWND)lParam);
case PGM_SETPOS:
return PAGER_SetPos(infoPtr, (INT)lParam, FALSE, TRUE);
case WM_CREATE:
return PAGER_Create (hwnd, (LPCREATESTRUCTW)lParam);
case WM_DESTROY:
return PAGER_Destroy (infoPtr);
case WM_SIZE:
return PAGER_Size (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
case WM_NCPAINT:
return PAGER_NCPaint (infoPtr, (HRGN)wParam);
case WM_STYLECHANGED:
return PAGER_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
case WM_NCCALCSIZE:
return PAGER_NCCalcSize (infoPtr, wParam, (LPRECT)lParam);
case WM_NCHITTEST:
return PAGER_NCHitTest (infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
case WM_MOUSEMOVE:
if (infoPtr->bForward && infoPtr->hwndChild)
PostMessageW(infoPtr->hwndChild, WM_MOUSEMOVE, wParam, lParam);
return PAGER_MouseMove (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
case WM_LBUTTONDOWN:
return PAGER_LButtonDown (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
case WM_LBUTTONUP:
return PAGER_LButtonUp (infoPtr, (INT)wParam, (short)LOWORD(lParam), (short)HIWORD(lParam));
case WM_ERASEBKGND:
return PAGER_EraseBackground (infoPtr, (HDC)wParam);
case WM_TIMER:
return PAGER_Timer (infoPtr, (INT)wParam);
case WM_NOTIFYFORMAT:
return PAGER_NotifyFormat (infoPtr, lParam);
case WM_NOTIFY:
return PAGER_Notify (infoPtr, (NMHDR *)lParam);
case WM_COMMAND:
return SendMessageW (infoPtr->hwndNotify, uMsg, wParam, lParam);
case WM_THEMECHANGED:
return PAGER_ThemeChanged (infoPtr);
default:
return DefWindowProcW (hwnd, uMsg, wParam, lParam);
}
}
VOID
PAGER_Register (void)
{
WNDCLASSW wndClass;
ZeroMemory (&wndClass, sizeof(WNDCLASSW));
wndClass.style = CS_GLOBALCLASS;
wndClass.lpfnWndProc = PAGER_WindowProc;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = sizeof(PAGER_INFO *);
wndClass.hCursor = LoadCursorW (0, (LPWSTR)IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wndClass.lpszClassName = WC_PAGESCROLLERW;
RegisterClassW (&wndClass);
}
VOID
PAGER_Unregister (void)
{
UnregisterClassW (WC_PAGESCROLLERW, NULL);
}