comctl32/button: Implement BCM_{GETNOTE,SETNOTE,GETNOTELENGTH} message.

Signed-off-by: Zhiyi Zhang <zzhang@codeweavers.com>
Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Zhiyi Zhang 2018-03-28 19:45:19 +08:00 committed by Alexandre Julliard
parent 3a6c724e90
commit 06d2ad29ae
3 changed files with 252 additions and 0 deletions

View File

@ -92,6 +92,8 @@ typedef struct _BUTTON_INFO
HWND hwnd;
LONG state;
HFONT font;
WCHAR *note;
INT note_length;
union
{
HICON icon;
@ -234,6 +236,14 @@ HRGN set_control_clipping( HDC hdc, const RECT *rect )
return hrgn;
}
static WCHAR *heap_strndupW(const WCHAR *src, size_t length)
{
size_t size = (length + 1) * sizeof(WCHAR);
WCHAR *dst = heap_alloc(size);
if (dst) memcpy(dst, src, size);
return dst;
}
/**********************************************************************
* Convert button styles to flags used by DrawText.
*/
@ -336,6 +346,7 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L
case WM_NCDESTROY:
SetWindowLongPtrW( hWnd, 0, 0 );
heap_free(infoPtr->note);
heap_free(infoPtr);
break;
@ -574,6 +585,81 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L
return 1; /* success. FIXME: check text length */
}
case BCM_SETNOTE:
{
WCHAR *note = (WCHAR *)lParam;
if (btn_type != BS_COMMANDLINK && btn_type != BS_DEFCOMMANDLINK)
{
SetLastError(ERROR_NOT_SUPPORTED);
return FALSE;
}
heap_free(infoPtr->note);
if (note)
{
infoPtr->note_length = lstrlenW(note);
infoPtr->note = heap_strndupW(note, infoPtr->note_length);
}
if (!note || !infoPtr->note)
{
infoPtr->note_length = 0;
infoPtr->note = heap_alloc_zero(sizeof(WCHAR));
}
SetLastError(NO_ERROR);
return TRUE;
}
case BCM_GETNOTE:
{
DWORD *size = (DWORD *)wParam;
WCHAR *buffer = (WCHAR *)lParam;
INT length = 0;
if (btn_type != BS_COMMANDLINK && btn_type != BS_DEFCOMMANDLINK)
{
SetLastError(ERROR_NOT_SUPPORTED);
return FALSE;
}
if (!buffer || !size || !infoPtr->note)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (*size > 0)
{
length = min(*size - 1, infoPtr->note_length);
memcpy(buffer, infoPtr->note, length * sizeof(WCHAR));
buffer[length] = '\0';
}
if (*size < infoPtr->note_length + 1)
{
*size = infoPtr->note_length + 1;
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
else
{
SetLastError(NO_ERROR);
return TRUE;
}
}
case BCM_GETNOTELENGTH:
{
if (btn_type != BS_COMMANDLINK && btn_type != BS_DEFCOMMANDLINK)
{
SetLastError(ERROR_NOT_SUPPORTED);
return 0;
}
return infoPtr->note_length;
}
case WM_SETFONT:
infoPtr->font = (HFONT)wParam;
if (lParam) InvalidateRect(hWnd, NULL, TRUE);

View File

@ -28,6 +28,7 @@
#include "msg.h"
#define IS_WNDPROC_HANDLE(x) (((ULONG_PTR)(x) >> 16) == (~0u >> 16))
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
static BOOL (WINAPI *pRemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR);
@ -805,6 +806,162 @@ static void test_button_class(void)
DestroyWindow(hwnd);
}
static void test_note(void)
{
HWND hwnd;
BOOL ret;
WCHAR test_w[] = {'t', 'e', 's', 't', 0};
WCHAR tes_w[] = {'t', 'e', 's', 0};
WCHAR deadbeef_w[] = {'d', 'e', 'a', 'd', 'b', 'e', 'e', 'f', 0};
WCHAR buffer_w[10];
DWORD size;
DWORD error;
INT type;
hwnd = create_button(BS_COMMANDLINK, NULL);
ok(hwnd != NULL, "Expect hwnd not null\n");
SetLastError(0xdeadbeef);
size = ARRAY_SIZE(buffer_w);
ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
error = GetLastError();
if (!ret && error == 0xdeadbeef)
{
win_skip("BCM_GETNOTE message is unavailable. Skipping note tests\n"); /* xp or 2003 */
DestroyWindow(hwnd);
return;
}
DestroyWindow(hwnd);
for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++)
{
if (type == BS_DEFCOMMANDLINK || type == BS_COMMANDLINK)
{
hwnd = create_button(type, NULL);
ok(hwnd != NULL, "Expect hwnd not null\n");
/* Get note when note hasn't been not set yet */
SetLastError(0xdeadbeef);
lstrcpyW(buffer_w, deadbeef_w);
size = ARRAY_SIZE(buffer_w);
ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
error = GetLastError();
ok(!ret, "Expect BCM_GETNOTE return false\n");
ok(!lstrcmpW(buffer_w, deadbeef_w), "Expect note: %s, got: %s\n",
wine_dbgstr_w(deadbeef_w), wine_dbgstr_w(buffer_w));
ok(size == ARRAY_SIZE(buffer_w), "Got: %d\n", size);
ok(error == ERROR_INVALID_PARAMETER, "Expect last error: 0x%08x, got: 0x%08x\n",
ERROR_INVALID_PARAMETER, error);
/* Get note length when note is not set */
ret = SendMessageA(hwnd, BCM_GETNOTELENGTH, 0, 0);
ok(ret == 0, "Expect note length: %d, got: %d\n", 0, ret);
/* Successful set note, get note and get note length */
SetLastError(0xdeadbeef);
ret = SendMessageA(hwnd, BCM_SETNOTE, 0, (LPARAM)test_w);
ok(ret, "Expect BCM_SETNOTE return true\n");
error = GetLastError();
ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error);
SetLastError(0xdeadbeef);
lstrcpyW(buffer_w, deadbeef_w);
size = ARRAY_SIZE(buffer_w);
ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
ok(ret, "Expect BCM_GETNOTE return true\n");
ok(!lstrcmpW(buffer_w, test_w), "Expect note: %s, got: %s\n", wine_dbgstr_w(test_w),
wine_dbgstr_w(buffer_w));
ok(size == ARRAY_SIZE(buffer_w), "Got: %d\n", size);
error = GetLastError();
ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error);
ret = SendMessageA(hwnd, BCM_GETNOTELENGTH, 0, 0);
ok(ret == ARRAY_SIZE(test_w) - 1, "Got: %d\n", ret);
/* Insufficient buffer, return partial string */
SetLastError(0xdeadbeef);
lstrcpyW(buffer_w, deadbeef_w);
size = ARRAY_SIZE(test_w) - 1;
ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
ok(!ret, "Expect BCM_GETNOTE return false\n");
ok(!lstrcmpW(buffer_w, tes_w), "Expect note: %s, got: %s\n", wine_dbgstr_w(tes_w),
wine_dbgstr_w(buffer_w));
ok(size == ARRAY_SIZE(test_w), "Got: %d\n", size);
error = GetLastError();
ok(error == ERROR_INSUFFICIENT_BUFFER, "Expect last error: 0x%08x, got: 0x%08x\n",
ERROR_INSUFFICIENT_BUFFER, error);
/* Set note with NULL buffer */
SetLastError(0xdeadbeef);
ret = SendMessageA(hwnd, BCM_SETNOTE, 0, 0);
ok(ret, "Expect BCM_SETNOTE return false\n");
error = GetLastError();
ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error);
/* Check that set note with NULL buffer make note empty */
SetLastError(0xdeadbeef);
lstrcpyW(buffer_w, deadbeef_w);
size = ARRAY_SIZE(buffer_w);
ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
ok(ret, "Expect BCM_GETNOTE return true\n");
ok(lstrlenW(buffer_w) == 0, "Expect note length 0\n");
ok(size == ARRAY_SIZE(buffer_w), "Got: %d\n", size);
error = GetLastError();
ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error);
ret = SendMessageA(hwnd, BCM_GETNOTELENGTH, 0, 0);
ok(ret == 0, "Expect note length: %d, got: %d\n", 0, ret);
/* Get note with NULL buffer */
SetLastError(0xdeadbeef);
size = ARRAY_SIZE(buffer_w);
ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, 0);
ok(!ret, "Expect BCM_SETNOTE return false\n");
ok(size == ARRAY_SIZE(buffer_w), "Got: %d\n", size);
error = GetLastError();
ok(error == ERROR_INVALID_PARAMETER, "Expect last error: 0x%08x, got: 0x%08x\n",
ERROR_INVALID_PARAMETER, error);
/* Get note with NULL size */
SetLastError(0xdeadbeef);
lstrcpyW(buffer_w, deadbeef_w);
ret = SendMessageA(hwnd, BCM_GETNOTE, 0, (LPARAM)buffer_w);
ok(!ret, "Expect BCM_SETNOTE return false\n");
ok(!lstrcmpW(buffer_w, deadbeef_w), "Expect note: %s, got: %s\n",
wine_dbgstr_w(deadbeef_w), wine_dbgstr_w(buffer_w));
error = GetLastError();
ok(error == ERROR_INVALID_PARAMETER, "Expect last error: 0x%08x, got: 0x%08x\n",
ERROR_INVALID_PARAMETER, error);
/* Get note with zero size */
SetLastError(0xdeadbeef);
size = 0;
lstrcpyW(buffer_w, deadbeef_w);
ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
ok(!ret, "Expect BCM_GETNOTE return false\n");
ok(!lstrcmpW(buffer_w, deadbeef_w), "Expect note: %s, got: %s\n",
wine_dbgstr_w(deadbeef_w), wine_dbgstr_w(buffer_w));
ok(size == 1, "Got: %d\n", size);
error = GetLastError();
ok(error == ERROR_INSUFFICIENT_BUFFER, "Expect last error: 0x%08x, got: 0x%08x\n",
ERROR_INSUFFICIENT_BUFFER, error);
DestroyWindow(hwnd);
}
else
{
hwnd = create_button(type, NULL);
ok(hwnd != NULL, "Expect hwnd not null\n");
SetLastError(0xdeadbeef);
size = ARRAY_SIZE(buffer_w);
ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
ok(!ret, "Expect BCM_GETNOTE return false\n");
error = GetLastError();
ok(error == ERROR_NOT_SUPPORTED, "Expect last error: 0x%08x, got: 0x%08x\n",
ERROR_NOT_SUPPORTED, error);
DestroyWindow(hwnd);
}
}
}
static void register_parent_class(void)
{
WNDCLASSA cls;
@ -837,6 +994,7 @@ START_TEST(button)
test_button_class();
test_button_messages();
test_note();
unload_v6_module(ctx_cookie, hCtx);
}

View File

@ -1093,6 +1093,14 @@ typedef struct tagNMBCHOTITEM
#define BS_COMMANDLINK 0x0000000E
#define BS_DEFCOMMANDLINK 0x0000000F
/* Button macros */
#define Button_SetNote(button, note) \
(BOOL)SNDMSG(button, BCM_SETNOTE, 0, (LPARAM)(note))
#define Button_GetNote(button, buffer, size) \
(BOOL)SNDMSG(button, BCM_GETNOTE, (WPARAM)(size), (LPARAM)(buffer))
#define Button_GetNoteLength(button) \
(LRESULT)SNDMSG(button, BCM_GETNOTELENGTH, 0, 0)
/* Toolbar */
#define TOOLBARCLASSNAMEA "ToolbarWindow32"