From 5f7243b5772eb2341e31263f1a1a5934fea6568b Mon Sep 17 00:00:00 2001 From: Amine Khaldi Date: Fri, 25 Jan 2019 13:16:18 +0100 Subject: [PATCH] [COMCTL32_WINETEST] Sync with Wine Staging 4.0. CORE-15682 --- modules/rostests/winetests/comctl32/button.c | 913 ++++++++++++++- modules/rostests/winetests/comctl32/combo.c | 11 +- .../rostests/winetests/comctl32/datetime.c | 41 + modules/rostests/winetests/comctl32/edit.c | 84 +- modules/rostests/winetests/comctl32/header.c | 4 +- .../rostests/winetests/comctl32/imagelist.c | 15 +- .../rostests/winetests/comctl32/ipaddress.c | 4 +- modules/rostests/winetests/comctl32/listbox.c | 725 ++++++++++-- .../rostests/winetests/comctl32/listview.c | 184 ++- modules/rostests/winetests/comctl32/misc.c | 70 +- .../rostests/winetests/comctl32/monthcal.c | 13 +- modules/rostests/winetests/comctl32/mru.c | 4 +- modules/rostests/winetests/comctl32/pager.c | 1005 ++++++++++++++++- .../rostests/winetests/comctl32/progress.c | 2 +- .../rostests/winetests/comctl32/propsheet.c | 42 +- modules/rostests/winetests/comctl32/rebar.c | 4 +- .../rostests/winetests/comctl32/resources.h | 1 + modules/rostests/winetests/comctl32/rsrc.rc | 7 + modules/rostests/winetests/comctl32/status.c | 128 ++- .../rostests/winetests/comctl32/subclass.c | 31 +- .../rostests/winetests/comctl32/taskdialog.c | 653 ++++++++++- modules/rostests/winetests/comctl32/toolbar.c | 19 +- .../rostests/winetests/comctl32/tooltips.c | 4 +- .../rostests/winetests/comctl32/trackbar.c | 52 + .../rostests/winetests/comctl32/treeview.c | 143 ++- modules/rostests/winetests/comctl32/updown.c | 12 +- 26 files changed, 3902 insertions(+), 269 deletions(-) diff --git a/modules/rostests/winetests/comctl32/button.c b/modules/rostests/winetests/comctl32/button.c index d73fc1da9bd..edfa447baba 100644 --- a/modules/rostests/winetests/comctl32/button.c +++ b/modules/rostests/winetests/comctl32/button.c @@ -20,7 +20,9 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#ifdef __REACTOS__ #undef USE_WINE_TODOS +#endif #include #include @@ -29,11 +31,18 @@ #include "v6util.h" #include "msg.h" +#ifdef __REACTOS__ +#define BS_PUSHBOX 0x0000000AL +#endif + #define IS_WNDPROC_HANDLE(x) (((ULONG_PTR)(x) >> 16) == (~0u >> 16)) static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR); static BOOL (WINAPI *pRemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR); static LRESULT (WINAPI *pDefSubclassProc)(HWND, UINT, WPARAM, LPARAM); +static HIMAGELIST (WINAPI *pImageList_Create)(int, int, UINT, int, int); +static int (WINAPI *pImageList_Add)(HIMAGELIST, HBITMAP, HBITMAP); +static BOOL (WINAPI *pImageList_Destroy)(HIMAGELIST); /****************** button message test *************************/ #define ID_BUTTON 0x000e @@ -78,6 +87,12 @@ static void init_functions(void) MAKEFUNC_ORD(RemoveWindowSubclass, 412); MAKEFUNC_ORD(DefSubclassProc, 413); #undef MAKEFUNC_ORD + +#define X(f) p##f = (void *)GetProcAddress(hmod, #f); + X(ImageList_Create); + X(ImageList_Add); + X(ImageList_Destroy); +#undef X } /* try to make sure pending X events have been processed before continuing */ @@ -550,15 +565,15 @@ static void test_button_messages(void) hfont2 = CreateFontIndirectA(&logfont); ok(hfont2 != NULL, "Failed to create Tahoma font\n"); - for (i = 0; i < sizeof(button)/sizeof(button[0]); i++) + for (i = 0; i < ARRAY_SIZE(button); i++) { HFONT prevfont, hfont; MSG msg; DWORD style, state; HDC hdc; - trace("%d: button test sequence\n", i); hwnd = create_button(button[i].style, parent); + ok(hwnd != NULL, "Failed to create a button.\n"); style = GetWindowLongA(hwnd, GWL_STYLE); style &= ~(WS_CHILD | BS_NOTIFY); @@ -807,6 +822,395 @@ 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 test_bm_get_set_image(void) +{ + HWND hwnd; + HDC hdc; + HBITMAP hbmp1x1; + HBITMAP hbmp2x2; + HBITMAP hmask2x2; + ICONINFO icon_info2x2; + HICON hicon2x2; + HBITMAP hbmp; + HICON hicon; + ICONINFO icon_info; + BITMAP bm; + static const DWORD default_style = BS_PUSHBUTTON | WS_TABSTOP | WS_POPUP | WS_VISIBLE; + + hdc = GetDC(0); + hbmp1x1 = CreateCompatibleBitmap(hdc, 1, 1); + hbmp2x2 = CreateCompatibleBitmap(hdc, 2, 2); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(hbmp1x1, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1, + bm.bmWidth, bm.bmHeight); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(hbmp2x2, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2, + bm.bmWidth, bm.bmHeight); + + hmask2x2 = CreateCompatibleBitmap(hdc, 2, 2); + ZeroMemory(&icon_info2x2, sizeof(icon_info2x2)); + icon_info2x2.fIcon = TRUE; + icon_info2x2.hbmMask = hmask2x2; + icon_info2x2.hbmColor = hbmp2x2; + hicon2x2 = CreateIconIndirect(&icon_info2x2); + ok(hicon2x2 !=NULL, "Expect CreateIconIndirect() success\n"); + + ZeroMemory(&icon_info, sizeof(icon_info)); + ok(GetIconInfo(hicon2x2, &icon_info), "Expect GetIconInfo() success\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2, + bm.bmWidth, bm.bmHeight); + DeleteObject(icon_info.hbmColor); + DeleteObject(icon_info.hbmMask); + + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0, + 0, 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + /* Get image when image is not set */ + hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0); + ok(hbmp == 0, "Expect hbmp == 0\n"); + /* Set image */ + hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp1x1); + ok(hbmp == 0, "Expect hbmp == 0\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0); + ok(hbmp != 0, "Expect hbmp != 0\n"); + /* Set null resets image */ + hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, 0); + ok(hbmp != 0, "Expect hbmp != 0\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0); + ok(hbmp == 0, "Expect hbmp == 0\n"); + DestroyWindow(hwnd); + + /* Set bitmap with BS_BITMAP */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0, + 0, 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp1x1); + ok(hbmp == 0, "Expect hbmp == 0\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0); + ok(hbmp != 0, "Expect hbmp != 0\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1, + bm.bmWidth, bm.bmHeight); + DestroyWindow(hwnd); + + /* Set bitmap without BS_BITMAP */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style, 0, 0, 100, 100, 0, 0, 0, 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp1x1); + ok(hbmp == 0, "Expect hbmp == 0\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0); + if (hbmp == 0) + { + /* on xp or 2003*/ + win_skip("Show both image and text is not supported. Skip following tests.\n"); + DestroyWindow(hwnd); + goto done; + } + ok(hbmp != 0, "Expect hbmp != 0\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1, + bm.bmWidth, bm.bmHeight); + DestroyWindow(hwnd); + + /* Set icon with BS_ICON */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0, + 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon2x2); + ok(hicon == 0, "Expect hicon == 0\n"); + hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0); + ok(hicon != 0, "Expect hicon != 0\n"); + ZeroMemory(&icon_info, sizeof(icon_info)); + ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2, + bm.bmWidth, bm.bmHeight); + DeleteObject(icon_info.hbmColor); + DeleteObject(icon_info.hbmMask); + DestroyWindow(hwnd); + + /* Set icon without BS_ICON */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style, 0, 0, 100, 100, 0, 0, 0, 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon2x2); + ok(hicon == 0, "Expect hicon == 0\n"); + hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0); + ok(hicon != 0, "Expect hicon != 0\n"); + ZeroMemory(&icon_info, sizeof(icon_info)); + ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2, + bm.bmWidth, bm.bmHeight); + DeleteObject(icon_info.hbmColor); + DeleteObject(icon_info.hbmMask); + DestroyWindow(hwnd); + + /* Set icon with BS_BITMAP */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0, + 0, 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon2x2); + ok(hicon == 0, "Expect hicon == 0\n"); + hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0); + ok(hicon != 0, "Expect hicon != 0\n"); + ZeroMemory(&icon_info, sizeof(icon_info)); + ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2, + bm.bmWidth, bm.bmHeight); + DeleteObject(icon_info.hbmColor); + DeleteObject(icon_info.hbmMask); + DestroyWindow(hwnd); + + /* Set bitmap with BS_ICON */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0, + 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp1x1); + ok(hbmp == 0, "Expect hbmp == 0\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0); + ok(hbmp != 0, "Expect hbmp != 0\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1, + bm.bmWidth, bm.bmHeight); + DestroyWindow(hwnd); + + /* Set bitmap with BS_BITMAP and IMAGE_ICON*/ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0, + 0, 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hbmp1x1); + ok(hbmp == 0, "Expect hbmp == 0\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0); + ok(hbmp != 0, "Expect hbmp != 0\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1, + bm.bmWidth, bm.bmHeight); + DestroyWindow(hwnd); + + /* Set icon with BS_ICON and IMAGE_BITMAP */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0, + 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hicon2x2); + ok(hicon == 0, "Expect hicon == 0\n"); + hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0); + ok(hicon != 0, "Expect hicon != 0\n"); + ZeroMemory(&icon_info, sizeof(icon_info)); + ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(icon_info.hbmColor, sizeof(BITMAP), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2, + bm.bmWidth, bm.bmHeight); + DeleteObject(icon_info.hbmColor); + DeleteObject(icon_info.hbmMask); + DestroyWindow(hwnd); + + /* Set bitmap with BS_ICON and IMAGE_ICON */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0, 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hbmp1x1); + ok(hbmp == 0, "Expect hbmp == 0\n"); + hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0); + ok(hbmp != 0, "Expect hbmp != 0\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1, + bm.bmWidth, bm.bmHeight); + DestroyWindow(hwnd); + + /* Set icon with BS_BITMAP and IMAGE_BITMAP */ + hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0, 0, 0); + ok(hwnd != NULL, "Expect hwnd to be not NULL\n"); + hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hicon2x2); + ok(hicon == 0, "Expect hicon == 0\n"); + hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0); + ok(hicon != 0, "Expect hicon != 0\n"); + ZeroMemory(&icon_info, sizeof(icon_info)); + ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n"); + ZeroMemory(&bm, sizeof(bm)); + ok(GetObjectW(icon_info.hbmColor, sizeof(BITMAP), &bm), "Expect GetObjectW() success\n"); + ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2, + bm.bmWidth, bm.bmHeight); + DeleteObject(icon_info.hbmColor); + DeleteObject(icon_info.hbmMask); + DestroyWindow(hwnd); + +done: + DestroyIcon(hicon2x2); + DeleteObject(hmask2x2); + DeleteObject(hbmp2x2); + DeleteObject(hbmp1x1); + ReleaseDC(0, hdc); +} + static void register_parent_class(void) { WNDCLASSA cls; @@ -824,6 +1228,504 @@ static void register_parent_class(void) RegisterClassA(&cls); } +static void test_button_data(void) +{ + static const DWORD styles[] = + { + BS_PUSHBUTTON, + BS_DEFPUSHBUTTON, + BS_CHECKBOX, + BS_AUTOCHECKBOX, + BS_RADIOBUTTON, + BS_3STATE, + BS_AUTO3STATE, + BS_GROUPBOX, + BS_USERBUTTON, + BS_AUTORADIOBUTTON, + BS_OWNERDRAW, + BS_SPLITBUTTON, + BS_DEFSPLITBUTTON, + BS_COMMANDLINK, + BS_DEFCOMMANDLINK, + }; + + struct button_desc + { + HWND self; + HWND parent; + LONG style; + }; + unsigned int i; + HWND parent; + + parent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 100, 100, 200, 200, 0, 0, 0, NULL); + ok(parent != 0, "Failed to create parent window\n"); + + for (i = 0; i < ARRAY_SIZE(styles); i++) + { + struct button_desc *desc; + HWND hwnd; + + hwnd = create_button(styles[i], parent); + ok(hwnd != NULL, "Failed to create a button.\n"); + + desc = (void *)GetWindowLongPtrA(hwnd, 0); + ok(desc != NULL, "Expected window data.\n"); + + if (desc) + { + ok(desc->self == hwnd, "Unexpected 'self' field.\n"); + ok(desc->parent == parent, "Unexpected 'parent' field.\n"); + ok(desc->style == (WS_CHILD | BS_NOTIFY | styles[i]), "Unexpected 'style' field.\n"); + } + + DestroyWindow(hwnd); + } + + DestroyWindow(parent); +} + +static void test_get_set_imagelist(void) +{ + HWND hwnd; + HIMAGELIST himl; + BUTTON_IMAGELIST biml = {0}; + HDC hdc; + HBITMAP hbmp; + INT width = 16; + INT height = 16; + INT index; + DWORD type; + BOOL ret; + + hdc = GetDC(0); + hbmp = CreateCompatibleBitmap(hdc, width, height); + ok(hbmp != NULL, "Expect hbmp not null\n"); + + himl = pImageList_Create(width, height, ILC_COLOR, 1, 0); + ok(himl != NULL, "Expect himl not null\n"); + index = pImageList_Add(himl, hbmp, NULL); + ok(index == 0, "Expect index == 0\n"); + DeleteObject(hbmp); + ReleaseDC(0, hdc); + + for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++) + { + hwnd = create_button(type, NULL); + ok(hwnd != NULL, "Expect hwnd not null\n"); + + /* Get imagelist when imagelist is unset yet */ + ret = SendMessageA(hwnd, BCM_GETIMAGELIST, 0, (LPARAM)&biml); + ok(ret, "Expect BCM_GETIMAGELIST return true\n"); + ok(biml.himl == 0 && IsRectEmpty(&biml.margin) && biml.uAlign == 0, + "Expect BUTTON_IMAGELIST is empty\n"); + + /* Set imagelist with himl null */ + biml.himl = 0; + biml.uAlign = BUTTON_IMAGELIST_ALIGN_CENTER; + ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml); + ok(ret || broken(!ret), /* xp or 2003 */ + "Expect BCM_SETIMAGELIST return true\n"); + + /* Set imagelist with uAlign invalid */ + biml.himl = himl; + biml.uAlign = -1; + ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml); + ok(ret, "Expect BCM_SETIMAGELIST return true\n"); + + /* Successful get and set imagelist */ + biml.himl = himl; + biml.uAlign = BUTTON_IMAGELIST_ALIGN_CENTER; + ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml); + ok(ret, "Expect BCM_SETIMAGELIST return true\n"); + ret = SendMessageA(hwnd, BCM_GETIMAGELIST, 0, (LPARAM)&biml); + ok(ret, "Expect BCM_GETIMAGELIST return true\n"); + ok(biml.himl == himl, "Expect himl to be same\n"); + ok(biml.uAlign == BUTTON_IMAGELIST_ALIGN_CENTER, "Expect uAlign to be %x\n", + BUTTON_IMAGELIST_ALIGN_CENTER); + + /* BCM_SETIMAGELIST null pointer handling */ + ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, 0); + ok(!ret, "Expect BCM_SETIMAGELIST return false\n"); + ret = SendMessageA(hwnd, BCM_GETIMAGELIST, 0, (LPARAM)&biml); + ok(ret, "Expect BCM_GETIMAGELIST return true\n"); + ok(biml.himl == himl, "Expect himl to be same\n"); + + /* BCM_GETIMAGELIST null pointer handling */ + biml.himl = himl; + biml.uAlign = BUTTON_IMAGELIST_ALIGN_CENTER; + ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml); + ok(ret, "Expect BCM_SETIMAGELIST return true\n"); + ret = SendMessageA(hwnd, BCM_GETIMAGELIST, 0, 0); + ok(!ret, "Expect BCM_GETIMAGELIST return false\n"); + + DestroyWindow(hwnd); + } + + pImageList_Destroy(himl); +} + +static void test_get_set_textmargin(void) +{ + HWND hwnd; + RECT margin_in; + RECT margin_out; + BOOL ret; + DWORD type; + + SetRect(&margin_in, 2, 1, 3, 4); + for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++) + { + hwnd = create_button(type, NULL); + ok(hwnd != NULL, "Expect hwnd not null\n"); + + /* Get text margin when it is unset */ + ret = SendMessageA(hwnd, BCM_GETTEXTMARGIN, 0, (LPARAM)&margin_out); + ok(ret, "Expect ret to be true\n"); + ok(IsRectEmpty(&margin_out), "Expect margin empty\n"); + + /* Successful get and set text margin */ + ret = SendMessageA(hwnd, BCM_SETTEXTMARGIN, 0, (LPARAM)&margin_in); + ok(ret, "Expect ret to be true\n"); + SetRectEmpty(&margin_out); + ret = SendMessageA(hwnd, BCM_GETTEXTMARGIN, 0, (LPARAM)&margin_out); + ok(ret, "Expect ret to be true\n"); + ok(EqualRect(&margin_in, &margin_out), "Expect margins to be equal\n"); + + /* BCM_SETTEXTMARGIN null pointer handling */ + ret = SendMessageA(hwnd, BCM_SETTEXTMARGIN, 0, 0); + ok(!ret, "Expect ret to be false\n"); + SetRectEmpty(&margin_out); + ret = SendMessageA(hwnd, BCM_GETTEXTMARGIN, 0, (LPARAM)&margin_out); + ok(ret, "Expect ret to be true\n"); + ok(EqualRect(&margin_in, &margin_out), "Expect margins to be equal\n"); + + /* BCM_GETTEXTMARGIN null pointer handling */ + ret = SendMessageA(hwnd, BCM_SETTEXTMARGIN, 0, (LPARAM)&margin_in); + ok(ret, "Expect ret to be true\n"); + ret = SendMessageA(hwnd, BCM_GETTEXTMARGIN, 0, 0); + ok(!ret, "Expect ret to be true\n"); + + DestroyWindow(hwnd); + } +} + +static void test_state(void) +{ + HWND hwnd; + DWORD type; + LONG state; + + /* Initial button state */ + for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++) + { + hwnd = create_button(type, NULL); + state = SendMessageA(hwnd, BM_GETSTATE, 0, 0); + ok(state == BST_UNCHECKED, "Expect state 0x%08x, got 0x%08x\n", BST_UNCHECKED, state); + DestroyWindow(hwnd); + } +} + +static void test_bcm_get_ideal_size(void) +{ + static const char *button_text2 = "WWWW\nWWWW"; + static const char *button_text = "WWWW"; + static const DWORD imagelist_aligns[] = {BUTTON_IMAGELIST_ALIGN_LEFT, BUTTON_IMAGELIST_ALIGN_RIGHT, + BUTTON_IMAGELIST_ALIGN_TOP, BUTTON_IMAGELIST_ALIGN_BOTTOM, + BUTTON_IMAGELIST_ALIGN_CENTER}; + static const DWORD aligns[] = {0, BS_TOP, BS_LEFT, BS_RIGHT, BS_BOTTOM, + BS_CENTER, BS_VCENTER, BS_RIGHTBUTTON, WS_EX_RIGHT}; + DWORD default_style = WS_TABSTOP | WS_POPUP | WS_VISIBLE; + const LONG client_width = 400, client_height = 200; + LONG image_width, height, line_count, text_width; + HFONT hfont, prev_font; + DWORD style, type; + BOOL ret; + HWND hwnd; + HDC hdc; + LOGFONTA lf; + TEXTMETRICA tm; + SIZE size; + HBITMAP hmask, hbmp; + ICONINFO icon_info; + HICON hicon; + HIMAGELIST himl; + BUTTON_IMAGELIST biml = {0}; + RECT rect; + INT i, j; + + /* Check for NULL pointer handling */ + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_PUSHBUTTON | default_style, 0, 0, client_width, client_height, + NULL, NULL, 0, NULL); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, 0); + ok(!ret, "Expect BCM_GETIDEALSIZE message to return false.\n"); + + /* Set font so that the test is consistent on Wine and Windows */ + ZeroMemory(&lf, sizeof(lf)); + lf.lfWeight = FW_NORMAL; + lf.lfHeight = 20; + lstrcpyA(lf.lfFaceName, "Tahoma"); + hfont = CreateFontIndirectA(&lf); + ok(hfont != NULL, "Failed to create test font.\n"); + + /* Get tmHeight */ + hdc = GetDC(hwnd); + prev_font = SelectObject(hdc, hfont); + GetTextMetricsA(hdc, &tm); + SelectObject(hdc, prev_font); + DrawTextA(hdc, button_text, -1, &rect, DT_CALCRECT); + text_width = rect.right - rect.left; + ReleaseDC(hwnd, hdc); + DestroyWindow(hwnd); + + /* XP and 2003 doesn't support command links, getting ideal size with button having only text returns client size on these platforms. */ + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFCOMMANDLINK | default_style, 0, 0, client_width, client_height, NULL, + NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + if (size.cx == client_width && size.cy == client_height) + { + /* on XP and 2003, buttons with image are not supported */ + win_skip("Skipping further tests on XP and 2003\n"); + return; + } + + /* Tests for image placements */ + /* Prepare bitmap */ + image_width = 48; + height = 48; + hdc = GetDC(0); + hmask = CreateCompatibleBitmap(hdc, image_width, height); + hbmp = CreateCompatibleBitmap(hdc, image_width, height); + + /* Only bitmap for push button, ideal size should be enough for image and text */ + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFPUSHBUTTON | BS_BITMAP | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + /* Ideal size contains text rect even show bitmap only */ + ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width, + size.cy, max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Image alignments when button has bitmap and text*/ + for (i = 0; i < ARRAY_SIZE(aligns); i++) + for (j = 0; j < ARRAY_SIZE(aligns); j++) + { + style = BS_DEFPUSHBUTTON | default_style | aligns[i] | aligns[j]; + hwnd = CreateWindowA(WC_BUTTONA, button_text, style, 0, 0, client_width, client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + if (!(style & (BS_CENTER | BS_VCENTER)) || ((style & BS_CENTER) && (style & BS_CENTER) != BS_CENTER) + || !(style & BS_VCENTER) || (style & BS_VCENTER) == BS_VCENTER) + ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)), + "Style: 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", style, size.cx, + image_width + text_width, size.cy, max(height, tm.tmHeight)); + else + ok((size.cx >= max(text_width, height) && size.cy >= height + tm.tmHeight), + "Style: 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", style, size.cx, + max(text_width, height), size.cy, height + tm.tmHeight); + DestroyWindow(hwnd); + } + + /* Image list alignments */ + himl = pImageList_Create(image_width, height, ILC_COLOR, 1, 1); + pImageList_Add(himl, hbmp, 0); + biml.himl = himl; + for (i = 0; i < ARRAY_SIZE(imagelist_aligns); i++) + { + biml.uAlign = imagelist_aligns[i]; + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFPUSHBUTTON | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + if (biml.uAlign == BUTTON_IMAGELIST_ALIGN_TOP || biml.uAlign == BUTTON_IMAGELIST_ALIGN_BOTTOM) + ok((size.cx >= max(text_width, height) && size.cy >= height + tm.tmHeight), + "Align:%d expect ideal cx %d >= %d and ideal cy %d >= %d\n", biml.uAlign, size.cx, + max(text_width, height), size.cy, height + tm.tmHeight); + else if (biml.uAlign == BUTTON_IMAGELIST_ALIGN_LEFT || biml.uAlign == BUTTON_IMAGELIST_ALIGN_RIGHT) + ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)), + "Align:%d expect ideal cx %d >= %d and ideal cy %d >= %d\n", biml.uAlign, size.cx, + image_width + text_width, size.cy, max(height, tm.tmHeight)); + else + ok(size.cx >= image_width && size.cy >= height, "Align:%d expect ideal cx %d >= %d and ideal cy %d >= %d\n", + biml.uAlign, size.cx, image_width, size.cy, height); + DestroyWindow(hwnd); + } + + /* Icon as image */ + /* Create icon from bitmap */ + ZeroMemory(&icon_info, sizeof(icon_info)); + icon_info.fIcon = TRUE; + icon_info.hbmMask = hmask; + icon_info.hbmColor = hbmp; + hicon = CreateIconIndirect(&icon_info); + + /* Only icon, ideal size should be enough for image and text */ + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFPUSHBUTTON | BS_ICON | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hicon); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + /* Ideal size contains text rect even show icons only */ + ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width, size.cy, + max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Show icon and text */ + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFPUSHBUTTON | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hicon); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width, size.cy, + max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Checkbox */ + /* Both bitmap and text for checkbox, ideal size is only enough for text because it doesn't support image(but not image list)*/ + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_AUTOCHECKBOX | default_style, 0, 0, client_width, client_height, + NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + ok((size.cx <= image_width + text_width && size.cx >= text_width && size.cy <= max(height, tm.tmHeight) + && size.cy >= tm.tmHeight), + "Expect ideal cx %d within range (%d, %d ) and ideal cy %d within range (%d, %d )\n", size.cx, + text_width, image_width + text_width, size.cy, tm.tmHeight, max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Both image list and text for checkbox, ideal size should have enough for image list and text */ + biml.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT; + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_AUTOCHECKBOX | BS_BITMAP | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width, size.cy, + max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Only bitmap for checkbox, ideal size should have enough for image and text */ + hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_AUTOCHECKBOX | BS_BITMAP | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width, size.cy, + max(height, tm.tmHeight)); + DestroyWindow(hwnd); + + /* Test button with only text */ + /* No text */ + for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++) + { + style = type | default_style; + hwnd = CreateWindowA(WC_BUTTONA, "", style, 0, 0, client_width, client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + + if (type == BS_COMMANDLINK || type == BS_DEFCOMMANDLINK) + { + todo_wine ok((size.cx == 0 && size.cy > 0), "Style 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", + style, size.cx, 0, size.cy, 0); + } + else + { + ok(size.cx == client_width && size.cy == client_height, + "Style 0x%08x expect size.cx == %d and size.cy == %d, got size.cx: %d size.cy: %d\n", style, + client_width, client_height, size.cx, size.cy); + } + DestroyWindow(hwnd); + } + + /* Single line and multiple lines text */ + for (line_count = 1; line_count <= 2; line_count++) + { + for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++) + { + style = line_count > 1 ? type | BS_MULTILINE : type; + style |= default_style; + + hwnd = CreateWindowA(WC_BUTTONA, (line_count == 2 ? button_text2 : button_text), style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + ZeroMemory(&size, sizeof(size)); + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n"); + + if (type == BS_3STATE || type == BS_AUTO3STATE || type == BS_GROUPBOX || type == BS_PUSHBOX + || type == BS_OWNERDRAW) + { + ok(size.cx == client_width && size.cy == client_height, + "Style 0x%08x expect ideal size (%d,%d), got (%d,%d)\n", style, client_width, client_height, size.cx, + size.cy); + } + else if (type == BS_COMMANDLINK || type == BS_DEFCOMMANDLINK) + { + todo_wine ok((size.cx == 0 && size.cy > 0), + "Style 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", style, size.cx, 0, + size.cy, 0); + } + else + { + height = line_count == 2 ? 2 * tm.tmHeight : tm.tmHeight; + ok(size.cx >= 0 && size.cy >= height, "Style 0x%08x expect ideal cx %d >= 0 and ideal cy %d >= %d\n", + style, size.cx, size.cy, height); + } + DestroyWindow(hwnd); + } + } + + pImageList_Destroy(himl); + DestroyIcon(hicon); + DeleteObject(hbmp); + DeleteObject(hmask); + ReleaseDC(0, hdc); + DeleteObject(hfont); +} + START_TEST(button) { ULONG_PTR ctx_cookie; @@ -839,6 +1741,13 @@ START_TEST(button) test_button_class(); test_button_messages(); + test_note(); + test_button_data(); + test_bm_get_set_image(); + test_get_set_imagelist(); + test_get_set_textmargin(); + test_state(); + test_bcm_get_ideal_size(); unload_v6_module(ctx_cookie, hCtx); } diff --git a/modules/rostests/winetests/comctl32/combo.c b/modules/rostests/winetests/comctl32/combo.c index 83b36f212fd..923d826b305 100644 --- a/modules/rostests/winetests/comctl32/combo.c +++ b/modules/rostests/winetests/comctl32/combo.c @@ -264,7 +264,7 @@ static void test_comboex_WM_LBUTTONDOWN(void) WS_VISIBLE|WS_CHILD|CBS_DROPDOWN, 0, 0, 200, 150, hComboExParentWnd, NULL, hMainHinst, NULL); - for (i = 0; i < sizeof(choices)/sizeof(UINT); i++){ + for (i = 0; i < ARRAY_SIZE(choices); i++){ COMBOBOXEXITEMW cbexItem; wsprintfW(buffer, stringFormat, choices[i]); @@ -1165,11 +1165,11 @@ static void test_combo_dropdown_size(DWORD style) int limit; } info_height[] = { {33, 50, -1}, - {35, 50, 40}, + {35, 100, 40}, {15, 50, 3}, }; - for (test = 0; test < sizeof(info_height) / sizeof(info_height[0]); test++) + for (test = 0; test < ARRAY_SIZE(info_height); test++) { const struct list_size_info *info_test = &info_height[test]; int height_item; /* Height of a list item */ @@ -1182,7 +1182,6 @@ static void test_combo_dropdown_size(DWORD style) info_test->height_combo, hMainWnd, (HMENU)COMBO_ID, NULL, 0); min_visible_expected = SendMessageA(hCombo, CB_GETMINVISIBLE, 0, 0); - todo_wine ok(min_visible_expected == 30, "Unexpected number of items %d.\n", min_visible_expected); cbInfo.cbSize = sizeof(COMBOBOXINFO); @@ -1202,10 +1201,8 @@ static void test_combo_dropdown_size(DWORD style) min_visible_expected = info_test->limit; ret = SendMessageA(hCombo, CB_SETMINVISIBLE, min_visible_expected, 0); - todo_wine ok(ret, "Failed to set visible limit.\n"); min_visible_actual = SendMessageA(hCombo, CB_GETMINVISIBLE, 0, 0); - todo_wine ok(min_visible_expected == min_visible_actual, "test %d: unexpected number of items %d.\n", test, min_visible_actual); } @@ -1242,7 +1239,6 @@ static void test_combo_dropdown_size(DWORD style) if (expected_height_list < 0) expected_height_list = 0; - todo_wine ok(expected_height_list == height_list, "Test %d, expected list height to be %d, got %d\n", test, expected_height_list, height_list); } @@ -1250,7 +1246,6 @@ static void test_combo_dropdown_size(DWORD style) { expected_height_list = min(info_test->num_items, min_visible_expected) * height_item; - todo_wine ok(expected_height_list == height_list, "Test %d, expected list height to be %d, got %d\n", test, expected_height_list, height_list); } diff --git a/modules/rostests/winetests/comctl32/datetime.c b/modules/rostests/winetests/comctl32/datetime.c index 7742dc5d183..88abce9ed61 100644 --- a/modules/rostests/winetests/comctl32/datetime.c +++ b/modules/rostests/winetests/comctl32/datetime.c @@ -721,6 +721,46 @@ static void test_dtm_set_and_get_systemtime_with_limits(void) DestroyWindow(hWnd); } +static void test_dtm_get_ideal_size(void) +{ + HWND hwnd; + HDC hdc; + HFONT hfont; + LOGFONTA lf; + TEXTMETRICA tm; + SIZE size; + BOOL r; + + hwnd = create_datetime_control(0); + r = SendMessageA(hwnd, DTM_GETIDEALSIZE, 0, (LPARAM)&size); + if (!r) + { + win_skip("DTM_GETIDEALSIZE is not available\n"); + DestroyWindow(hwnd); + return; + } + + /* Set font so that the test is consistent on Wine and Windows */ + ZeroMemory(&lf, sizeof(lf)); + lf.lfWeight = FW_NORMAL; + lf.lfHeight = 20; + lstrcpyA(lf.lfFaceName, "Tahoma"); + hfont = CreateFontIndirectA(&lf); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE); + + hdc = GetDC(hwnd); + GetTextMetricsA(hdc, &tm); + ReleaseDC(hwnd, hdc); + + r = SendMessageA(hwnd, DTM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(r, "Expect DTM_GETIDEALSIZE message to return true\n"); + ok(size.cx > 0 && size.cy >= tm.tmHeight, + "Expect size.cx > 0 and size.cy >= %d, got cx:%d cy:%d\n", tm.tmHeight, size.cx, size.cy); + + DestroyWindow(hwnd); + DeleteObject(hfont); +} + static void test_wm_set_get_text(void) { static const CHAR a_str[] = "a"; @@ -821,6 +861,7 @@ START_TEST(datetime) test_dtm_set_and_get_mccolor(); test_dtm_set_and_get_mcfont(); test_dtm_get_monthcal(); + test_dtm_get_ideal_size(); test_wm_set_get_text(); test_dts_shownone(); diff --git a/modules/rostests/winetests/comctl32/edit.c b/modules/rostests/winetests/comctl32/edit.c index 7e9e7e76348..bc44d0c2404 100644 --- a/modules/rostests/winetests/comctl32/edit.c +++ b/modules/rostests/winetests/comctl32/edit.c @@ -2969,7 +2969,7 @@ static void test_EM_GETLINE(void) hwnd[0] = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); hwnd[1] = create_editcontrolW(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); - for (i = 0; i < sizeof(hwnd)/sizeof(hwnd[0]); i++) + for (i = 0; i < ARRAY_SIZE(hwnd); i++) { static const WCHAR strW[] = {'t','e','x','t',0}; static const char *str = "text"; @@ -2994,13 +2994,13 @@ static void test_EM_GETLINE(void) ok(!strcmp(buff, str), "Unexpected line data %s.\n", buff); memset(buffW, 0, sizeof(buffW)); - *(WORD *)buffW = sizeof(buffW)/sizeof(buffW[0]); + *(WORD *)buffW = ARRAY_SIZE(buffW); r = SendMessageW(hwnd[i], EM_GETLINE, 0, (LPARAM)buffW); ok(r == lstrlenW(strW), "Failed to get a line %d.\n", r); ok(!lstrcmpW(buffW, strW), "Unexpected line data %s.\n", wine_dbgstr_w(buffW)); memset(buffW, 0, sizeof(buffW)); - *(WORD *)buffW = sizeof(buffW)/sizeof(buffW[0]); + *(WORD *)buffW = ARRAY_SIZE(buffW); r = SendMessageW(hwnd[i], EM_GETLINE, 1, (LPARAM)buffW); ok(r == lstrlenW(strW), "Failed to get a line %d.\n", r); ok(!lstrcmpW(buffW, strW), "Unexpected line data %s.\n", wine_dbgstr_w(buffW)); @@ -3061,6 +3061,83 @@ static const struct message killfocus_combined_seq[] = { 0 } }; +static void test_cue_banner(void) +{ + HWND hwnd_edit; + BOOL ret; + static WCHAR getcuetestW[5] = {'T',0}; + static const WCHAR testcmp1W[] = {'T','e','s','t',0}; + static const WCHAR testcmp2W[] = {'T','e','s',0}; + static const WCHAR emptyW[] = {0}; + + hwnd_edit = create_editcontrolW(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, (WPARAM)getcuetestW, 5); + if (lstrcmpW(getcuetestW, emptyW) != 0) + { + win_skip("skipping for Win XP and 2003 Server.\n"); + DestroyWindow(hwnd_edit); + return; + } + ok(lstrcmpW(getcuetestW, emptyW) == 0, "First char is %c\n", getcuetestW[0]); + ok(ret == FALSE, "EM_GETCUEBANNER should have returned FALSE.\n"); + + lstrcpyW(getcuetestW, testcmp1W); + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, (WPARAM)getcuetestW, 0); + ok(lstrcmpW(getcuetestW, testcmp1W) == 0, "String was %s.\n", wine_dbgstr_w(getcuetestW)); + ok(ret == FALSE, "EM_GETCUEBANNER should have returned FALSE.\n"); + + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, 0, 0); + ok(ret == FALSE, "EM_GETCUEBANNER should have returned FALSE.\n"); + + ret = SendMessageW(hwnd_edit, EM_SETCUEBANNER, 0, 0); + ok(ret == FALSE, "EM_SETCUEBANNER should have returned FALSE.\n"); + + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, 0, 0); + ok(ret == FALSE, "EM_GETCUEBANNER should have returned FALSE.\n"); + + lstrcpyW(getcuetestW, testcmp1W); + ret = SendMessageW(hwnd_edit, EM_SETCUEBANNER, 0, (LPARAM)getcuetestW); + ok(ret == TRUE, "EM_SETCUEBANNER should have returned TRUE.\n"); + + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, 0, 5); + ok(ret == TRUE, "EM_GETCUEBANNER should have returned TRUE.\n"); + + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, (WPARAM)getcuetestW, 5); + ok(ret == TRUE, "EM_GETCUEBANNER should have returned TRUE.\n"); + ok(lstrcmpW(getcuetestW, testcmp1W) == 0, "EM_GETCUEBANNER returned string %s.\n", wine_dbgstr_w(getcuetestW)); + + ret = SendMessageW(hwnd_edit, EM_SETCUEBANNER, 0, (LPARAM)emptyW); + ok(ret == TRUE, "EM_SETCUEBANNER should have returned TRUE.\n"); + + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, (WPARAM)getcuetestW, 5); + ok(ret == TRUE, "EM_GETCUEBANNER should have returned TRUE.\n"); + ok(lstrcmpW(getcuetestW, emptyW) == 0, "EM_GETCUEBANNER returned string %s.\n", wine_dbgstr_w(getcuetestW)); + + /* EM_GETCUEBANNER's buffer size includes null char */ + ret = SendMessageW(hwnd_edit, EM_SETCUEBANNER, 0, (LPARAM)testcmp1W); + ok(ret == TRUE, "EM_SETCUEBANNER should have returned TRUE.\n"); + memset(getcuetestW, 0, lstrlenW(testcmp1W)*sizeof(WCHAR)); + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, (WPARAM)getcuetestW, (LPARAM)lstrlenW(testcmp1W)+1); + ok(ret == TRUE, "EM_GETCUEBANNER should have returned TRUE.\n"); + ok(lstrcmpW(getcuetestW, testcmp1W) == 0, "EM_GETCUEBANNER returned string %s.\n", wine_dbgstr_w(getcuetestW)); + memset(getcuetestW, 0, lstrlenW(testcmp1W)*sizeof(WCHAR)); + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, (WPARAM)getcuetestW, (LPARAM)lstrlenW(testcmp1W)); + ok(lstrcmpW(getcuetestW, testcmp2W) == 0, "EM_GETCUEBANNER returned string %s.\n", wine_dbgstr_w(getcuetestW)); + DestroyWindow(hwnd_edit); + + /* setting cue banner fails for multi-line edit controls */ + hwnd_edit = create_editcontrolW(ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE, 0); + lstrcpyW(getcuetestW, testcmp1W); + ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, (WPARAM)getcuetestW, 5); + ok(ret == FALSE, "EM_SETCUEBANNER.\n"); + ok(lstrcmpW(getcuetestW, testcmp1W) == 0, "String was %s.\n", wine_dbgstr_w(getcuetestW)); + ret = SendMessageW(hwnd_edit, EM_SETCUEBANNER, 0, (LPARAM)getcuetestW); + ok(ret == FALSE, "EM_SETCUEBANNER.\n"); + + DestroyWindow(hwnd_edit); +} + static void test_change_focus(void) { HWND hwnd, parent_wnd; @@ -3138,6 +3215,7 @@ START_TEST(edit) test_EM_GETLINE(); test_wordbreak_proc(); test_change_focus(); + test_cue_banner(); UnregisterWindowClasses(); diff --git a/modules/rostests/winetests/comctl32/header.c b/modules/rostests/winetests/comctl32/header.c index 7ea80519a65..3428936cd75 100644 --- a/modules/rostests/winetests/comctl32/header.c +++ b/modules/rostests/winetests/comctl32/header.c @@ -1130,7 +1130,7 @@ static void test_hdm_index_messages(HWND hParent) ok_sequence(sequences, PARENT_SEQ_INDEX, add_header_to_parent_seq, "adder header control to parent", FALSE); flush_sequences(sequences, NUM_MSG_SEQUENCES); - for (i = 0; i < sizeof(item_texts)/sizeof(item_texts[0]); i++) + for (i = 0; i < ARRAY_SIZE(item_texts); i++) { hdItem.mask = HDI_TEXT | HDI_WIDTH | HDI_FORMAT; hdItem.pszText = (char*)item_texts[i]; @@ -1170,7 +1170,7 @@ static void test_hdm_index_messages(HWND hParent) hdItem.mask = HDI_TEXT | HDI_WIDTH; hdItem.pszText = buffA; - hdItem.cchTextMax = sizeof(buffA)/sizeof(buffA[0]); + hdItem.cchTextMax = ARRAY_SIZE(buffA); retVal = SendMessageA(hChild, HDM_GETITEMA, 0, (LPARAM) &hdItem); ok(retVal == TRUE, "Getting the 1st header item should return TRUE, got %d\n", retVal); diff --git a/modules/rostests/winetests/comctl32/imagelist.c b/modules/rostests/winetests/comctl32/imagelist.c index bcd62c9e9c8..8064a2b98ea 100644 --- a/modules/rostests/winetests/comctl32/imagelist.c +++ b/modules/rostests/winetests/comctl32/imagelist.c @@ -922,7 +922,6 @@ static void check_ilhead_data(const ILHEAD *ilh, INT cx, INT cy, INT cur, INT ma } else { - grow = (WORD)(grow + 3) & ~3; ok(ilh->cMaxImage == max, "wrong cMaxImage %d (expected %d)\n", ilh->cMaxImage, max); ok(ilh->cGrow == grow_aligned, "Unexpected cGrow %d, expected %d\n", ilh->cGrow, grow_aligned); } @@ -1100,7 +1099,7 @@ static void image_list_init(HIMAGELIST himl, INT grow) check_iml_data(himl, BMP_CX, BMP_CX, 0, 2, grow, ILC_COLOR24, "total 0"); - for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) + for (i = 0; i < ARRAY_SIZE(td); i++) { image_list_add_bitmap(himl, td[i].grey, i + 1); check_iml_data(himl, td[i].cx, td[i].cy, td[i].cur, td[i].max, grow, td[i].bpp, td[i].comment); @@ -2029,7 +2028,11 @@ static void check_color_table(const char *name, HDC hdc, HIMAGELIST himl, UINT i { IMAGEINFO info; INT ret; +#ifdef __REACTOS__ char bmi_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors) + 256 * sizeof(RGBQUAD)]; +#else + char bmi_buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )]; +#endif BITMAPINFO *bmi = (BITMAPINFO *)bmi_buffer; int i, depth = ilc & 0xfe; @@ -2061,7 +2064,11 @@ static void check_color_table(const char *name, HDC hdc, HIMAGELIST himl, UINT i static void get_default_color_table(HDC hdc, int bpp, RGBQUAD *table) { +#ifdef __REACTOS__ char bmi_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors) + 256 * sizeof(RGBQUAD)]; +#else + char bmi_buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )]; +#endif BITMAPINFO *bmi = (BITMAPINFO *)bmi_buffer; HBITMAP tmp; int i; @@ -2109,7 +2116,11 @@ static void test_color_table(UINT ilc) { HIMAGELIST himl; INT ret; +#ifdef __REACTOS__ char bmi_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors) + 256 * sizeof(RGBQUAD)]; +#else + char bmi_buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )]; +#endif BITMAPINFO *bmi = (BITMAPINFO *)bmi_buffer; HDC hdc = CreateCompatibleDC(0); HBITMAP dib4, dib8, dib32; diff --git a/modules/rostests/winetests/comctl32/ipaddress.c b/modules/rostests/winetests/comctl32/ipaddress.c index 52581899a79..cc79b7bf11b 100644 --- a/modules/rostests/winetests/comctl32/ipaddress.c +++ b/modules/rostests/winetests/comctl32/ipaddress.c @@ -50,12 +50,12 @@ static void test_get_set_text(void) } /* check text just after creation */ - r = GetWindowTextA(hwnd, ip, sizeof(ip)/sizeof(CHAR)); + r = GetWindowTextA(hwnd, ip, ARRAY_SIZE(ip)); expect(7, r); ok(strcmp(ip, "0.0.0.0") == 0, "Expected null IP address, got %s\n", ip); SendMessageA(hwnd, IPM_SETADDRESS, 0, MAKEIPADDRESS(127, 0, 0, 1)); - r = GetWindowTextA(hwnd, ip, sizeof(ip)/sizeof(CHAR)); + r = GetWindowTextA(hwnd, ip, ARRAY_SIZE(ip)); expect(9, r); ok(strcmp(ip, "127.0.0.1") == 0, "Expected 127.0.0.1, got %s\n", ip); diff --git a/modules/rostests/winetests/comctl32/listbox.c b/modules/rostests/winetests/comctl32/listbox.c index 1513bea7f50..e7894839838 100644 --- a/modules/rostests/winetests/comctl32/listbox.c +++ b/modules/rostests/winetests/comctl32/listbox.c @@ -30,6 +30,52 @@ #include "wine/heap.h" #include "wine/test.h" #include "v6util.h" +#include "msg.h" + +enum seq_index +{ + LB_SEQ_INDEX, + PARENT_SEQ_INDEX, + NUM_MSG_SEQUENCES +}; + +static struct msg_sequence *sequences[NUM_MSG_SEQUENCES]; + +/* encoded MEASUREITEMSTRUCT into a WPARAM */ +typedef struct +{ + union + { + struct + { + UINT CtlType : 4; + UINT CtlID : 4; + UINT itemID : 4; + UINT wParam : 20; + } item; + WPARAM wp; + } u; +} MEASURE_ITEM_STRUCT; + +static unsigned hash_Ly_W(const WCHAR *str) +{ + unsigned hash = 0; + + for (; *str; str++) + hash = hash * 1664525u + (unsigned char)(*str) + 1013904223u; + + return hash; +} + +static unsigned hash_Ly(const char *str) +{ + unsigned hash = 0; + + for (; *str; str++) + hash = hash * 1664525u + (unsigned char)(*str) + 1013904223u; + + return hash; +} static const char * const strings[4] = { "First added", @@ -43,22 +89,53 @@ static const char * const strings[4] = { static const char BAD_EXTENSION[] = "*.badtxt"; -static int strcmp_aw(LPCWSTR strw, const char *stra) -{ - WCHAR buf[1024]; +#define ID_LISTBOX 1 - if (!stra) return 1; - MultiByteToWideChar(CP_ACP, 0, stra, -1, buf, sizeof(buf)/sizeof(WCHAR)); - return lstrcmpW(strw, buf); +static LRESULT WINAPI listbox_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA); + static LONG defwndproc_counter = 0; + struct message msg = { 0 }; + LRESULT ret; + + switch (message) + { + case WM_SIZE: + case WM_GETTEXT: + case WM_PAINT: + case WM_ERASEBKGND: + case WM_WINDOWPOSCHANGING: + case WM_WINDOWPOSCHANGED: + case WM_NCCALCSIZE: + case WM_NCPAINT: + case WM_NCHITTEST: + case WM_DEVICECHANGE: + break; + + default: + msg.message = message; + msg.flags = sent|wparam|lparam; + if (defwndproc_counter) msg.flags |= defwinproc; + msg.wParam = wParam; + msg.lParam = lParam; + add_message(sequences, LB_SEQ_INDEX, &msg); + } + + defwndproc_counter++; + ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam); + defwndproc_counter--; + + return ret; } static HWND create_listbox(DWORD add_style, HWND parent) { INT_PTR ctl_id = 0; + WNDPROC oldproc; HWND handle; if (parent) - ctl_id=1; + ctl_id = ID_LISTBOX; handle = CreateWindowA(WC_LISTBOXA, "TestList", (LBS_STANDARD & ~LBS_SORT) | add_style, 0, 0, 100, 100, parent, (HMENU)ctl_id, NULL, 0); @@ -69,6 +146,9 @@ static HWND create_listbox(DWORD add_style, HWND parent) SendMessageA(handle, LB_ADDSTRING, 0, (LPARAM) strings[2]); SendMessageA(handle, LB_ADDSTRING, 0, (LPARAM) strings[3]); + oldproc = (WNDPROC)SetWindowLongPtrA(handle, GWLP_WNDPROC, (LONG_PTR)listbox_wnd_proc); + SetWindowLongPtrA(handle, GWLP_USERDATA, (LONG_PTR)oldproc); + return handle; } @@ -84,7 +164,6 @@ struct listbox_stat struct listbox_test { - struct listbox_prop prop; struct listbox_stat init, init_todo; struct listbox_stat click, click_todo; struct listbox_stat step, step_todo; @@ -117,8 +196,7 @@ static void keypress(HWND handle, WPARAM keycode, BYTE scancode, BOOL extended) #define listbox_field_ok(t, s, f, got) \ ok (t.s.f==got.f, "style %#x, step " #s ", field " #f \ - ": expected %d, got %d\n", (unsigned int)t.prop.add_style, \ - t.s.f, got.f) + ": expected %d, got %d\n", style, t.s.f, got.f) #define listbox_todo_field_ok(t, s, f, got) \ todo_wine_if (t.s##_todo.f) { listbox_field_ok(t, s, f, got); } @@ -129,12 +207,23 @@ static void keypress(HWND handle, WPARAM keycode, BYTE scancode, BOOL extended) listbox_todo_field_ok(t, s, caret, got); \ listbox_todo_field_ok(t, s, selcount, got) -static void run_test(const struct listbox_test test) +static void run_test(DWORD style, const struct listbox_test test) { + static const struct message delete_seq[] = + { + { LB_DELETESTRING, sent|wparam|lparam, 0, 0 }, + { LB_DELETESTRING, sent|wparam|lparam, 0, 0 }, + { LB_DELETESTRING, sent|wparam|lparam, 0, 0 }, + { LB_DELETESTRING, sent|wparam|lparam, 0, 0 }, + { LB_RESETCONTENT, sent|wparam|lparam|defwinproc, 0, 0 }, + { 0 } + }; struct listbox_stat answer; - HWND hLB=create_listbox (test.prop.add_style, 0); + int i, res, count; RECT second_item; - int i, res; + HWND hLB; + + hLB = create_listbox (style, 0); listbox_query (hLB, &answer); listbox_ok (test, init, answer); @@ -152,13 +241,13 @@ static void run_test(const struct listbox_test test) DestroyWindow(hLB); - hLB = create_listbox(test.prop.add_style, 0); + hLB = create_listbox(style, 0); SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 2)); listbox_query(hLB, &answer); listbox_ok(test, sel, answer); - for (i = 0; i < 4; i++) + for (i = 0; i < 4 && !(style & LBS_NODATA); i++) { DWORD size = SendMessageA(hLB, LB_GETTEXTLEN, i, 0); int resA, resW; @@ -171,13 +260,9 @@ static void run_test(const struct listbox_test test) txtw = heap_alloc_zero((size + 1) * sizeof(*txtw)); resW = SendMessageW(hLB, LB_GETTEXT, i, (LPARAM)txtw); - if (resA != resW) - trace("SendMessageW(LB_GETTEXT) not supported on this platform (resA=%d resW=%d), skipping...\n", resA, resW); - else - { - WideCharToMultiByte(CP_ACP, 0, txtw, -1, txt, size, NULL, NULL); - ok(!strcmp (txt, strings[i]), "returned string for item %d does not match %s vs %s\n", i, txt, strings[i]); - } + ok(resA == resW, "Unexpected text length.\n"); + WideCharToMultiByte(CP_ACP, 0, txtw, -1, txt, size, NULL, NULL); + ok(!strcmp (txt, strings[i]), "Unexpected string for item %d, %s vs %s.\n", i, txt, strings[i]); heap_free(txtw); heap_free(txt); @@ -190,8 +275,17 @@ static void run_test(const struct listbox_test test) ok(res == LB_ERR, "Expected LB_ERR items, got %d\n", res); res = SendMessageA(hLB, LB_DELETESTRING, 4, 0); ok(res == LB_ERR, "Expected LB_ERR items, got %d\n", res); - res = SendMessageA(hLB, LB_GETCOUNT, 0, 0); - ok(res == 4, "Expected 4 items, got %d\n", res); + count = SendMessageA(hLB, LB_GETCOUNT, 0, 0); + ok(count == 4, "Unexpected item count %d.\n", count); + + /* Emptying listbox sends a LB_RESETCONTENT to itself. */ + flush_sequence(sequences, LB_SEQ_INDEX); + for (i = count; i--;) + { + res = SendMessageA(hLB, LB_DELETESTRING, 0, 0); + ok(res == i, "Unexpected return value %d.\n", res); + } + ok_sequence(sequences, LB_SEQ_INDEX, delete_seq, "Emptying listbox", FALSE); DestroyWindow(hLB); } @@ -234,41 +328,85 @@ static void test_item_height(void) static int got_selchange; -static LRESULT WINAPI main_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +static LRESULT WINAPI main_window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { + static LONG defwndproc_counter = 0; + struct message m = { 0 }; + LRESULT ret; + + m.message = msg; + m.flags = sent|wparam|lparam; + if (defwndproc_counter) m.flags |= defwinproc; + m.wParam = wParam; + m.lParam = lParam; + switch (msg) { case WM_MEASUREITEM: { - DWORD style = GetWindowLongA(GetWindow(hwnd, GW_CHILD), GWL_STYLE); - MEASUREITEMSTRUCT *mi = (void*)lparam; + MEASUREITEMSTRUCT *mis = (void *)lParam; + BOOL is_unicode_data = FALSE; + MEASURE_ITEM_STRUCT mi; - ok(wparam == mi->CtlID, "got wParam=%08lx, expected %08x\n", wparam, mi->CtlID); - ok(mi->CtlType == ODT_LISTBOX, "mi->CtlType = %u\n", mi->CtlType); - ok(mi->CtlID == 1, "mi->CtlID = %u\n", mi->CtlID); - ok(mi->itemHeight, "mi->itemHeight = 0\n"); - - if (mi->itemID > 4 || style & LBS_OWNERDRAWFIXED) - break; - - if (style & LBS_HASSTRINGS) + if (mis->CtlType == ODT_LISTBOX) { - ok(!strcmp_aw((WCHAR*)mi->itemData, strings[mi->itemID]), - "mi->itemData = %s (%d)\n", wine_dbgstr_w((WCHAR*)mi->itemData), mi->itemID); + HWND ctrl = GetDlgItem(hwnd, mis->CtlID); + is_unicode_data = GetWindowLongA(ctrl, GWL_STYLE) & LBS_HASSTRINGS; + } + + mi.u.wp = 0; + mi.u.item.CtlType = mis->CtlType; + mi.u.item.CtlID = mis->CtlID; + mi.u.item.itemID = mis->itemID; + mi.u.item.wParam = wParam; + + m.wParam = mi.u.wp; + if (is_unicode_data) + m.lParam = mis->itemData ? hash_Ly_W((const WCHAR *)mis->itemData) : 0; + else + m.lParam = mis->itemData ? hash_Ly((const char *)mis->itemData) : 0; + add_message(sequences, PARENT_SEQ_INDEX, &m); + + ok(wParam == mis->CtlID, "got wParam=%08lx, expected %08x\n", wParam, mis->CtlID); + ok(mis->CtlType == ODT_LISTBOX, "mi->CtlType = %u\n", mis->CtlType); + ok(mis->CtlID == 1, "mi->CtlID = %u\n", mis->CtlID); + ok(mis->itemHeight, "mi->itemHeight = 0\n"); + + break; + } + case WM_COMPAREITEM: + { + COMPAREITEMSTRUCT *cis = (COMPAREITEMSTRUCT *)lParam; + HWND ctrl = GetDlgItem(hwnd, cis->CtlID); + BOOL is_unicode_data = TRUE; + + ok(wParam == cis->CtlID, "expected %#x, got %#lx\n", cis->CtlID, wParam); + ok(cis->hwndItem == ctrl, "expected %p, got %p\n", ctrl, cis->hwndItem); + ok((int)cis->itemID1 >= 0, "expected >= 0, got %d\n", cis->itemID1); + ok((int)cis->itemID2 == -1, "expected -1, got %d\n", cis->itemID2); + + if (cis->CtlType == ODT_LISTBOX) + is_unicode_data = GetWindowLongA(ctrl, GWL_STYLE) & LBS_HASSTRINGS; + + if (is_unicode_data) + { + m.wParam = cis->itemData1 ? hash_Ly_W((const WCHAR *)cis->itemData1) : 0; + m.lParam = cis->itemData2 ? hash_Ly_W((const WCHAR *)cis->itemData2) : 0; } else { - ok((void*)mi->itemData == strings[mi->itemID], - "mi->itemData = %08lx, expected %p\n", mi->itemData, strings[mi->itemID]); + m.wParam = cis->itemData1 ? hash_Ly((const char *)cis->itemData1) : 0; + m.lParam = cis->itemData2 ? hash_Ly((const char *)cis->itemData2) : 0; } + add_message(sequences, PARENT_SEQ_INDEX, &m); break; } case WM_DRAWITEM: { RECT rc_item, rc_client, rc_clip; - DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lparam; + DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lParam; - ok(wparam == dis->CtlID, "got wParam=%08lx instead of %08x\n", wparam, dis->CtlID); + ok(wParam == dis->CtlID, "got wParam=%08lx instead of %08x\n", wParam, dis->CtlID); ok(dis->CtlType == ODT_LISTBOX, "wrong CtlType %04x\n", dis->CtlType); GetClientRect(dis->hwndItem, &rc_client); @@ -284,14 +422,18 @@ static LRESULT WINAPI main_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARA } case WM_COMMAND: - if (HIWORD( wparam ) == LBN_SELCHANGE) got_selchange++; + if (HIWORD( wParam ) == LBN_SELCHANGE) got_selchange++; break; default: break; } - return DefWindowProcA(hwnd, msg, wparam, lparam); + defwndproc_counter++; + ret = DefWindowProcA(hwnd, msg, wParam, lParam); + defwndproc_counter--; + + return msg == WM_COMPAREITEM ? -1 : ret; } static HWND create_parent( void ) @@ -320,33 +462,72 @@ static HWND create_parent( void ) static void test_ownerdraw(void) { + static const DWORD styles[] = + { + 0, + LBS_NODATA + }; HWND parent, hLB; INT ret; RECT rc; + UINT i; parent = create_parent(); ok(parent != NULL, "Failed to create parent window.\n"); - hLB = create_listbox(LBS_OWNERDRAWFIXED | WS_CHILD | WS_VISIBLE, parent); - ok(hLB != NULL, "Failed to create listbox window.\n"); + for (i = 0; i < ARRAY_SIZE(styles); i++) + { + hLB = create_listbox(LBS_OWNERDRAWFIXED | WS_CHILD | WS_VISIBLE | styles[i], parent); + ok(hLB != NULL, "Failed to create listbox window.\n"); - SetForegroundWindow(hLB); - UpdateWindow(hLB); + SetForegroundWindow(hLB); + UpdateWindow(hLB); - /* make height short enough */ - SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc); - SetWindowPos(hLB, 0, 0, 0, 100, rc.bottom - rc.top + 1, SWP_NOZORDER | SWP_NOMOVE); + /* make height short enough */ + SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc); + SetWindowPos(hLB, 0, 0, 0, 100, rc.bottom - rc.top + 1, SWP_NOZORDER | SWP_NOMOVE); - /* make 0 item invisible */ - SendMessageA(hLB, LB_SETTOPINDEX, 1, 0); - ret = SendMessageA(hLB, LB_GETTOPINDEX, 0, 0); - ok(ret == 1, "wrong top index %d\n", ret); + /* make 0 item invisible */ + SendMessageA(hLB, LB_SETTOPINDEX, 1, 0); + ret = SendMessageA(hLB, LB_GETTOPINDEX, 0, 0); + ok(ret == 1, "wrong top index %d\n", ret); - SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc); - ok(!IsRectEmpty(&rc), "empty item rect\n"); - ok(rc.top < 0, "rc.top is not negative (%d)\n", rc.top); + SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc); + ok(!IsRectEmpty(&rc), "empty item rect\n"); + ok(rc.top < 0, "rc.top is not negative (%d)\n", rc.top); + + DestroyWindow(hLB); + + /* Both FIXED and VARIABLE, FIXED should override VARIABLE. */ + hLB = CreateWindowA(WC_LISTBOXA, "TestList", LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE | styles[i], + 0, 0, 100, 100, NULL, NULL, NULL, 0); + ok(hLB != NULL, "last error 0x%08x\n", GetLastError()); + + ok(GetWindowLongA(hLB, GWL_STYLE) & LBS_OWNERDRAWVARIABLE, "Unexpected window style.\n"); + + ret = SendMessageA(hLB, LB_INSERTSTRING, -1, 0); + ok(ret == 0, "Unexpected return value %d.\n", ret); + ret = SendMessageA(hLB, LB_INSERTSTRING, -1, 0); + ok(ret == 1, "Unexpected return value %d.\n", ret); + + ret = SendMessageA(hLB, LB_SETITEMHEIGHT, 0, 13); + ok(ret == LB_OKAY, "Failed to set item height, %d.\n", ret); + + ret = SendMessageA(hLB, LB_GETITEMHEIGHT, 0, 0); + ok(ret == 13, "Unexpected item height %d.\n", ret); + + ret = SendMessageA(hLB, LB_SETITEMHEIGHT, 1, 42); + ok(ret == LB_OKAY, "Failed to set item height, %d.\n", ret); + + ret = SendMessageA(hLB, LB_GETITEMHEIGHT, 0, 0); + ok(ret == 42, "Unexpected item height %d.\n", ret); + + ret = SendMessageA(hLB, LB_GETITEMHEIGHT, 1, 0); + ok(ret == 42, "Unexpected item height %d.\n", ret); + + DestroyWindow (hLB); + } - DestroyWindow(hLB); DestroyWindow(parent); } @@ -447,17 +628,121 @@ static void test_LB_SETCURSEL(void) SendMessageA(hLB, LB_SETITEMHEIGHT, 0, 32); + ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0); ok(ret == 2, "LB_SETCURSEL returned %d instead of 2\n", ret); ret = GetScrollPos(hLB, SB_VERT); ok(ret == 0, "expected vscroll 0, got %d\n", ret); + ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + ret = SendMessageA(hLB, LB_SETCURSEL, 3, 0); ok(ret == 3, "LB_SETCURSEL returned %d instead of 3\n", ret); ret = GetScrollPos(hLB, SB_VERT); ok(ret == 1, "expected vscroll 1, got %d\n", ret); + ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + DestroyWindow(hLB); + + hLB = create_listbox(0, 0); + ok(hLB != NULL, "Failed to create ListBox window.\n"); + + ret = SendMessageA(hLB, LB_SETCURSEL, 1, 0); + ok(ret == 1, "Unexpected return value %d.\n", ret); + + ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + + DestroyWindow(hLB); + + /* LBS_EXTENDEDSEL */ + hLB = create_listbox(LBS_EXTENDEDSEL, 0); + ok(hLB != NULL, "Failed to create listbox.\n"); + + ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + + ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0); + ok(ret == -1, "LB_SETCURSEL returned %d instead of 2\n", ret); + + ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + + DestroyWindow(hLB); + + /* LBS_MULTIPLESEL */ + hLB = create_listbox(LBS_MULTIPLESEL, 0); + ok(hLB != NULL, "Failed to create listbox.\n"); + + ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + + ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0); + ok(ret == -1, "LB_SETCURSEL returned %d instead of 2\n", ret); + + ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + + DestroyWindow(hLB); +} + +static void test_LB_SETSEL(void) +{ + HWND list; + int ret; + + /* LBS_EXTENDEDSEL */ + list = create_listbox(LBS_EXTENDEDSEL, 0); + ok(list != NULL, "Failed to create ListBox window.\n"); + + ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + + ret = SendMessageA(list, LB_SETSEL, TRUE, 0); + ok(ret == 0, "Unexpected return value %d.\n", ret); + ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0); + ok(ret == 0, "Unexpected anchor index %d.\n", ret); + + ret = SendMessageA(list, LB_SETSEL, TRUE, 1); + ok(ret == 0, "Unexpected return value %d.\n", ret); + ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0); + ok(ret == 1, "Unexpected anchor index %d.\n", ret); + + ret = SendMessageA(list, LB_SETSEL, FALSE, 1); + ok(ret == 0, "Unexpected return value %d.\n", ret); + ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0); + ok(ret == 1, "Unexpected anchor index %d.\n", ret); + + DestroyWindow(list); + + /* LBS_MULTIPLESEL */ + list = create_listbox(LBS_MULTIPLESEL, 0); + ok(list != NULL, "Failed to create ListBox window.\n"); + + ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0); + ok(ret == -1, "Unexpected anchor index %d.\n", ret); + + ret = SendMessageA(list, LB_SETSEL, TRUE, 0); + ok(ret == 0, "Unexpected return value %d.\n", ret); + ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0); + ok(ret == 0, "Unexpected anchor index %d.\n", ret); + + ret = SendMessageA(list, LB_SETSEL, TRUE, 1); + ok(ret == 0, "Unexpected return value %d.\n", ret); + ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0); + ok(ret == 1, "Unexpected anchor index %d.\n", ret); + + ret = SendMessageA(list, LB_SETSEL, FALSE, 1); + ok(ret == 0, "Unexpected return value %d.\n", ret); + ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0); + ok(ret == 1, "Unexpected anchor index %d.\n", ret); + + DestroyWindow(list); } static void test_listbox_height(void) @@ -603,6 +888,7 @@ static void test_listbox_item_data(void) static void test_listbox_LB_DIR(void) { + char path[MAX_PATH], curdir[MAX_PATH]; HWND hList; int res, itemCount; int itemCount_justFiles; @@ -615,6 +901,16 @@ static void test_listbox_LB_DIR(void) char driveletter; const char *wildcard = "*"; HANDLE file; + BOOL ret; + + GetCurrentDirectoryA(ARRAY_SIZE(curdir), curdir); + + GetTempPathA(ARRAY_SIZE(path), path); + ret = SetCurrentDirectoryA(path); + ok(ret, "Failed to set current directory.\n"); + + ret = CreateDirectoryA("lb_dir_test", NULL); + ok(ret, "Failed to create test directory.\n"); file = CreateFileA( "wtest1.tmp.c", GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL ); ok(file != INVALID_HANDLE_VALUE, "Error creating the test file: %d\n", GetLastError()); @@ -945,11 +1241,11 @@ static void test_listbox_LB_DIR(void) itemCount, itemCount_allDirs); ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, *) returned incorrect index!\n"); - if (itemCount && GetCurrentDirectoryA( MAX_PATH, pathBuffer ) > 3) /* there's no [..] in drive root */ + if (itemCount) { memset(pathBuffer, 0, MAX_PATH); SendMessageA(hList, LB_GETTEXT, 0, (LPARAM)pathBuffer); - ok( !strcmp(pathBuffer, "[..]"), "First element is not [..]\n"); + ok( !strcmp(pathBuffer, "[..]"), "First element is %s, not [..]\n", pathBuffer); } /* This tests behavior when no files match the wildcard */ @@ -1034,6 +1330,9 @@ static void test_listbox_LB_DIR(void) DestroyWindow(hList); DeleteFileA( "wtest1.tmp.c" ); + RemoveDirectoryA("lb_dir_test"); + + SetCurrentDirectoryA(curdir); } static HWND g_listBox; @@ -1533,7 +1832,13 @@ static void test_listbox_dlgdir(void) static void test_set_count( void ) { + static const DWORD styles[] = + { + LBS_OWNERDRAWFIXED, + LBS_HASSTRINGS, + }; HWND parent, listbox; + unsigned int i; LONG ret; RECT r; @@ -1563,37 +1868,39 @@ static void test_set_count( void ) ok( !IsRectEmpty( &r ), "got empty rect\n"); DestroyWindow( listbox ); + + for (i = 0; i < ARRAY_SIZE(styles); ++i) + { + listbox = create_listbox( styles[i] | WS_CHILD | WS_VISIBLE, parent ); + + SetLastError( 0xdeadbeef ); + ret = SendMessageA( listbox, LB_SETCOUNT, 100, 0 ); + ok( ret == LB_ERR, "expected %d, got %d\n", LB_ERR, ret ); + ok( GetLastError() == 0xdeadbeef, "Unexpected error %d.\n", GetLastError() ); + + DestroyWindow( listbox ); + } + DestroyWindow( parent ); } -static int lb_getlistboxinfo; - -static LRESULT WINAPI listbox_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA); - - if (message == LB_GETLISTBOXINFO) - lb_getlistboxinfo++; - - return CallWindowProcA(oldproc, hwnd, message, wParam, lParam); -} - static void test_GetListBoxInfo(void) { + static const struct message getlistboxinfo_seq[] = + { + { LB_GETLISTBOXINFO, sent }, + { 0 } + }; HWND listbox, parent; - WNDPROC oldproc; DWORD ret; parent = create_parent(); listbox = create_listbox(WS_CHILD | WS_VISIBLE, parent); - oldproc = (WNDPROC)SetWindowLongPtrA(listbox, GWLP_WNDPROC, (LONG_PTR)listbox_subclass_proc); - SetWindowLongPtrA(listbox, GWLP_USERDATA, (LONG_PTR)oldproc); - - lb_getlistboxinfo = 0; + flush_sequences(sequences, NUM_MSG_SEQUENCES); ret = GetListBoxInfo(listbox); ok(ret > 0, "got %d\n", ret); - ok(lb_getlistboxinfo == 1, "got %d\n", lb_getlistboxinfo); + ok_sequence(sequences, LB_SEQ_INDEX, getlistboxinfo_seq, "GetListBoxInfo()", FALSE); DestroyWindow(listbox); DestroyWindow(parent); @@ -1805,76 +2112,101 @@ static void test_listbox(void) { static const struct listbox_test SS = /* {add_style} */ - {{0}, - {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + {{LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, { 1, 1, 1, LB_ERR}, {0,0,0,0}, { 2, 2, 2, LB_ERR}, {0,0,0,0}, {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; /* {selected, anchor, caret, selcount}{TODO fields} */ static const struct listbox_test SS_NS = - {{LBS_NOSEL}, - {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + {{LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, { 1, 1, 1, LB_ERR}, {0,0,0,0}, { 2, 2, 2, LB_ERR}, {0,0,0,0}, {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; static const struct listbox_test MS = - {{LBS_MULTIPLESEL}, - { 0, LB_ERR, 0, 0}, {0,0,0,0}, + {{ 0, LB_ERR, 0, 0}, {0,0,0,0}, { 1, 1, 1, 1}, {0,0,0,0}, { 2, 1, 2, 1}, {0,0,0,0}, { 0, LB_ERR, 0, 2}, {0,0,0,0}}; static const struct listbox_test MS_NS = - {{LBS_MULTIPLESEL | LBS_NOSEL}, - {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + {{LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, { 1, 1, 1, LB_ERR}, {0,0,0,0}, { 2, 2, 2, LB_ERR}, {0,0,0,0}, {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; static const struct listbox_test ES = - {{LBS_EXTENDEDSEL}, - { 0, LB_ERR, 0, 0}, {0,0,0,0}, + {{ 0, LB_ERR, 0, 0}, {0,0,0,0}, { 1, 1, 1, 1}, {0,0,0,0}, { 2, 2, 2, 1}, {0,0,0,0}, { 0, LB_ERR, 0, 2}, {0,0,0,0}}; static const struct listbox_test ES_NS = - {{LBS_EXTENDEDSEL | LBS_NOSEL}, - {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + {{LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, { 1, 1, 1, LB_ERR}, {0,0,0,0}, { 2, 2, 2, LB_ERR}, {0,0,0,0}, {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; static const struct listbox_test EMS = - {{LBS_EXTENDEDSEL | LBS_MULTIPLESEL}, - { 0, LB_ERR, 0, 0}, {0,0,0,0}, + {{ 0, LB_ERR, 0, 0}, {0,0,0,0}, { 1, 1, 1, 1}, {0,0,0,0}, { 2, 2, 2, 1}, {0,0,0,0}, { 0, LB_ERR, 0, 2}, {0,0,0,0}}; static const struct listbox_test EMS_NS = - {{LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL}, - {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + {{LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, { 1, 1, 1, LB_ERR}, {0,0,0,0}, { 2, 2, 2, LB_ERR}, {0,0,0,0}, {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; - run_test(SS); - run_test(SS_NS); - run_test(MS); - run_test(MS_NS); - run_test(ES); - run_test(ES_NS); - run_test(EMS); - run_test(EMS_NS); + run_test(0, SS); + run_test(LBS_NOSEL, SS_NS); + run_test(LBS_MULTIPLESEL, MS); + run_test(LBS_MULTIPLESEL | LBS_NOSEL, MS_NS); + run_test(LBS_EXTENDEDSEL, ES); + run_test(LBS_EXTENDEDSEL | LBS_NOSEL, ES_NS); + run_test(LBS_EXTENDEDSEL | LBS_MULTIPLESEL, EMS); + run_test(LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL, EMS_NS); + + run_test(LBS_NODATA | LBS_OWNERDRAWFIXED, SS); + run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_NOSEL, SS_NS); + run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_MULTIPLESEL, MS); + run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_MULTIPLESEL | LBS_NOSEL, MS_NS); + run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL, ES); + run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL | LBS_NOSEL, ES_NS); + run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL | LBS_MULTIPLESEL, EMS); + run_test(LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL, EMS_NS); } +static const struct message lb_addstring_ownerdraw_parent_seq[] = +{ + { WM_MEASUREITEM, sent|wparam|lparam, 0x1012, 0xf30604ed }, + { WM_MEASUREITEM, sent|wparam|lparam, 0x1112, 0xf30604ee }, + { WM_MEASUREITEM, sent|wparam|lparam, 0x1212, 0xf30604ef }, + { 0 } +}; + +static const struct message lb_addstring_sort_parent_seq[] = +{ + { WM_MEASUREITEM, sent|wparam|lparam, 0x1012, 0xf30604ed }, + { WM_COMPAREITEM, sent|wparam|lparam, 0xf30604ed, 0xf30604ee }, + { WM_MEASUREITEM, sent|wparam|lparam, 0x1112, 0xf30604ee }, + { WM_COMPAREITEM, sent|wparam|lparam, 0xf30604ed, 0xf30604ef }, + { WM_COMPAREITEM, sent|wparam|lparam, 0xf30604ee, 0xf30604ef }, + { WM_MEASUREITEM, sent|wparam|lparam, 0x1212, 0xf30604ef }, + { 0 } +}; + +static const struct message empty_seq[] = +{ + { 0 } +}; + static void test_WM_MEASUREITEM(void) { HWND parent, listbox; - LRESULT data; + LRESULT data, ret; parent = create_parent(); listbox = create_listbox(WS_CHILD | LBS_OWNERDRAWVARIABLE, parent); @@ -1888,6 +2220,187 @@ static void test_WM_MEASUREITEM(void) data = SendMessageA(listbox, LB_GETITEMDATA, 0, 0); ok(!data, "data = %08lx\n", data); + + /* LBS_HASSTRINGS */ + parent = create_parent(); + listbox = CreateWindowExA(WS_EX_NOPARENTNOTIFY, WC_LISTBOXA, NULL, + WS_CHILD | LBS_NOTIFY | LBS_OWNERDRAWVARIABLE | LBS_HASSTRINGS | WS_VISIBLE, + 10, 10, 80, 80, parent, (HMENU)ID_LISTBOX, 0, NULL); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 0"); + ok(ret == 0, "expected 0, got %ld\n", ret); + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 1"); + ok(ret == 1, "expected 1, got %ld\n", ret); + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 2"); + ok(ret == 2, "expected 2, got %ld\n", ret); + + ok_sequence(sequences, PARENT_SEQ_INDEX, lb_addstring_ownerdraw_parent_seq, + "LB_ADDSTRING (LBS_HASSTRINGS, ownerdraw)", FALSE); + DestroyWindow(listbox); + + /* LBS_SORT, no LBS_HASSTRINGS */ + listbox = CreateWindowExA(WS_EX_NOPARENTNOTIFY, WC_LISTBOXA, NULL, + WS_CHILD | LBS_NOTIFY | LBS_OWNERDRAWVARIABLE | LBS_SORT | WS_VISIBLE, + 10, 10, 80, 80, parent, (HMENU)ID_LISTBOX, 0, NULL); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 0"); + ok(ret == 0, "expected 0, got %ld\n", ret); + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 1"); + ok(ret == 1, "expected 1, got %ld\n", ret); + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 2"); + ok(ret == 2, "expected 2, got %ld\n", ret); + + ok_sequence(sequences, PARENT_SEQ_INDEX, lb_addstring_sort_parent_seq, "LB_ADDSTRING (LBS_SORT)", FALSE); + DestroyWindow(listbox); + + /* LBS_HASSTRINGS */ + listbox = CreateWindowExA(WS_EX_NOPARENTNOTIFY, WC_LISTBOXA, NULL, + WS_CHILD | LBS_NOTIFY | LBS_HASSTRINGS | WS_VISIBLE, + 10, 10, 80, 80, parent, (HMENU)ID_LISTBOX, 0, NULL); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 2"); + ok(ret == 0, "expected 0, got %ld\n", ret); + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 0"); + ok(ret == 1, "expected 1, got %ld\n", ret); + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 1"); + ok(ret == 2, "expected 2, got %ld\n", ret); + + ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "LB_ADDSTRING (LBS_HASSTRINGS)", FALSE); + DestroyWindow(listbox); + + /* LBS_HASSTRINGS, LBS_SORT */ + listbox = CreateWindowExA(WS_EX_NOPARENTNOTIFY, WC_LISTBOXA, NULL, + WS_CHILD | LBS_NOTIFY | LBS_HASSTRINGS | LBS_SORT | WS_VISIBLE, + 10, 10, 80, 80, parent, (HMENU)ID_LISTBOX, 0, NULL); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 2"); + ok(ret == 0, "expected 0, got %ld\n", ret); + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 0"); + ok(ret == 0, "expected 0, got %ld\n", ret); + ret = SendMessageA(listbox, LB_ADDSTRING, 0, (LPARAM)"item 1"); + ok(ret == 1, "expected 1, got %ld\n", ret); + + ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "LB_ADDSTRING (LBS_HASSTRINGS, LBS_SORT)", FALSE); + DestroyWindow(listbox); + + DestroyWindow(parent); +} + +static void test_LBS_NODATA(void) +{ + static const DWORD invalid_styles[] = + { + 0, + LBS_OWNERDRAWVARIABLE, + LBS_SORT, + LBS_HASSTRINGS, + LBS_OWNERDRAWFIXED | LBS_SORT, + LBS_OWNERDRAWFIXED | LBS_HASSTRINGS, + }; + static const UINT invalid_idx[] = { -2, 2 }; + static const UINT valid_idx[] = { 0, 1 }; + static const ULONG_PTR zero_data; + HWND listbox, parent; + INT ret, text_len; + unsigned int i; + ULONG_PTR data; + BOOL is_wow64; + + listbox = CreateWindowA(WC_LISTBOXA, "TestList", LBS_NODATA | LBS_OWNERDRAWFIXED | WS_VISIBLE, + 0, 0, 100, 100, NULL, NULL, NULL, 0); + ok(listbox != NULL, "Failed to create ListBox window.\n"); + + ret = SendMessageA(listbox, LB_INSERTSTRING, -1, 0); + ok(ret == 0, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_INSERTSTRING, -1, 0); + ok(ret == 1, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_GETCOUNT, 0, 0); + ok(ret == 2, "Unexpected return value %d.\n", ret); + + /* Invalid indices. */ + for (i = 0; i < ARRAY_SIZE(invalid_idx); ++i) + { + ret = SendMessageA(listbox, LB_SETITEMDATA, invalid_idx[i], 42); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_GETTEXTLEN, invalid_idx[i], 0); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + if (ret == LB_ERR) + { + ret = SendMessageA(listbox, LB_GETTEXT, invalid_idx[i], (LPARAM)&data); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + } + ret = SendMessageA(listbox, LB_GETITEMDATA, invalid_idx[i], 0); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + } + + IsWow64Process(GetCurrentProcess(), &is_wow64); +#ifdef _WIN64 + text_len = 8; +#else + text_len = is_wow64 ? 8 : 4; +#endif + + /* Valid indices. */ + for (i = 0; i < ARRAY_SIZE(valid_idx); ++i) + { + ret = SendMessageA(listbox, LB_SETITEMDATA, valid_idx[i], 42); + ok(ret == TRUE, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_GETTEXTLEN, valid_idx[i], 0); + todo_wine_if(is_wow64) + ok(ret == text_len, "Unexpected return value %d.\n", ret); + + memset(&data, 0xee, sizeof(data)); + ret = SendMessageA(listbox, LB_GETTEXT, valid_idx[i], (LPARAM)&data); + ok(ret == sizeof(data), "Unexpected return value %d.\n", ret); + ok(!memcmp(&data, &zero_data, sizeof(data)), "Unexpected item data.\n"); + + ret = SendMessageA(listbox, LB_GETITEMDATA, valid_idx[i], 0); + ok(ret == 0, "Unexpected return value %d.\n", ret); + } + + /* More messages that don't work with LBS_NODATA. */ + ret = SendMessageA(listbox, LB_FINDSTRING, 1, 0); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_FINDSTRING, 1, 42); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_FINDSTRINGEXACT, 1, 0); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_FINDSTRINGEXACT, 1, 42); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_SELECTSTRING, 1, 0); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + ret = SendMessageA(listbox, LB_SELECTSTRING, 1, 42); + ok(ret == LB_ERR, "Unexpected return value %d.\n", ret); + + DestroyWindow(listbox); + + /* Invalid window style combinations. */ + parent = create_parent(); + ok(parent != NULL, "Failed to create parent window.\n"); + + for (i = 0; i < ARRAY_SIZE(invalid_styles); ++i) + { + DWORD style; + + listbox = CreateWindowA(WC_LISTBOXA, "TestList", LBS_NODATA | WS_CHILD | invalid_styles[i], + 0, 0, 100, 100, parent, (HMENU)ID_LISTBOX, NULL, 0); + ok(listbox != NULL, "Failed to create a listbox.\n"); + + style = GetWindowLongA(listbox, GWL_STYLE); + ok((style & invalid_styles[i]) == invalid_styles[i], "%u: unexpected window styles %#x.\n", i, style); + ret = SendMessageA(listbox, LB_SETCOUNT, 100, 0); + ok(ret == LB_ERR, "%u: unexpected return value %d.\n", i, ret); + DestroyWindow(listbox); + } + DestroyWindow(parent); } @@ -1899,6 +2412,8 @@ START_TEST(listbox) if (!load_v6_module(&ctx_cookie, &hCtx)) return; + init_msg_sequences(sequences, NUM_MSG_SEQUENCES); + test_listbox(); test_item_height(); test_ownerdraw(); @@ -1914,6 +2429,8 @@ START_TEST(listbox) test_missing_lbuttonup(); test_extents(); test_WM_MEASUREITEM(); + test_LB_SETSEL(); + test_LBS_NODATA(); unload_v6_module(ctx_cookie, hCtx); } diff --git a/modules/rostests/winetests/comctl32/listview.c b/modules/rostests/winetests/comctl32/listview.c index ddeb991c816..ec289fa3f23 100644 --- a/modules/rostests/winetests/comctl32/listview.c +++ b/modules/rostests/winetests/comctl32/listview.c @@ -75,6 +75,8 @@ static BOOL g_disp_A_to_W; static NMLVDISPINFOA g_editbox_disp_info; /* when this is set focus will be tested on LVN_DELETEITEM */ static BOOL g_focus_test_LVN_DELETEITEM; +/* Whether to send WM_KILLFOCUS to the edit control during LVN_ENDLABELEDIT */ +static BOOL g_WM_KILLFOCUS_on_LVN_ENDLABELEDIT; static HWND subclass_editbox(HWND hwndListview); @@ -445,6 +447,25 @@ static const struct message parent_list_cd_seq[] = { { 0 } }; +static const struct message listview_end_label_edit[] = { + { WM_NOTIFY, sent|id, 0, 0, LVN_ENDLABELEDITA }, + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGING}, + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED }, + { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW }, /* XP */ + { WM_NOTIFY, sent|id, 0, 0, NM_SETFOCUS }, + { 0 } +}; + +static const struct message listview_end_label_edit_kill_focus[] = { + { WM_NOTIFY, sent|id, 0, 0, LVN_ENDLABELEDITA }, + { WM_COMMAND, sent|id|optional, 0, 0, EN_KILLFOCUS }, /* todo: not sent by wine yet */ + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGING }, + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED }, + { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW }, /* XP */ + { WM_NOTIFY, sent|id, 0, 0, NM_SETFOCUS }, + { 0 } +}; + static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static LONG defwndproc_counter = 0; @@ -457,6 +478,7 @@ static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LP msg.wParam = wParam; msg.lParam = lParam; if (message == WM_NOTIFY && lParam) msg.id = ((NMHDR*)lParam)->code; + if (message == WM_COMMAND) msg.id = HIWORD(wParam); /* log system messages, except for painting */ if (message < WM_USER && @@ -503,24 +525,17 @@ static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LP /* always accept new item text */ NMLVDISPINFOA *di = (NMLVDISPINFOA*)lParam; g_editbox_disp_info = *di; - trace("LVN_ENDLABELEDIT: text=%s\n", di->item.pszText ? di->item.pszText : "(null)"); /* edit control still available from this notification */ edit = (HWND)SendMessageA(((NMHDR*)lParam)->hwndFrom, LVM_GETEDITCONTROL, 0, 0); ok(IsWindow(edit), "expected valid edit control handle\n"); ok((GetWindowLongA(edit, GWL_STYLE) & ES_MULTILINE) == 0, "edit is multiline\n"); + if (g_WM_KILLFOCUS_on_LVN_ENDLABELEDIT) + SendMessageA(edit, WM_KILLFOCUS, 0, 0); + return TRUE; } - case LVN_BEGINSCROLL: - case LVN_ENDSCROLL: - { - NMLVSCROLL *pScroll = (NMLVSCROLL*)lParam; - - trace("LVN_%sSCROLL: (%d,%d)\n", pScroll->hdr.code == LVN_BEGINSCROLL ? - "BEGIN" : "END", pScroll->dx, pScroll->dy); - } - break; case LVN_ITEMCHANGING: { NMLISTVIEW *nmlv = (NMLISTVIEW*)lParam; @@ -1781,6 +1796,7 @@ static void test_redraw(void) HDC hdc; BOOL res; DWORD r; + RECT rect; hwnd = create_listview_control(LVS_REPORT); subclass_header(hwnd); @@ -1838,6 +1854,13 @@ static void test_redraw(void) ReleaseDC(hwndparent, hdc); + /* test setting the window style to what it already was */ + UpdateWindow(hwnd); + SetWindowLongA(hwnd, GWL_STYLE, GetWindowLongA(hwnd, GWL_STYLE)); + GetUpdateRect(hwnd, &rect, FALSE); + ok(rect.left == 0 && rect.top == 0 && rect.right == 0 && rect.bottom == 0, + "Expected empty update rect, got %s\n", wine_dbgstr_rect(&rect)); + DestroyWindow(hwnd); } @@ -2337,7 +2360,7 @@ static void test_multiselect(void) r = SendMessageA(hwnd, LVM_GETSELECTIONMARK, 0, 0); ok(r == 0, "got %d\n", r); - for (i = 0; i < sizeof(task_list)/sizeof(task_list[0]); i++) { + for (i = 0; i < ARRAY_SIZE(task_list); i++) { DWORD selected_count; LVITEMA item; @@ -3490,7 +3513,7 @@ static void test_norecompute(void) item.mask = LVIF_TEXT | LVIF_NORECOMPUTE; item.iItem = 0; item.pszText = buff; - item.cchTextMax = sizeof(buff)/sizeof(CHAR); + item.cchTextMax = ARRAY_SIZE(buff); res = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); expect(TRUE, res); ok(lstrcmpA(buff, testA) == 0, "Expected (%s), got (%s)\n", testA, buff); @@ -3504,7 +3527,7 @@ static void test_norecompute(void) item.mask = LVIF_TEXT | LVIF_NORECOMPUTE; item.iItem = 1; item.pszText = buff; - item.cchTextMax = sizeof(buff)/sizeof(CHAR); + item.cchTextMax = ARRAY_SIZE(buff); flush_sequences(sequences, NUM_MSG_SEQUENCES); res = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); @@ -3529,7 +3552,7 @@ static void test_norecompute(void) item.mask = LVIF_TEXT | LVIF_NORECOMPUTE; item.iItem = 0; item.pszText = buff; - item.cchTextMax = sizeof(buff)/sizeof(CHAR); + item.cchTextMax = ARRAY_SIZE(buff); flush_sequences(sequences, NUM_MSG_SEQUENCES); res = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); expect(TRUE, res); @@ -4671,7 +4694,7 @@ static void test_canceleditlabel(void) ok(!IsWindow(hwndedit), "Expected edit control to be destroyed\n"); memset(&itema, 0, sizeof(itema)); itema.pszText = buff; - itema.cchTextMax = sizeof(buff)/sizeof(CHAR); + itema.cchTextMax = ARRAY_SIZE(buff); ret = SendMessageA(hwnd, LVM_GETITEMTEXTA, 0, (LPARAM)&itema); expect(5, ret); ok(strcmp(buff, test1) == 0, "Expected label text not to change\n"); @@ -5499,7 +5522,7 @@ static void test_header_notification2(void) memset(&itemW, 0, sizeof(itemW)); itemW.mask = HDI_WIDTH | HDI_ORDER | HDI_TEXT; itemW.pszText = buffer; - itemW.cchTextMax = sizeof(buffer); + itemW.cchTextMax = ARRAY_SIZE(buffer); ret = SendMessageW(header, HDM_GETITEMW, 0, (LPARAM)&itemW); expect(1, ret); @@ -5667,7 +5690,7 @@ static void test_dispinfo(void) g_disp_A_to_W = TRUE; item.pszText = (char*)buff; - item.cchTextMax = sizeof(buff)/sizeof(WCHAR); + item.cchTextMax = ARRAY_SIZE(buff); ret = SendMessageA(hwnd, LVM_GETITEMTEXTA, 0, (LPARAM)&item); ok(ret == sizeof(testA)-1, "got %d, expected 4\n", ret); g_disp_A_to_W = FALSE; @@ -6186,7 +6209,7 @@ static void test_state_image(void) }; int i; - for (i = 0; i < sizeof(styles)/sizeof(styles[0]); i++) + for (i = 0; i < ARRAY_SIZE(styles); i++) { static char text[] = "Item"; static char subtext[] = "Subitem"; @@ -6331,6 +6354,127 @@ static void test_LVSCW_AUTOSIZE(void) DestroyWindow(hwnd); } +static void test_LVN_ENDLABELEDIT(void) +{ + WCHAR text[] = {'l','a','l','a',0}; + HWND hwnd, hwndedit; + LVITEMW item = {0}; + DWORD ret; + + hwnd = create_listview_control(LVS_REPORT | LVS_EDITLABELS); + + insert_column(hwnd, 0); + + item.mask = LVIF_TEXT; + item.pszText = text; + SendMessageW(hwnd, LVM_INSERTITEMW, 0, (LPARAM)&item); + + /* Test normal editing */ + SetFocus(hwnd); + hwndedit = (HWND)SendMessageW(hwnd, LVM_EDITLABELW, 0, 0); + ok(hwndedit != NULL, "Failed to get edit control.\n"); + + ret = SendMessageA(hwndedit, WM_SETTEXT, 0, (LPARAM)"test"); + ok(ret, "Failed to set edit text.\n"); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + ret = SendMessageA(hwndedit, WM_KEYDOWN, VK_RETURN, 0); + ok_sequence(sequences, PARENT_SEQ_INDEX, listview_end_label_edit, "Label edit", FALSE); + + /* Test editing with kill focus */ + SetFocus(hwnd); + hwndedit = (HWND)SendMessageW(hwnd, LVM_EDITLABELW, 0, 0); + ok(hwndedit != NULL, "Failed to get edit control.\n"); + + ret = SendMessageA(hwndedit, WM_SETTEXT, 0, (LPARAM)"test2"); + ok(ret, "Failed to set edit text.\n"); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + g_WM_KILLFOCUS_on_LVN_ENDLABELEDIT = TRUE; + ret = SendMessageA(hwndedit, WM_KEYDOWN, VK_RETURN, 0); + g_WM_KILLFOCUS_on_LVN_ENDLABELEDIT = FALSE; + + ok_sequence(sequences, PARENT_SEQ_INDEX, listview_end_label_edit_kill_focus, + "Label edit, kill focus", FALSE); + ok(GetFocus() == hwnd, "Unexpected focused window.\n"); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + DestroyWindow(hwnd); +} + +static LRESULT CALLBACK create_item_height_wndproc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_CREATE) + return 0; + + return CallWindowProcA(listviewWndProc, hwnd, msg, wParam, lParam); +} + +static void test_LVM_GETCOUNTPERPAGE(void) +{ + static const DWORD styles[] = { LVS_ICON, LVS_LIST, LVS_REPORT, LVS_SMALLICON }; + unsigned int i, j; + WNDCLASSEXA cls; + ATOM class; + HWND hwnd; + BOOL ret; + + cls.cbSize = sizeof(WNDCLASSEXA); + ret = GetClassInfoExA(GetModuleHandleA(NULL), WC_LISTVIEWA, &cls); + ok(ret, "Failed to get class info.\n"); + listviewWndProc = cls.lpfnWndProc; + cls.lpfnWndProc = create_item_height_wndproc; + cls.lpszClassName = "CountPerPageClass"; + class = RegisterClassExA(&cls); + ok(class, "Failed to register class.\n"); + + for (i = 0; i < ARRAY_SIZE(styles); i++) + { + static char text[] = "item text"; + LVITEMA item = { 0 }; + UINT count, count2; + + hwnd = create_listview_control(styles[i]); + ok(hwnd != NULL, "Failed to create listview window.\n"); + + count = SendMessageA(hwnd, LVM_GETCOUNTPERPAGE, 0, 0); + if (styles[i] == LVS_LIST || styles[i] == LVS_REPORT) + ok(count > 0 || broken(styles[i] == LVS_LIST && count == 0), "%u: unexpected count %u.\n", i, count); + else + ok(count == 0, "%u: unexpected count %u.\n", i, count); + + for (j = 0; j < 10; j++) + { + item.mask = LVIF_TEXT; + item.pszText = text; + SendMessageA(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&item); + } + + count2 = SendMessageA(hwnd, LVM_GETCOUNTPERPAGE, 0, 0); + if (styles[i] == LVS_LIST || styles[i] == LVS_REPORT) + ok(count == count2, "%u: unexpected count %u.\n", i, count2); + else + ok(count2 == 10, "%u: unexpected count %u.\n", i, count2); + + DestroyWindow(hwnd); + + hwnd = CreateWindowA("CountPerPageClass", "Test", WS_VISIBLE | styles[i], 0, 0, 100, 100, NULL, NULL, + GetModuleHandleA(NULL), 0); + ok(hwnd != NULL, "Failed to create a window.\n"); + + count = SendMessageA(hwnd, LVM_GETCOUNTPERPAGE, 0, 0); + ok(count == 0, "%u: unexpected count %u.\n", i, count); + + DestroyWindow(hwnd); + } + + ret = UnregisterClassA("CountPerPageClass", NULL); + ok(ret, "Failed to unregister test class.\n"); +} + START_TEST(listview) { ULONG_PTR ctx_cookie; @@ -6392,6 +6536,8 @@ START_TEST(listview) test_callback_mask(); test_state_image(); test_LVSCW_AUTOSIZE(); + test_LVN_ENDLABELEDIT(); + test_LVM_GETCOUNTPERPAGE(); if (!load_v6_module(&ctx_cookie, &hCtx)) { @@ -6434,6 +6580,8 @@ START_TEST(listview) test_oneclickactivate(); test_state_image(); test_LVSCW_AUTOSIZE(); + test_LVN_ENDLABELEDIT(); + test_LVM_GETCOUNTPERPAGE(); unload_v6_module(ctx_cookie, hCtx); diff --git a/modules/rostests/winetests/comctl32/misc.c b/modules/rostests/winetests/comctl32/misc.c index e18ca394534..a52744fc2e9 100644 --- a/modules/rostests/winetests/comctl32/misc.c +++ b/modules/rostests/winetests/comctl32/misc.c @@ -269,8 +269,7 @@ static void test_LoadIconWithScaleDown(void) /* non-existing filename */ hr = pLoadIconMetric(NULL, nonexisting_fileW, LIM_LARGE, &icon); - todo_wine - ok(hr == HRESULT_FROM_WIN32(ERROR_RESOURCE_TYPE_NOT_FOUND), + ok(hr == HRESULT_FROM_WIN32(ERROR_RESOURCE_TYPE_NOT_FOUND) || hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) /* Win7 */, "Expected HRESULT_FROM_WIN32(ERROR_RESOURCE_TYPE_NOT_FOUND), got %x\n", hr); hr = pLoadIconWithScaleDown(NULL, nonexisting_fileW, 32, 32, &icon); @@ -343,23 +342,32 @@ static void test_LoadIconWithScaleDown(void) FreeLibrary(hinst); } -static void check_class( const char *name, int must_exist, UINT style, UINT ignore ) +static void check_class( const char *name, int must_exist, UINT style, UINT ignore, BOOL v6 ) { WNDCLASSA wc; if (GetClassInfoA( 0, name, &wc )) { -todo_wine_if(strcmp(name, "Button") && - strcmp(name, "ComboBox") && - strcmp(name, "Edit") && - strcmp(name, "Static") && - strcmp(name, "ListBox") && - strcmp(name, "ComboLBox")) + char buff[64]; + HWND hwnd; + +todo_wine_if(!strcmp(name, "SysLink") && !must_exist && !v6) + ok( must_exist, "System class %s should %sexist\n", name, must_exist ? "" : "NOT " ); + if (!must_exist) return; + +todo_wine_if(!strcmp(name, "ScrollBar") || (!strcmp(name, "tooltips_class32") && v6)) ok( !(~wc.style & style & ~ignore), "System class %s is missing bits %x (%08x/%08x)\n", name, ~wc.style & style, wc.style, style ); +todo_wine_if((!strcmp(name, "tooltips_class32") && v6) || !strcmp(name, "SysLink")) ok( !(wc.style & ~style), "System class %s has extra bits %x (%08x/%08x)\n", name, wc.style & ~style, wc.style, style ); ok( !wc.hInstance, "System class %s has hInstance %p\n", name, wc.hInstance ); + + hwnd = CreateWindowA(name, 0, 0, 0, 0, 0, 0, 0, NULL, GetModuleHandleA(NULL), 0); + ok( hwnd != NULL, "Failed to create window for class %s.\n", name ); + GetClassNameA(hwnd, buff, ARRAY_SIZE(buff)); + ok( !strcmp(name, buff), "Unexpected class name %s, expected %s.\n", buff, name ); + DestroyWindow(hwnd); } else ok( !must_exist, "System class %s does not exist\n", name ); @@ -369,13 +377,40 @@ todo_wine_if(strcmp(name, "Button") && static void test_builtin_classes(void) { /* check style bits */ - check_class( "Button", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0 ); - check_class( "ComboBox", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0 ); - check_class( "Edit", 1, CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS, 0 ); - check_class( "ListBox", 1, CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS, 0 ); - check_class( "ScrollBar", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0 ); - check_class( "Static", 1, CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS, 0 ); - check_class( "ComboLBox", 1, CS_SAVEBITS | CS_DBLCLKS | CS_DROPSHADOW | CS_GLOBALCLASS, CS_DROPSHADOW ); + check_class( "Button", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE ); + check_class( "ComboBox", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE ); + check_class( "Edit", 1, CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE ); + check_class( "ListBox", 1, CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE ); + check_class( "ScrollBar", 1, CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE ); + check_class( "Static", 1, CS_PARENTDC | CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE ); + check_class( "ComboLBox", 1, CS_SAVEBITS | CS_DBLCLKS | CS_DROPSHADOW | CS_GLOBALCLASS, CS_DROPSHADOW, FALSE ); +} + +static void test_comctl32_classes(BOOL v6) +{ + check_class(ANIMATE_CLASSA, 1, CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE); + check_class(WC_COMBOBOXEXA, 1, CS_GLOBALCLASS, 0, FALSE); + check_class(DATETIMEPICK_CLASSA, 1, CS_GLOBALCLASS, 0, FALSE); + check_class(WC_HEADERA, 1, CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE); + check_class(HOTKEY_CLASSA, 1, CS_GLOBALCLASS, 0, FALSE); + check_class(WC_IPADDRESSA, 1, CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE); + check_class(WC_LISTVIEWA, 1, CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE); + check_class(MONTHCAL_CLASSA, 1, CS_GLOBALCLASS, 0, FALSE); + check_class(WC_NATIVEFONTCTLA, 1, CS_GLOBALCLASS, 0, FALSE); + check_class(WC_PAGESCROLLERA, 1, CS_GLOBALCLASS, 0, FALSE); + check_class(PROGRESS_CLASSA, 1, CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE); + check_class(REBARCLASSNAMEA, 1, CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE); + check_class(STATUSCLASSNAMEA, 1, CS_DBLCLKS | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE); + check_class(WC_TABCONTROLA, 1, CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE); + check_class(TOOLBARCLASSNAMEA, 1, CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE); + if (v6) + check_class(TOOLTIPS_CLASSA, 1, CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS | CS_DROPSHADOW, CS_SAVEBITS | CS_HREDRAW | CS_VREDRAW /* XP */, TRUE); + else + check_class(TOOLTIPS_CLASSA, 1, CS_DBLCLKS | CS_GLOBALCLASS | CS_SAVEBITS, CS_HREDRAW | CS_VREDRAW /* XP */, FALSE); + check_class(TRACKBAR_CLASSA, 1, CS_GLOBALCLASS, 0, FALSE); + check_class(WC_TREEVIEWA, 1, CS_DBLCLKS | CS_GLOBALCLASS, 0, FALSE); + check_class(UPDOWN_CLASSA, 1, CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS, 0, FALSE); + check_class("SysLink", v6, CS_GLOBALCLASS, 0, FALSE); } START_TEST(misc) @@ -389,9 +424,12 @@ START_TEST(misc) test_GetPtrAW(); test_Alloc(); + test_comctl32_classes(FALSE); + if (!load_v6_module(&ctx_cookie, &hCtx)) return; + test_comctl32_classes(TRUE); test_builtin_classes(); test_LoadIconWithScaleDown(); diff --git a/modules/rostests/winetests/comctl32/monthcal.c b/modules/rostests/winetests/comctl32/monthcal.c index 85822a320c6..11a637fb82f 100644 --- a/modules/rostests/winetests/comctl32/monthcal.c +++ b/modules/rostests/winetests/comctl32/monthcal.c @@ -1216,7 +1216,7 @@ if (0) } else { title_index++; - if (sizeof(title_hits) / sizeof(title_hits[0]) <= title_index) + if (ARRAY_SIZE(title_hits) <= title_index) break; todo_wine_if(title_hits[title_index].todo) @@ -1241,8 +1241,7 @@ if (0) todo_wine ok(month_count + year_count >= 1, "Not enough month and year items\n"); - ok(r.right <= x && title_index + 1 == sizeof(title_hits) / sizeof(title_hits[0]), - "Wrong title layout\n"); + ok(r.right <= x && title_index + 1 == ARRAY_SIZE(title_hits), "Wrong title layout\n"); DestroyWindow(hwnd); } @@ -1799,7 +1798,7 @@ static void test_hittest_v6(void) mchit.iOffset = -1; mchit.iCol = mchit.iRow = -1; mchit.uHit = 0; - mchit.rc.left = mchit.rc.right = mchit.rc.top = mchit.rc.bottom = -1; + SetRect(&mchit.rc, -1, -1, -1, -1); ret = SendMessageA(hwnd, MCM_HITTEST, 0, (LPARAM)&mchit); expect_hex(MCHT_CALENDARDATE, ret); expect_hex(MCHT_CALENDARDATE, mchit.uHit); @@ -1816,7 +1815,7 @@ static void test_hittest_v6(void) mchit.iOffset = -1; mchit.iCol = mchit.iRow = -1; mchit.uHit = 0; - mchit.rc.left = mchit.rc.right = mchit.rc.top = mchit.rc.bottom = -1; + SetRect(&mchit.rc, -1, -1, -1, -1); ret = SendMessageA(hwnd, MCM_HITTEST, 0, (LPARAM)&mchit); expect_hex(MCHT_TITLE, ret); expect_hex(MCHT_TITLE, mchit.uHit); @@ -1835,7 +1834,7 @@ static void test_hittest_v6(void) mchit.iOffset = -2; mchit.iCol = mchit.iRow = -2; mchit.uHit = ~0; - mchit.rc.left = mchit.rc.right = mchit.rc.top = mchit.rc.bottom = -1; + SetRect(&mchit.rc, -1, -1, -1, -1); ret = SendMessageA(hwnd, MCM_HITTEST, 0, (LPARAM)&mchit); todo_wine expect_hex(MCHT_NOWHERE, ret); todo_wine expect_hex(MCHT_NOWHERE, mchit.uHit); @@ -2016,7 +2015,7 @@ static void test_sel_notify(void) }; int i; - for(i = 0; i < sizeof styles / sizeof styles[0]; i++) + for(i = 0; i < ARRAY_SIZE(styles); i++) { hwnd = create_monthcal_control(styles[i].val); SetWindowLongPtrA(hwnd, GWLP_ID, SEL_NOTIFY_TEST_ID); diff --git a/modules/rostests/winetests/comctl32/mru.c b/modules/rostests/winetests/comctl32/mru.c index dfad6f859d4..b3d3e7a265b 100644 --- a/modules/rostests/winetests/comctl32/mru.c +++ b/modules/rostests/winetests/comctl32/mru.c @@ -118,7 +118,7 @@ static LSTATUS mru_RegDeleteTreeA(HKEY hKey, LPCSTR lpszSubKey) dwMaxSubkeyLen++; dwMaxValueLen++; dwMaxLen = max(dwMaxSubkeyLen, dwMaxValueLen); - if (dwMaxLen > sizeof(szNameBuf)/sizeof(CHAR)) + if (dwMaxLen > ARRAY_SIZE(szNameBuf)) { /* Name too big: alloc a buffer for it */ if (!(lpszName = heap_alloc(dwMaxLen * sizeof(CHAR)))) @@ -480,7 +480,7 @@ static void test_CreateMRUListLazyA(void) return; } - for (i = 0; i < sizeof(create_lazyA)/sizeof(create_lazya_t); i++) + for (i = 0; i < ARRAY_SIZE(create_lazyA); i++) { const create_lazya_t *ptr = &create_lazyA[i]; HANDLE hMRU; diff --git a/modules/rostests/winetests/comctl32/pager.c b/modules/rostests/winetests/comctl32/pager.c index 0da396cdd1c..f48dc039543 100644 --- a/modules/rostests/winetests/comctl32/pager.c +++ b/modules/rostests/winetests/comctl32/pager.c @@ -28,10 +28,195 @@ #define PAGER_SEQ_INDEX 0 static HWND parent_wnd, child1_wnd, child2_wnd; +static INT notify_format; +static BOOL notify_query_received; +static WCHAR test_w[] = {'t', 'e', 's', 't', 0}; +static CHAR test_a[] = {'t', 'e', 's', 't', 0}; +/* Double zero so that it's safe to cast it to WCHAR * */ +static CHAR te_a[] = {'t', 'e', 0, 0}; +static WCHAR empty_w[] = {0}; +static CHAR empty_a[] = {0}; +static CHAR large_a[] = "You should have received a copy of the GNU Lesser General Public License along with this ..."; +static WCHAR large_w[] = +{ + 'Y', 'o', 'u', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ', 'h', 'a', 'v', 'e', ' ', 'r', 'e', 'c', 'e', 'i', 'v', 'e', + 'd', ' ', 'a', ' ', 'c', 'o', 'p', 'y', ' ', 'o', 'f', ' ', 't', 'h', 'e', ' ', 'G', 'N', 'U', ' ', 'L', 'e', 's', + 's', 'e', 'r', ' ', 'G', 'e', 'n', 'e', 'r', 'a', 'l', ' ', 'P', 'u', 'b', 'l', 'i', 'c', ' ', 'L', 'i', 'c', 'e', + 'n', 's', 'e', ' ', 'a', 'l', 'o', 'n', 'g', ' ', 'w', 'i', 't', 'h', ' ', 't', 'h', 'i', 's', ' ', '.', '.', '.', 0 +}; +static WCHAR large_truncated_65_w[65] = +{ + 'Y', 'o', 'u', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ', 'h', 'a', 'v', 'e', ' ', 'r', 'e', 'c', 'e', 'i', 'v', + 'e', 'd', ' ', 'a', ' ', 'c', 'o', 'p', 'y', ' ', 'o', 'f', ' ', 't', 'h', 'e', ' ', 'G', 'N', 'U', ' ', 'L', + 'e', 's', 's', 'e', 'r', ' ', 'G', 'e', 'n', 'e', 'r', 'a', 'l', ' ', 'P', 'u', 'b', 'l', 'i', 'c', 0 +}; +static WCHAR large_truncated_80_w[80] = +{ + 'Y', 'o', 'u', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ', 'h', 'a', 'v', 'e', ' ', 'r', 'e', 'c', 'e', + 'i', 'v', 'e', 'd', ' ', 'a', ' ', 'c', 'o', 'p', 'y', ' ', 'o', 'f', ' ', 't', 'h', 'e', ' ', 'G', + 'N', 'U', ' ', 'L', 'e', 's', 's', 'e', 'r', ' ', 'G', 'e', 'n', 'e', 'r', 'a', 'l', ' ', 'P', 'u', + 'b', 'l', 'i', 'c', ' ', 'L', 'i', 'c', 'e', 'n', 's', 'e', ' ', 'a', 'l', 'o', 'n', 'g', ' ', 'w' +}; +static WCHAR buffer[64]; + +/* Text field conversion test behavior flags. */ +enum test_conversion_flags +{ + CONVERT_SEND = 0x01, + DONT_CONVERT_SEND = 0x02, + CONVERT_RECEIVE = 0x04, + DONT_CONVERT_RECEIVE = 0x08, + SEND_EMPTY_IF_NULL = 0x10, + DONT_SEND_EMPTY_IF_NULL = 0x20, + SET_NULL_IF_NO_MASK = 0x40, + ZERO_SEND = 0x80 +}; + +enum handler_ids +{ + TVITEM_NEW_HANDLER, + TVITEM_OLD_HANDLER +}; + +static struct notify_test_info +{ + UINT unicode; + UINT ansi; + UINT_PTR id_from; + HWND hwnd_from; + /* Whether parent received notification */ + BOOL received; + UINT test_id; + UINT sub_test_id; + UINT handler_id; + /* Text field conversion test behavior flag */ + DWORD flags; +} notify_test_info; + +struct notify_test_send +{ + /* Data sent to pager */ + WCHAR *send_text; + INT send_text_size; + INT send_text_max; + /* Data expected by parent of pager */ + void *expect_text; +}; + +struct notify_test_receive +{ + /* Data sent to pager */ + WCHAR *send_text; + INT send_text_size; + INT send_text_max; + /* Data for parent to write */ + CHAR *write_pointer; + CHAR *write_text; + INT write_text_size; + INT write_text_max; + /* Data when message returned */ + void *return_text; + INT return_text_max; +}; + +struct generic_text_helper_para +{ + void *ptr; + size_t size; + UINT *mask; + UINT required_mask; + WCHAR **text; + INT *text_max; + UINT code_unicode; + UINT code_ansi; + DWORD flags; + UINT handler_id; +}; + +static const struct notify_test_send test_convert_send_data[] = +{ + {test_w, sizeof(test_w), ARRAY_SIZE(buffer), test_a} +}; + +static const struct notify_test_send test_dont_convert_send_data[] = +{ + {test_w, sizeof(test_w), ARRAY_SIZE(buffer), test_w} +}; + +static const struct notify_test_receive test_convert_receive_data[] = +{ + {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), NULL, test_a, sizeof(test_a), -1, test_w, ARRAY_SIZE(buffer)}, + {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), test_a, NULL, 0, -1, test_w, ARRAY_SIZE(buffer)}, + {NULL, sizeof(empty_w), ARRAY_SIZE(buffer), test_a, NULL, 0, -1, NULL, ARRAY_SIZE(buffer)}, + {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), large_a, NULL, 0, -1, large_truncated_65_w, ARRAY_SIZE(buffer)}, + {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), empty_a, 0, 0, 1, empty_w, 1}, +}; + +static const struct notify_test_receive test_dont_convert_receive_data[] = +{ + {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), NULL, test_a, sizeof(test_a), -1, test_a, ARRAY_SIZE(buffer)}, + {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), test_a, NULL, 0, -1, test_a, ARRAY_SIZE(buffer)}, +}; + +static const struct notify_test_tooltip +{ + /* Data for parent to write */ + CHAR *write_sztext; + INT write_sztext_size; + CHAR *write_lpsztext; + HMODULE write_hinst; + /* Data when message returned */ + WCHAR *return_sztext; + INT return_sztext_size; + WCHAR *return_lpsztext; + HMODULE return_hinst; + /* Data expected by parent */ + CHAR *expect_sztext; + /* Data send to parent */ + WCHAR *send_sztext; + INT send_sztext_size; + WCHAR *send_lpsztext; +} test_tooltip_data[] = +{ + {NULL, 0, NULL, NULL, empty_w, -1, empty_w}, + {test_a, sizeof(test_a), NULL, NULL, test_w, -1, test_w}, + {test_a, sizeof(test_a), test_a, NULL, test_w, -1, test_w}, + {test_a, sizeof(test_a), (CHAR *)1, (HMODULE)0xdeadbeef, empty_w, -1, (WCHAR *)1, (HMODULE)0xdeadbeef}, + {test_a, sizeof(test_a), test_a, (HMODULE)0xdeadbeef, test_w, -1, test_w, (HMODULE)0xdeadbeef}, + {NULL, 0, test_a, NULL, test_w, -1, test_w}, + {test_a, 2, test_a, NULL, test_w, -1, test_w}, + {NULL, 0, NULL, NULL, test_w, -1, test_w, NULL, test_a, test_w, sizeof(test_w)}, + {NULL, 0, NULL, NULL, empty_w, -1, empty_w, NULL, empty_a, NULL, 0, test_w}, + {NULL, 0, large_a, NULL, large_truncated_80_w, sizeof(large_truncated_80_w), large_w} +}; + +static const struct notify_test_datetime_format +{ + /* Data send to parent */ + WCHAR *send_pszformat; + /* Data expected by parent */ + CHAR *expect_pszformat; + /* Data for parent to write */ + CHAR *write_szdisplay; + INT write_szdisplay_size; + CHAR *write_pszdisplay; + /* Data when message returned */ + WCHAR *return_szdisplay; + INT return_szdisplay_size; + WCHAR *return_pszdisplay; +} test_datetime_format_data[] = +{ + {test_w, test_a}, + {NULL, NULL, NULL, 0, test_a, empty_w, -1, test_w}, + {NULL, NULL, test_a, sizeof(test_a), NULL, test_w, -1, test_w}, + {NULL, NULL, test_a, 2, test_a, (WCHAR *)te_a, -1, test_w}, + {NULL, NULL, NULL, 0, large_a, NULL, 0, large_w} +}; #define CHILD1_ID 1 #define CHILD2_ID 2 +static BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*); static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR); static struct msg_sequence *sequences[NUM_MSG_SEQUENCES]; @@ -97,6 +282,14 @@ static const struct message set_pos_empty_seq[] = { { 0 } }; +static CHAR *heap_strdup(const CHAR *str) +{ + int len = lstrlenA(str) + 1; + CHAR *ret = heap_alloc(len * sizeof(CHAR)); + lstrcpyA(ret, str); + return ret; +} + static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static LONG defwndproc_counter = 0; @@ -257,11 +450,7 @@ static void test_pager(void) RECT rect, rect2; pager = create_pager_control( PGS_HORZ ); - if (!pager) - { - win_skip( "Pager control not supported\n" ); - return; - } + ok(pager != NULL, "Fail to create pager\n"); register_child_wnd_class(); @@ -333,11 +522,809 @@ static void test_pager(void) DestroyWindow( pager ); } -START_TEST(pager) +static LRESULT WINAPI test_notifyformat_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { - HMODULE mod = GetModuleHandleA("comctl32.dll"); + switch (message) + { + case WM_NOTIFYFORMAT: + if (lParam == NF_QUERY) + { + notify_query_received = TRUE; + return notify_format; + } + else if (lParam == NF_REQUERY) + return SendMessageA(GetParent(hwnd), WM_NOTIFYFORMAT, (WPARAM)hwnd, NF_QUERY); + else + return 0; + default: + return notify_format == NFR_UNICODE ? DefWindowProcW(hwnd, message, wParam, lParam) + : DefWindowProcA(hwnd, message, wParam, lParam); + } +} + +static BOOL register_notifyformat_class(void) +{ + static const WCHAR class_w[] = {'P', 'a', 'g', 'e', 'r', ' ', 'n', 'o', 't', 'i', 'f', 'y', 'f', + 'o', 'r', 'm', 'a', 't', ' ', 'c', 'l', 'a', 's', 's', 0}; + WNDCLASSW cls = {0}; + + cls.lpfnWndProc = test_notifyformat_proc; + cls.hInstance = GetModuleHandleW(NULL); + cls.lpszClassName = class_w; + return RegisterClassW(&cls); +} + +static void test_wm_notifyformat(void) +{ + static const WCHAR class_w[] = {'P', 'a', 'g', 'e', 'r', ' ', 'n', 'o', 't', 'i', 'f', 'y', 'f', + 'o', 'r', 'm', 'a', 't', ' ', 'c', 'l', 'a', 's', 's', 0}; + static const WCHAR parent_w[] = {'p', 'a', 'r', 'e', 'n', 't', 0}; + static const WCHAR pager_w[] = {'p', 'a', 'g', 'e', 'r', 0}; + static const WCHAR child_w[] = {'c', 'h', 'i', 'l', 'd', 0}; + static const INT formats[] = {NFR_UNICODE, NFR_ANSI}; + HWND parent, pager, child; + LRESULT ret; + INT i; + + ok(register_notifyformat_class(), "Register test class failed, error 0x%08x\n", GetLastError()); + + for (i = 0; i < ARRAY_SIZE(formats); i++) + { + notify_format = formats[i]; + parent = CreateWindowW(class_w, parent_w, WS_OVERLAPPED, 0, 0, 100, 100, 0, 0, GetModuleHandleW(0), 0); + ok(parent != NULL, "CreateWindow failed\n"); + pager = CreateWindowW(WC_PAGESCROLLERW, pager_w, WS_CHILD, 0, 0, 100, 100, parent, 0, GetModuleHandleW(0), 0); + ok(pager != NULL, "CreateWindow failed\n"); + child = CreateWindowW(class_w, child_w, WS_CHILD, 0, 0, 100, 100, pager, 0, GetModuleHandleW(0), 0); + ok(child != NULL, "CreateWindow failed\n"); + SendMessageW(pager, PGM_SETCHILD, 0, (LPARAM)child); + + /* Test parent */ + notify_query_received = FALSE; + ret = SendMessageW(pager, WM_NOTIFYFORMAT, (WPARAM)parent, NF_REQUERY); + ok(ret == notify_format, "Expect %d, got %ld\n", notify_format, ret); + ok(notify_query_received, "Didn't receive notify\n"); + + /* Send NF_QUERY directly to parent */ + notify_query_received = FALSE; + ret = SendMessageW(parent, WM_NOTIFYFORMAT, (WPARAM)pager, NF_QUERY); + ok(ret == notify_format, "Expect %d, got %ld\n", notify_format, ret); + ok(notify_query_received, "Didn't receive notify\n"); + + /* Pager send notifications to its parent regardless of wParam */ + notify_query_received = FALSE; + ret = SendMessageW(pager, WM_NOTIFYFORMAT, (WPARAM)parent_wnd, NF_REQUERY); + ok(ret == notify_format, "Expect %d, got %ld\n", notify_format, ret); + ok(notify_query_received, "Didn't receive notify\n"); + + /* Pager always wants Unicode notifications from children */ + ret = SendMessageW(child, WM_NOTIFYFORMAT, (WPARAM)pager, NF_REQUERY); + ok(ret == NFR_UNICODE, "Expect %d, got %ld\n", NFR_UNICODE, ret); + ret = SendMessageW(pager, WM_NOTIFYFORMAT, (WPARAM)child, NF_QUERY); + ok(ret == NFR_UNICODE, "Expect %d, got %ld\n", NFR_UNICODE, ret); + + DestroyWindow(parent); + } + + UnregisterClassW(class_w, GetModuleHandleW(NULL)); +} + +static void notify_generic_text_handler(CHAR **text, INT *text_max) +{ + const struct notify_test_send *send_data; + const struct notify_test_receive *receive_data; + + switch (notify_test_info.test_id) + { + case CONVERT_SEND: + case DONT_CONVERT_SEND: + { + send_data = (notify_test_info.test_id == CONVERT_SEND ? test_convert_send_data : test_dont_convert_send_data) + + notify_test_info.sub_test_id; + if (notify_test_info.flags & ZERO_SEND) + ok(!lstrcmpA(*text, empty_a), "Code 0x%08x test 0x%08x sub test %d expect empty text, got %s\n", + notify_test_info.unicode, notify_test_info.test_id, notify_test_info.sub_test_id, *text); + else if (notify_test_info.flags & CONVERT_SEND) + ok(!lstrcmpA(send_data->expect_text, *text), "Code 0x%08x test 0x%08x sub test %d expect %s, got %s\n", + notify_test_info.unicode, notify_test_info.test_id, notify_test_info.sub_test_id, + (CHAR *)send_data->expect_text, *text); + else + ok(!lstrcmpW((WCHAR *)send_data->expect_text, (WCHAR *)*text), + "Code 0x%08x test 0x%08x sub test %d expect %s, got %s\n", notify_test_info.unicode, + notify_test_info.test_id, notify_test_info.sub_test_id, wine_dbgstr_w((WCHAR *)send_data->expect_text), + wine_dbgstr_w((WCHAR *)*text)); + if (text_max) + ok(*text_max == send_data->send_text_max, "Code 0x%08x test 0x%08x sub test %d expect %d, got %d\n", + notify_test_info.unicode, notify_test_info.test_id, notify_test_info.sub_test_id, + send_data->send_text_max, *text_max); + break; + } + case CONVERT_RECEIVE: + case DONT_CONVERT_RECEIVE: + { + receive_data = (notify_test_info.test_id == CONVERT_RECEIVE ? test_convert_receive_data : test_dont_convert_receive_data) + + notify_test_info.sub_test_id; + if (text_max) + ok(*text_max == receive_data->send_text_max, "Code 0x%08x test 0x%08x sub test %d expect %d, got %d\n", + notify_test_info.unicode, notify_test_info.test_id, notify_test_info.sub_test_id, + receive_data->send_text_max, *text_max); + + if (receive_data->write_text) + memcpy(*text, receive_data->write_text, receive_data->write_text_size); + /* 64bit Windows will try to free the text pointer even if it's application provided when handling + * HDN_GETDISPINFOW. Deliberate leak here. */ + else if(notify_test_info.unicode == HDN_GETDISPINFOW) + *text = heap_strdup(receive_data->write_pointer); + else + *text = receive_data->write_pointer; + if (text_max && receive_data->write_text_max != -1) *text_max = receive_data->write_text_max; + break; + } + case SEND_EMPTY_IF_NULL: + ok(!lstrcmpA(*text, empty_a), "Code 0x%08x test 0x%08x sub test %d expect empty text, got %s\n", + notify_test_info.unicode, notify_test_info.test_id, notify_test_info.sub_test_id, *text); + break; + case DONT_SEND_EMPTY_IF_NULL: + ok(!*text, "Code 0x%08x test 0x%08x sub test %d expect null text\n", notify_test_info.unicode, + notify_test_info.test_id, notify_test_info.sub_test_id); + break; + } +} + +static void notify_tooltip_handler(NMTTDISPINFOA *nm) +{ + const struct notify_test_tooltip *data = test_tooltip_data + notify_test_info.sub_test_id; + ok(nm->lpszText == nm->szText, "Sub test %d expect %p, got %p\n", notify_test_info.sub_test_id, nm->szText, + nm->lpszText); + if (data->expect_sztext) + ok(!lstrcmpA(data->expect_sztext, nm->szText), "Sub test %d expect %s, got %s\n", notify_test_info.sub_test_id, + data->expect_sztext, nm->szText); + if (data->write_sztext) memcpy(nm->szText, data->write_sztext, data->write_sztext_size); + if (data->write_lpsztext) nm->lpszText = data->write_lpsztext; + if (data->write_hinst) nm->hinst = data->write_hinst; +} + +static void notify_datetime_handler(NMDATETIMEFORMATA *nm) +{ + const struct notify_test_datetime_format *data = test_datetime_format_data + notify_test_info.sub_test_id; + if (data->expect_pszformat) + ok(!lstrcmpA(data->expect_pszformat, nm->pszFormat), "Sub test %d expect %s, got %s\n", + notify_test_info.sub_test_id, data->expect_pszformat, nm->pszFormat); + ok(nm->pszDisplay == nm->szDisplay, "Test %d expect %p, got %p\n", notify_test_info.sub_test_id, nm->szDisplay, + nm->pszDisplay); + if (data->write_szdisplay) memcpy(nm->szDisplay, data->write_szdisplay, data->write_szdisplay_size); + if (data->write_pszdisplay) nm->pszDisplay = data->write_pszdisplay; +} + +static LRESULT WINAPI test_notify_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + static const WCHAR test[] = {'t', 'e', 's', 't', 0}; + switch (message) + { + case WM_NOTIFY: + { + NMHDR *hdr = (NMHDR *)lParam; + + /* Not notifications we want to test */ + if (!notify_test_info.unicode) break; + ok(!notify_test_info.received, "Extra notification received\n"); + + ok(wParam == notify_test_info.id_from, "Expect %ld, got %ld\n", notify_test_info.id_from, wParam); + ok(hdr->code == notify_test_info.ansi, "Expect 0x%08x, got 0x%08x\n", notify_test_info.ansi, hdr->code); + ok(hdr->idFrom == notify_test_info.id_from, "Expect %ld, got %ld\n", notify_test_info.id_from, wParam); + ok(hdr->hwndFrom == notify_test_info.hwnd_from, "Expect %p, got %p\n", notify_test_info.hwnd_from, hdr->hwndFrom); + + if (hdr->code != notify_test_info.ansi) + { + skip("Notification code mismatch, skipping lParam check\n"); + return 0; + } + switch (hdr->code) + { + /* ComboBoxEx */ + case CBEN_INSERTITEM: + case CBEN_DELETEITEM: + { + NMCOMBOBOXEXW *nmcbe = (NMCOMBOBOXEXW *)hdr; + notify_generic_text_handler((CHAR **)&nmcbe->ceItem.pszText, NULL); + break; + } + case CBEN_DRAGBEGINA: + { + NMCBEDRAGBEGINA *nmcbedb = (NMCBEDRAGBEGINA *)hdr; + ok(!lstrcmpA(nmcbedb->szText, test_a), "Expect %s, got %s\n", nmcbedb->szText, test_a); + break; + } + case CBEN_ENDEDITA: + { + NMCBEENDEDITA *nmcbeed = (NMCBEENDEDITA *)hdr; + ok(!lstrcmpA(nmcbeed->szText, test_a), "Expect %s, got %s\n", nmcbeed->szText, test_a); + break; + } + case CBEN_GETDISPINFOA: + { + NMCOMBOBOXEXA *nmcbe = (NMCOMBOBOXEXA *)hdr; + notify_generic_text_handler(&nmcbe->ceItem.pszText, &nmcbe->ceItem.cchTextMax); + break; + } + /* Date and Time Picker */ + case DTN_FORMATA: + { + notify_datetime_handler((NMDATETIMEFORMATA *)hdr); + break; + } + case DTN_FORMATQUERYA: + { + NMDATETIMEFORMATQUERYA *nmdtfq = (NMDATETIMEFORMATQUERYA *)hdr; + notify_generic_text_handler((CHAR **)&nmdtfq->pszFormat, NULL); + break; + } + case DTN_WMKEYDOWNA: + { + NMDATETIMEWMKEYDOWNA *nmdtkd = (NMDATETIMEWMKEYDOWNA *)hdr; + notify_generic_text_handler((CHAR **)&nmdtkd->pszFormat, NULL); + break; + } + case DTN_USERSTRINGA: + { + NMDATETIMESTRINGA *nmdts = (NMDATETIMESTRINGA *)hdr; + notify_generic_text_handler((CHAR **)&nmdts->pszUserString, NULL); + break; + } + /* Header */ + case HDN_BEGINDRAG: + case HDN_ENDDRAG: + case HDN_BEGINFILTEREDIT: + case HDN_ENDFILTEREDIT: + case HDN_DROPDOWN: + case HDN_FILTERCHANGE: + case HDN_ITEMKEYDOWN: + case HDN_ITEMSTATEICONCLICK: + case HDN_OVERFLOWCLICK: + { + NMHEADERW *nmhd = (NMHEADERW *)hdr; + ok(!lstrcmpW(nmhd->pitem->pszText, test_w), "Expect %s, got %s\n", wine_dbgstr_w(test_w), + wine_dbgstr_w(nmhd->pitem->pszText)); + ok(!lstrcmpW(((HD_TEXTFILTERW *)nmhd->pitem->pvFilter)->pszText, test_w), "Expect %s, got %s\n", + wine_dbgstr_w(test_w), wine_dbgstr_w(((HD_TEXTFILTERW *)nmhd->pitem->pvFilter)->pszText)); + break; + } + case HDN_BEGINTRACKA: + case HDN_DIVIDERDBLCLICKA: + case HDN_ENDTRACKA: + case HDN_ITEMCHANGEDA: + case HDN_ITEMCHANGINGA: + case HDN_ITEMCLICKA: + case HDN_ITEMDBLCLICKA: + case HDN_TRACKA: + { + NMHEADERA *nmhd = (NMHEADERA *)hdr; + ok(!lstrcmpA(nmhd->pitem->pszText, test_a), "Expect %s, got %s\n", test_a, nmhd->pitem->pszText); + ok(!lstrcmpA(((HD_TEXTFILTERA *)nmhd->pitem->pvFilter)->pszText, test_a), "Expect %s, got %s\n", test_a, + ((HD_TEXTFILTERA *)nmhd->pitem->pvFilter)->pszText); + break; + } + case HDN_GETDISPINFOA: + { + NMHDDISPINFOA *nmhddi = (NMHDDISPINFOA *)hdr; + notify_generic_text_handler(&nmhddi->pszText, &nmhddi->cchTextMax); + break; + } + /* List View */ + case LVN_BEGINLABELEDITA: + case LVN_ENDLABELEDITA: + case LVN_GETDISPINFOA: + case LVN_SETDISPINFOA: + { + NMLVDISPINFOA *nmlvdi = (NMLVDISPINFOA *)hdr; + notify_generic_text_handler(&nmlvdi->item.pszText, &nmlvdi->item.cchTextMax); + break; + } + case LVN_GETINFOTIPA: + { + NMLVGETINFOTIPA *nmlvgit = (NMLVGETINFOTIPA *)hdr; + notify_generic_text_handler(&nmlvgit->pszText, &nmlvgit->cchTextMax); + break; + } + case LVN_INCREMENTALSEARCHA: + case LVN_ODFINDITEMA: + { + NMLVFINDITEMA *nmlvfi = (NMLVFINDITEMA *)hdr; + notify_generic_text_handler((CHAR **)&nmlvfi->lvfi.psz, NULL); + break; + } + /* Toolbar */ + case TBN_SAVE: + { + NMTBSAVE *nmtbs = (NMTBSAVE *)hdr; + notify_generic_text_handler((CHAR **)&nmtbs->tbButton.iString, NULL); + break; + } + case TBN_RESTORE: + { + NMTBRESTORE *nmtbr = (NMTBRESTORE *)hdr; + notify_generic_text_handler((CHAR **)&nmtbr->tbButton.iString, NULL); + break; + } + case TBN_GETBUTTONINFOA: + { + NMTOOLBARA *nmtb = (NMTOOLBARA *)hdr; + notify_generic_text_handler(&nmtb->pszText, &nmtb->cchText); + break; + } + case TBN_GETDISPINFOW: + { + NMTBDISPINFOW *nmtbdi = (NMTBDISPINFOW *)hdr; + notify_generic_text_handler((CHAR **)&nmtbdi->pszText, &nmtbdi->cchText); + break; + } + case TBN_GETINFOTIPA: + { + NMTBGETINFOTIPA *nmtbgit = (NMTBGETINFOTIPA *)hdr; + notify_generic_text_handler(&nmtbgit->pszText, &nmtbgit->cchTextMax); + break; + } + /* Tooltip */ + case TTN_GETDISPINFOA: + { + notify_tooltip_handler((NMTTDISPINFOA *)hdr); + break; + } + /* Tree View */ + case TVN_BEGINLABELEDITA: + case TVN_ENDLABELEDITA: + case TVN_GETDISPINFOA: + case TVN_SETDISPINFOA: + { + NMTVDISPINFOA *nmtvdi = (NMTVDISPINFOA *)hdr; + notify_generic_text_handler(&nmtvdi->item.pszText, &nmtvdi->item.cchTextMax); + break; + } + case TVN_GETINFOTIPA: + { + NMTVGETINFOTIPA *nmtvgit = (NMTVGETINFOTIPA *)hdr; + notify_generic_text_handler(&nmtvgit->pszText, &nmtvgit->cchTextMax); + break; + } + case TVN_SINGLEEXPAND: + case TVN_BEGINDRAGA: + case TVN_BEGINRDRAGA: + case TVN_ITEMEXPANDEDA: + case TVN_ITEMEXPANDINGA: + case TVN_DELETEITEMA: + case TVN_SELCHANGINGA: + case TVN_SELCHANGEDA: + { + NMTREEVIEWA *nmtv = (NMTREEVIEWA *)hdr; + if (notify_test_info.handler_id == TVITEM_NEW_HANDLER) + notify_generic_text_handler((CHAR **)&nmtv->itemNew.pszText, &nmtv->itemNew.cchTextMax); + else + notify_generic_text_handler((CHAR **)&nmtv->itemOld.pszText, &nmtv->itemOld.cchTextMax); + break; + } + + default: + ok(0, "Unexpected message 0x%08x\n", hdr->code); + } + notify_test_info.received = TRUE; + ok(!lstrcmpA(test_a, "test"), "test_a got modified\n"); + ok(!lstrcmpW(test_w, test), "test_w got modified\n"); + return 0; + } + case WM_NOTIFYFORMAT: + if (lParam == NF_QUERY) return NFR_ANSI; + break; + } + return DefWindowProcA(hwnd, message, wParam, lParam); +} + +static BOOL register_test_notify_class(void) +{ + WNDCLASSA cls = {0}; + + cls.lpfnWndProc = test_notify_proc; + cls.hInstance = GetModuleHandleA(NULL); + cls.lpszClassName = "Pager notify class"; + return RegisterClassA(&cls); +} + +static void send_notify(HWND pager, UINT unicode, UINT ansi, LPARAM lParam, BOOL code_change) +{ + NMHDR *hdr = (NMHDR *)lParam; + + notify_test_info.unicode = unicode; + notify_test_info.id_from = 1; + notify_test_info.hwnd_from = child1_wnd; + notify_test_info.ansi = ansi; + notify_test_info.received = FALSE; + + hdr->code = unicode; + hdr->idFrom = 1; + hdr->hwndFrom = child1_wnd; + + SendMessageW(pager, WM_NOTIFY, hdr->idFrom, lParam); + ok(notify_test_info.received, "Expect notification received\n"); + ok(hdr->code == code_change ? ansi : unicode, "Expect 0x%08x, got 0x%08x\n", hdr->code, + code_change ? ansi : unicode); +} + +/* Send notify to test text field conversion. In parent proc notify_generic_text_handler() handles these messages */ +static void test_notify_generic_text_helper(HWND pager, const struct generic_text_helper_para *para) +{ + const struct notify_test_send *send_data; + const struct notify_test_receive *receive_data; + INT array_size; + INT i; + + notify_test_info.flags = para->flags; + notify_test_info.handler_id = para->handler_id; + + if (para->flags & (CONVERT_SEND | DONT_CONVERT_SEND)) + { + if (para->flags & CONVERT_SEND) + { + notify_test_info.test_id = CONVERT_SEND; + send_data = test_convert_send_data; + array_size = ARRAY_SIZE(test_convert_send_data); + } + else + { + notify_test_info.test_id = DONT_CONVERT_SEND; + send_data = test_dont_convert_send_data; + array_size = ARRAY_SIZE(test_dont_convert_send_data); + } + + for (i = 0; i < array_size; i++) + { + const struct notify_test_send *data = send_data + i; + notify_test_info.sub_test_id = i; + + memset(para->ptr, 0, para->size); + if (para->mask) *para->mask = para->required_mask; + if (data->send_text) + { + memcpy(buffer, data->send_text, data->send_text_size); + *para->text = buffer; + } + if (para->text_max) *para->text_max = data->send_text_max; + send_notify(pager, para->code_unicode, para->code_ansi, (LPARAM)para->ptr, TRUE); + } + } + + if (para->flags & (CONVERT_RECEIVE | DONT_CONVERT_RECEIVE)) + { + if (para->flags & CONVERT_RECEIVE) + { + notify_test_info.test_id = CONVERT_RECEIVE; + receive_data = test_convert_receive_data; + array_size = ARRAY_SIZE(test_convert_receive_data); + } + else + { + notify_test_info.test_id = DONT_CONVERT_RECEIVE; + receive_data = test_dont_convert_receive_data; + array_size = ARRAY_SIZE(test_dont_convert_receive_data); + } + + for (i = 0; i < array_size; i++) + { + const struct notify_test_receive *data = receive_data + i; + notify_test_info.sub_test_id = i; + + memset(para->ptr, 0, para->size); + if (para->mask) *para->mask = para->required_mask; + if (data->send_text) + { + memcpy(buffer, data->send_text, data->send_text_size); + *para->text = buffer; + } + if (para->text_max) *para->text_max = data->send_text_max; + send_notify(pager, para->code_unicode, para->code_ansi, (LPARAM)para->ptr, TRUE); + if (data->return_text) + { + if (para->flags & CONVERT_RECEIVE) + ok(!lstrcmpW(data->return_text, *para->text), "Code 0x%08x sub test %d expect %s, got %s\n", + para->code_unicode, i, wine_dbgstr_w((WCHAR *)data->return_text), wine_dbgstr_w(*para->text)); + else + ok(!lstrcmpA(data->return_text, (CHAR *)*para->text), "Code 0x%08x sub test %d expect %s, got %s\n", + para->code_unicode, i, (CHAR *)data->return_text, (CHAR *)*para->text); + } + if (para->text_max) + ok(data->return_text_max == *para->text_max, "Code 0x%08x sub test %d expect %d, got %d\n", + para->code_unicode, i, data->return_text_max, *para->text_max); + } + } + + /* Extra tests for other behavior flags that are not worth it to create their own test arrays */ + memset(para->ptr, 0, para->size); + if (para->mask) *para->mask = para->required_mask; + if (para->text_max) *para->text_max = 1; + if (para->flags & SEND_EMPTY_IF_NULL) + notify_test_info.test_id = SEND_EMPTY_IF_NULL; + else + notify_test_info.test_id = DONT_SEND_EMPTY_IF_NULL; + send_notify(pager, para->code_unicode, para->code_ansi, (LPARAM)para->ptr, TRUE); + + notify_test_info.test_id = SET_NULL_IF_NO_MASK; + memset(para->ptr, 0, para->size); + memset(buffer, 0, sizeof(buffer)); + *para->text = buffer; + if (para->text_max) *para->text_max = ARRAY_SIZE(buffer); + send_notify(pager, para->code_unicode, para->code_ansi, (LPARAM)para->ptr, TRUE); + if(para->flags & SET_NULL_IF_NO_MASK) + ok(!*para->text, "Expect null text\n"); +} + +static void test_wm_notify_comboboxex(HWND pager) +{ + static NMCBEDRAGBEGINW nmcbedb; + static NMCBEENDEDITW nmcbeed; + + /* CBEN_DRAGBEGIN */ + memset(&nmcbedb, 0, sizeof(nmcbedb)); + memcpy(nmcbedb.szText, test_w, sizeof(test_w)); + send_notify(pager, CBEN_DRAGBEGINW, CBEN_DRAGBEGINA, (LPARAM)&nmcbedb, FALSE); + ok(!lstrcmpW(nmcbedb.szText, test_w), "Expect %s, got %s\n", wine_dbgstr_w(test_w), wine_dbgstr_w(nmcbedb.szText)); + + /* CBEN_ENDEDIT */ + memset(&nmcbeed, 0, sizeof(nmcbeed)); + memcpy(nmcbeed.szText, test_w, sizeof(test_w)); + send_notify(pager, CBEN_ENDEDITW, CBEN_ENDEDITA, (LPARAM)&nmcbeed, FALSE); + ok(!lstrcmpW(nmcbeed.szText, test_w), "Expect %s, got %s\n", wine_dbgstr_w(test_w), wine_dbgstr_w(nmcbeed.szText)); +} + +static void test_wm_notify_datetime(HWND pager) +{ + const struct notify_test_datetime_format *data; + NMDATETIMEFORMATW nmdtf; + INT i; + + for (i = 0; i < ARRAY_SIZE(test_datetime_format_data); i++) + { + data = test_datetime_format_data + i; + notify_test_info.sub_test_id = i; + + memset(&nmdtf, 0, sizeof(nmdtf)); + if(data->send_pszformat) nmdtf.pszFormat = data->send_pszformat; + nmdtf.pszDisplay = nmdtf.szDisplay; + send_notify(pager, DTN_FORMATW, DTN_FORMATA, (LPARAM)&nmdtf, TRUE); + if (data->return_szdisplay) + ok(!lstrcmpW(nmdtf.szDisplay, data->return_szdisplay), "Sub test %d expect %s, got %s\n", i, + wine_dbgstr_w(data->return_szdisplay), wine_dbgstr_w(nmdtf.szDisplay)); + if (data->return_pszdisplay) + ok(!lstrcmpW(nmdtf.pszDisplay, data->return_pszdisplay), "Sub test %d expect %s, got %s\n", i, + wine_dbgstr_w(data->return_pszdisplay), wine_dbgstr_w(nmdtf.pszDisplay)); + } +} + +static void test_wm_notify_header(HWND pager) +{ + NMHEADERW nmh = {{0}}; + HDITEMW hdi = {0}; + HD_TEXTFILTERW hdtf = {0}; + + hdi.mask = HDI_TEXT | HDI_FILTER; + hdi.pszText = test_w; + hdtf.pszText = test_w; + nmh.pitem = &hdi; + nmh.pitem->pvFilter = &hdtf; + send_notify(pager, HDN_BEGINDRAG, HDN_BEGINDRAG, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ENDDRAG, HDN_ENDDRAG, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_BEGINFILTEREDIT, HDN_BEGINFILTEREDIT, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ENDFILTEREDIT, HDN_ENDFILTEREDIT, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_DROPDOWN, HDN_DROPDOWN, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_FILTERCHANGE, HDN_FILTERCHANGE, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ITEMKEYDOWN, HDN_ITEMKEYDOWN, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ITEMSTATEICONCLICK, HDN_ITEMSTATEICONCLICK, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_OVERFLOWCLICK, HDN_OVERFLOWCLICK, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_BEGINTRACKW, HDN_BEGINTRACKA, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_DIVIDERDBLCLICKW, HDN_DIVIDERDBLCLICKA, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ENDTRACKW, HDN_ENDTRACKA, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ITEMCHANGEDW, HDN_ITEMCHANGEDA, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ITEMCHANGINGW, HDN_ITEMCHANGINGA, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ITEMCLICKW, HDN_ITEMCLICKA, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_ITEMDBLCLICKW, HDN_ITEMDBLCLICKA, (LPARAM)&nmh, TRUE); + send_notify(pager, HDN_TRACKW, HDN_TRACKA, (LPARAM)&nmh, TRUE); +} + +static void test_wm_notify_tooltip(HWND pager) +{ + NMTTDISPINFOW nmttdi; + const struct notify_test_tooltip *data; + INT i; + + for (i = 0; i < ARRAY_SIZE(test_tooltip_data); i++) + { + data = test_tooltip_data + i; + notify_test_info.sub_test_id = i; + + memset(&nmttdi, 0, sizeof(nmttdi)); + if (data->send_sztext) memcpy(nmttdi.szText, data->send_sztext, data->send_sztext_size); + if (data->send_lpsztext) nmttdi.lpszText = data->send_lpsztext; + send_notify(pager, TTN_GETDISPINFOW, TTN_GETDISPINFOA, (LPARAM)&nmttdi, FALSE); + if (data->return_sztext) + { + if (data->return_sztext_size == -1) + ok(!lstrcmpW(nmttdi.szText, data->return_sztext), "Sub test %d expect %s, got %s\n", i, + wine_dbgstr_w(data->return_sztext), wine_dbgstr_w(nmttdi.szText)); + else + ok(!memcmp(nmttdi.szText, data->return_sztext, data->return_sztext_size), "Wrong szText content\n"); + } + if (data->return_lpsztext) + { + if (IS_INTRESOURCE(data->return_lpsztext)) + ok(nmttdi.lpszText == data->return_lpsztext, "Sub test %d expect %s, got %s\n", i, + wine_dbgstr_w(data->return_lpsztext), wine_dbgstr_w(nmttdi.lpszText)); + else + ok(!lstrcmpW(nmttdi.lpszText, data->return_lpsztext), "Test %d expect %s, got %s\n", i, + wine_dbgstr_w(data->return_lpsztext), wine_dbgstr_w(nmttdi.lpszText)); + } + if (data->return_hinst) + ok(nmttdi.hinst == data->return_hinst, "Sub test %d expect %p, got %p\n", i, data->return_hinst, + nmttdi.hinst); + } +} + +static void test_wm_notify(void) +{ + static const CHAR *class = "Pager notify class"; + HWND parent, pager; + /* Combo Box Ex */ + static NMCOMBOBOXEXW nmcbe; + /* Date and Time Picker */ + static NMDATETIMEFORMATQUERYW nmdtfq; + static NMDATETIMEWMKEYDOWNW nmdtkd; + static NMDATETIMESTRINGW nmdts; + /* Header */ + static NMHDDISPINFOW nmhddi; + /* List View */ + static NMLVDISPINFOW nmlvdi; + static NMLVGETINFOTIPW nmlvgit; + static NMLVFINDITEMW nmlvfi; + /* Tool Bar */ + static NMTBRESTORE nmtbr; + static NMTBSAVE nmtbs; + static NMTOOLBARW nmtb; + static NMTBDISPINFOW nmtbdi; + static NMTBGETINFOTIPW nmtbgit; + /* Tree View */ + static NMTVDISPINFOW nmtvdi; + static NMTVGETINFOTIPW nmtvgit; + static NMTREEVIEWW nmtv; + static const struct generic_text_helper_para paras[] = + { + /* Combo Box Ex */ + {&nmcbe, sizeof(nmcbe), &nmcbe.ceItem.mask, CBEIF_TEXT, &nmcbe.ceItem.pszText, &nmcbe.ceItem.cchTextMax, + CBEN_INSERTITEM, CBEN_INSERTITEM, DONT_CONVERT_SEND | DONT_CONVERT_RECEIVE}, + {&nmcbe, sizeof(nmcbe), &nmcbe.ceItem.mask, CBEIF_TEXT, &nmcbe.ceItem.pszText, &nmcbe.ceItem.cchTextMax, + CBEN_DELETEITEM, CBEN_DELETEITEM, DONT_CONVERT_SEND | DONT_CONVERT_RECEIVE}, + {&nmcbe, sizeof(nmcbe), &nmcbe.ceItem.mask, CBEIF_TEXT, &nmcbe.ceItem.pszText, &nmcbe.ceItem.cchTextMax, + CBEN_GETDISPINFOW, CBEN_GETDISPINFOA, ZERO_SEND | SET_NULL_IF_NO_MASK | DONT_CONVERT_SEND | CONVERT_RECEIVE}, + /* Date and Time Picker */ + {&nmdtfq, sizeof(nmdtfq), NULL, 0, (WCHAR **)&nmdtfq.pszFormat, NULL, DTN_FORMATQUERYW, DTN_FORMATQUERYA, + CONVERT_SEND}, + {&nmdtkd, sizeof(nmdtkd), NULL, 0, (WCHAR **)&nmdtkd.pszFormat, NULL, DTN_WMKEYDOWNW, DTN_WMKEYDOWNA, + CONVERT_SEND}, + {&nmdts, sizeof(nmdts), NULL, 0, (WCHAR **)&nmdts.pszUserString, NULL, DTN_USERSTRINGW, DTN_USERSTRINGA, + CONVERT_SEND}, + /* Header */ + {&nmhddi, sizeof(nmhddi), &nmhddi.mask, HDI_TEXT, &nmhddi.pszText, &nmhddi.cchTextMax, HDN_GETDISPINFOW, + HDN_GETDISPINFOA, SEND_EMPTY_IF_NULL | CONVERT_SEND | CONVERT_RECEIVE}, + /* List View */ + {&nmlvfi, sizeof(nmlvfi), &nmlvfi.lvfi.flags, LVFI_STRING, (WCHAR **)&nmlvfi.lvfi.psz, NULL, + LVN_INCREMENTALSEARCHW, LVN_INCREMENTALSEARCHA, CONVERT_SEND}, + {&nmlvfi, sizeof(nmlvfi), &nmlvfi.lvfi.flags, LVFI_SUBSTRING, (WCHAR **)&nmlvfi.lvfi.psz, NULL, LVN_ODFINDITEMW, + LVN_ODFINDITEMA, CONVERT_SEND}, + {&nmlvdi, sizeof(nmlvdi), &nmlvdi.item.mask, LVIF_TEXT, &nmlvdi.item.pszText, &nmlvdi.item.cchTextMax, + LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE}, + {&nmlvdi, sizeof(nmlvdi), &nmlvdi.item.mask, LVIF_TEXT, &nmlvdi.item.pszText, &nmlvdi.item.cchTextMax, + LVN_ENDLABELEDITW, LVN_ENDLABELEDITA, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE}, + {&nmlvdi, sizeof(nmlvdi), &nmlvdi.item.mask, LVIF_TEXT, &nmlvdi.item.pszText, &nmlvdi.item.cchTextMax, + LVN_GETDISPINFOW, LVN_GETDISPINFOA, DONT_CONVERT_SEND | CONVERT_RECEIVE}, + {&nmlvdi, sizeof(nmlvdi), &nmlvdi.item.mask, LVIF_TEXT, &nmlvdi.item.pszText, &nmlvdi.item.cchTextMax, + LVN_SETDISPINFOW, LVN_SETDISPINFOA, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE}, + {&nmlvgit, sizeof(nmlvgit), NULL, 0, &nmlvgit.pszText, &nmlvgit.cchTextMax, LVN_GETINFOTIPW, LVN_GETINFOTIPA, + CONVERT_SEND | CONVERT_RECEIVE}, + /* Tool Bar */ + {&nmtbs, sizeof(nmtbs), NULL, 0, (WCHAR **)&nmtbs.tbButton.iString, NULL, TBN_SAVE, TBN_SAVE, + DONT_CONVERT_SEND | DONT_CONVERT_RECEIVE}, + {&nmtbr, sizeof(nmtbr), NULL, 0, (WCHAR **)&nmtbr.tbButton.iString, NULL, TBN_RESTORE, TBN_RESTORE, + DONT_CONVERT_SEND | DONT_CONVERT_RECEIVE}, + {&nmtbdi, sizeof(nmtbdi), &nmtbdi.dwMask, TBNF_TEXT, &nmtbdi.pszText, &nmtbdi.cchText, TBN_GETDISPINFOW, + TBN_GETDISPINFOW, DONT_CONVERT_SEND | DONT_CONVERT_RECEIVE}, + {&nmtb, sizeof(nmtb), NULL, 0, &nmtb.pszText, &nmtb.cchText, TBN_GETBUTTONINFOW, TBN_GETBUTTONINFOA, + SEND_EMPTY_IF_NULL | CONVERT_SEND | CONVERT_RECEIVE}, + {&nmtbgit, sizeof(nmtbgit), NULL, 0, &nmtbgit.pszText, &nmtbgit.cchTextMax, TBN_GETINFOTIPW, TBN_GETINFOTIPA, + DONT_CONVERT_SEND | CONVERT_RECEIVE}, + /* Tree View */ + {&nmtvdi, sizeof(nmtvdi), &nmtvdi.item.mask, TVIF_TEXT, &nmtvdi.item.pszText, &nmtvdi.item.cchTextMax, + TVN_BEGINLABELEDITW, TVN_BEGINLABELEDITA, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE}, + {&nmtvdi, sizeof(nmtvdi), &nmtvdi.item.mask, TVIF_TEXT, &nmtvdi.item.pszText, &nmtvdi.item.cchTextMax, + TVN_ENDLABELEDITW, TVN_ENDLABELEDITA, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE}, + {&nmtvdi, sizeof(nmtvdi), &nmtvdi.item.mask, TVIF_TEXT, &nmtvdi.item.pszText, &nmtvdi.item.cchTextMax, + TVN_GETDISPINFOW, TVN_GETDISPINFOA, ZERO_SEND | DONT_CONVERT_SEND| CONVERT_RECEIVE}, + {&nmtvdi, sizeof(nmtvdi), &nmtvdi.item.mask, TVIF_TEXT, &nmtvdi.item.pszText, &nmtvdi.item.cchTextMax, + TVN_SETDISPINFOW, TVN_SETDISPINFOA, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE}, + {&nmtvgit, sizeof(nmtvgit), NULL, 0, &nmtvgit.pszText, &nmtvgit.cchTextMax, TVN_GETINFOTIPW, TVN_GETINFOTIPA, + DONT_CONVERT_SEND | CONVERT_RECEIVE}, + {&nmtv, sizeof(nmtv), &nmtv.itemNew.mask, TVIF_TEXT, &nmtv.itemNew.pszText, &nmtv.itemNew.cchTextMax, + TVN_SINGLEEXPAND, TVN_SINGLEEXPAND, DONT_CONVERT_SEND | DONT_CONVERT_RECEIVE, TVITEM_NEW_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, + TVN_SINGLEEXPAND, TVN_SINGLEEXPAND, DONT_CONVERT_SEND | DONT_CONVERT_RECEIVE, TVITEM_OLD_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemNew.mask, TVIF_TEXT, &nmtv.itemNew.pszText, &nmtv.itemNew.cchTextMax, + TVN_BEGINDRAGW, TVN_BEGINDRAGA, CONVERT_SEND, TVITEM_NEW_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, + TVN_BEGINDRAGW, TVN_BEGINDRAGA, DONT_CONVERT_SEND, TVITEM_OLD_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemNew.mask, TVIF_TEXT, &nmtv.itemNew.pszText, &nmtv.itemNew.cchTextMax, + TVN_BEGINRDRAGW, TVN_BEGINRDRAGA, CONVERT_SEND, TVITEM_NEW_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, + TVN_BEGINRDRAGW, TVN_BEGINRDRAGA, DONT_CONVERT_SEND, TVITEM_OLD_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemNew.mask, TVIF_TEXT, &nmtv.itemNew.pszText, &nmtv.itemNew.cchTextMax, + TVN_ITEMEXPANDEDW, TVN_ITEMEXPANDEDA, CONVERT_SEND, TVITEM_NEW_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, + TVN_ITEMEXPANDEDW, TVN_ITEMEXPANDEDA, DONT_CONVERT_SEND, TVITEM_OLD_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemNew.mask, TVIF_TEXT, &nmtv.itemNew.pszText, &nmtv.itemNew.cchTextMax, + TVN_ITEMEXPANDINGW, TVN_ITEMEXPANDINGA, CONVERT_SEND, TVITEM_NEW_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, + TVN_ITEMEXPANDINGW, TVN_ITEMEXPANDINGA, DONT_CONVERT_SEND, TVITEM_OLD_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemNew.mask, TVIF_TEXT, &nmtv.itemNew.pszText, &nmtv.itemNew.cchTextMax, + TVN_DELETEITEMW, TVN_DELETEITEMA, DONT_CONVERT_SEND, TVITEM_NEW_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, + TVN_DELETEITEMW, TVN_DELETEITEMA, CONVERT_SEND, TVITEM_OLD_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemNew.mask, TVIF_TEXT, &nmtv.itemNew.pszText, &nmtv.itemNew.cchTextMax, + TVN_SELCHANGINGW, TVN_SELCHANGINGA, CONVERT_SEND, TVITEM_NEW_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, + TVN_SELCHANGINGW, TVN_SELCHANGINGA, CONVERT_SEND, TVITEM_OLD_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemNew.mask, TVIF_TEXT, &nmtv.itemNew.pszText, &nmtv.itemNew.cchTextMax, + TVN_SELCHANGEDW, TVN_SELCHANGEDA, CONVERT_SEND, TVITEM_NEW_HANDLER}, + {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, + TVN_SELCHANGEDW, TVN_SELCHANGEDA, CONVERT_SEND, TVITEM_OLD_HANDLER} + }; + INT i; + + ok(register_test_notify_class(), "Register test class failed, error 0x%08x\n", GetLastError()); + + parent = CreateWindowA(class, "parent", WS_OVERLAPPED, 0, 0, 100, 100, 0, 0, GetModuleHandleA(0), 0); + ok(parent != NULL, "CreateWindow failed\n"); + pager = CreateWindowA(WC_PAGESCROLLERA, "pager", WS_CHILD, 0, 0, 100, 100, parent, 0, GetModuleHandleA(0), 0); + ok(pager != NULL, "CreateWindow failed\n"); + child1_wnd = CreateWindowA(class, "child", WS_CHILD, 0, 0, 100, 100, pager, (HMENU)1, GetModuleHandleA(0), 0); + ok(child1_wnd != NULL, "CreateWindow failed\n"); + SendMessageW(pager, PGM_SETCHILD, 0, (LPARAM)child1_wnd); + + for (i = 0; i < ARRAY_SIZE(paras); i++) + test_notify_generic_text_helper(pager, paras + i); + + /* Tests for those that can't be covered by generic text test helper */ + test_wm_notify_comboboxex(pager); + test_wm_notify_datetime(pager); + test_wm_notify_header(pager); + test_wm_notify_tooltip(pager); + + DestroyWindow(parent); + UnregisterClassA(class, GetModuleHandleA(NULL)); +} + +static void init_functions(void) +{ + HMODULE mod = LoadLibraryA("comctl32.dll"); + +#define X(f) p##f = (void*)GetProcAddress(mod, #f); + X(InitCommonControlsEx); +#undef X pSetWindowSubclass = (void*)GetProcAddress(mod, (LPSTR)410); +} + +START_TEST(pager) +{ + INITCOMMONCONTROLSEX iccex; + + init_functions(); + + iccex.dwSize = sizeof(iccex); + iccex.dwICC = ICC_PAGESCROLLER_CLASS; + pInitCommonControlsEx(&iccex); init_msg_sequences(sequences, NUM_MSG_SEQUENCES); @@ -345,4 +1332,8 @@ START_TEST(pager) ok(parent_wnd != NULL, "Failed to create parent window!\n"); test_pager(); + test_wm_notifyformat(); + test_wm_notify(); + + DestroyWindow(parent_wnd); } diff --git a/modules/rostests/winetests/comctl32/progress.c b/modules/rostests/winetests/comctl32/progress.c index b911021be0a..497cb47d3c3 100644 --- a/modules/rostests/winetests/comctl32/progress.c +++ b/modules/rostests/winetests/comctl32/progress.c @@ -258,7 +258,7 @@ static void test_PBM_STEPIT(void) HWND progress; int i, j; - for (i = 0; i < sizeof(stepit_tests)/sizeof(stepit_tests[0]); i++) + for (i = 0; i < ARRAY_SIZE(stepit_tests); i++) { struct stepit_test *test = &stepit_tests[i]; LRESULT ret; diff --git a/modules/rostests/winetests/comctl32/propsheet.c b/modules/rostests/winetests/comctl32/propsheet.c index 25d7cdec894..7d9da3f72b0 100644 --- a/modules/rostests/winetests/comctl32/propsheet.c +++ b/modules/rostests/winetests/comctl32/propsheet.c @@ -280,7 +280,6 @@ static void test_disableowner(void) psh.pfnCallback = disableowner_callback; p = pPropertySheetA(&psh); - todo_wine ok(p == 0, "Expected 0, got %ld\n", p); ok(IsWindowEnabled(parenthwnd) != 0, "parent window should be enabled\n"); DestroyWindow(parenthwnd); @@ -1147,6 +1146,46 @@ static void test_CreatePropertySheetPage(void) } } +static void test_bad_control_class(void) +{ + PROPSHEETPAGEA psp; + PROPSHEETHEADERA psh; + HPROPSHEETPAGE hpsp; + INT_PTR ret; + + memset(&psp, 0, sizeof(psp)); + psp.dwSize = sizeof(psp); + psp.hInstance = GetModuleHandleA(NULL); + U(psp).pszTemplate = (LPCSTR)MAKEINTRESOURCE(IDD_PROP_PAGE_BAD_CONTROL); + psp.pfnDlgProc = page_dlg_proc; + + hpsp = pCreatePropertySheetPageA(&psp); + ok(hpsp != 0, "CreatePropertySheetPage failed\n"); + + memset(&psh, 0, sizeof(psh)); + psh.dwSize = PROPSHEETHEADERA_V1_SIZE; + psh.nPages = 1; + psh.hwndParent = GetDesktopWindow(); + U3(psh).phpage = &hpsp; + +#ifndef __REACTOS__ /* FIXME: Inspect why this causes a hang */ + ret = pPropertySheetA(&psh); + ok(ret == 0, "got %ld\n", ret); +#endif + + /* Need to recreate hpsp otherwise the test fails under Windows */ + hpsp = pCreatePropertySheetPageA(&psp); + ok(hpsp != 0, "CreatePropertySheetPage failed\n"); + U3(psh).phpage = &hpsp; + + psh.dwFlags = PSH_MODELESS; + ret = pPropertySheetA(&psh); + ok(ret != 0, "got %ld\n", ret); + + ok(IsWindow((HWND)ret), "bad window handle %#lx\n", ret); + DestroyWindow((HWND)ret); +} + static void init_functions(void) { HMODULE hComCtl32 = LoadLibraryA("comctl32.dll"); @@ -1172,6 +1211,7 @@ START_TEST(propsheet) init_functions(); + test_bad_control_class(); test_title(); test_nopage(); test_disableowner(); diff --git a/modules/rostests/winetests/comctl32/rebar.c b/modules/rostests/winetests/comctl32/rebar.c index b3e78009f57..501638c758f 100644 --- a/modules/rostests/winetests/comctl32/rebar.c +++ b/modules/rostests/winetests/comctl32/rebar.c @@ -828,7 +828,7 @@ static DWORD resize_numtests = 0; RECT r; \ int value; \ const rbresize_test_result_t *res = &resize_results[resize_numtests++]; \ - assert(resize_numtests <= sizeof(resize_results)/sizeof(resize_results[0])); \ + assert(resize_numtests <= ARRAY_SIZE(resize_results)); \ GetWindowRect(hRebar, &r); \ MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); \ if ((dwStyles[i] & (CCS_NOPARENTALIGN|CCS_NODIVIDER)) == CCS_NOPARENTALIGN) {\ @@ -855,7 +855,7 @@ static void test_resize(void) CCS_TOP | WS_BORDER, CCS_NOPARENTALIGN | CCS_NODIVIDER | WS_BORDER, CCS_NORESIZE | WS_BORDER, CCS_NOMOVEY | WS_BORDER}; - const int styles_count = sizeof(dwStyles) / sizeof(dwStyles[0]); + const int styles_count = ARRAY_SIZE(dwStyles); int i; for (i = 0; i < styles_count; i++) diff --git a/modules/rostests/winetests/comctl32/resources.h b/modules/rostests/winetests/comctl32/resources.h index 3a89cd7baf1..53522c0a09d 100644 --- a/modules/rostests/winetests/comctl32/resources.h +++ b/modules/rostests/winetests/comctl32/resources.h @@ -39,6 +39,7 @@ #define IDD_PROP_PAGE_WITH_CUSTOM_DEFAULT_BUTTON 34 #define IDD_PROP_PAGE_MESSAGE_TEST 35 #define IDD_PROP_PAGE_ERROR 36 +#define IDD_PROP_PAGE_BAD_CONTROL 37 #define IDC_PS_EDIT1 1000 #define IDC_PS_EDIT2 1001 diff --git a/modules/rostests/winetests/comctl32/rsrc.rc b/modules/rostests/winetests/comctl32/rsrc.rc index 327aa225e17..6eb4fe8a0a8 100644 --- a/modules/rostests/winetests/comctl32/rsrc.rc +++ b/modules/rostests/winetests/comctl32/rsrc.rc @@ -78,6 +78,13 @@ FONT 8, "MS Shell Dlg" { } +IDD_PROP_PAGE_BAD_CONTROL DIALOG 0, 0, 100, 100 +STYLE WS_POPUP | WS_CAPTION | WS_CLIPSIBLINGS | WS_VISIBLE +FONT 8, "MS Shell Dlg" +{ + CONTROL "", -1, "invalid class", 0, 0, 0, 0, 0 +} + STRINGTABLE { IDS_TBADD1 "abc" diff --git a/modules/rostests/winetests/comctl32/status.c b/modules/rostests/winetests/comctl32/status.c index 2308974761d..c7d99892180 100644 --- a/modules/rostests/winetests/comctl32/status.c +++ b/modules/rostests/winetests/comctl32/status.c @@ -127,7 +127,7 @@ static int CALLBACK check_height_font_enumproc(ENUMLOGFONTEXA *enumlf, NEWTEXTME if (type != TRUETYPE_FONTTYPE) facename = enumlf->elfLogFont.lfFaceName; - for (i = 0; i < sizeof(sizes)/sizeof(sizes[0]); i++) + for (i = 0; i < ARRAY_SIZE(sizes); i++) { HFONT hFont; TEXTMETRICA tm; @@ -586,6 +586,131 @@ static void test_notify(void) ok(g_got_contextmenu, "WM_RBUTTONUP did not activate the context menu!\n"); } +static void test_sizegrip(void) +{ + HWND hwndStatus; + LONG style; + RECT rc, rcClient; + POINT pt; + int width, r; + + hwndStatus = CreateWindowA(SUBCLASS_NAME, "", WS_CHILD|WS_VISIBLE|SBARS_SIZEGRIP, + 0, 0, 100, 100, g_hMainWnd, NULL, NULL, NULL); + + style = GetWindowLongPtrA(g_hMainWnd, GWL_STYLE); + width = GetSystemMetrics(SM_CXVSCROLL); + + GetClientRect(hwndStatus, &rcClient); + + pt.x = rcClient.right; + pt.y = rcClient.top; + ClientToScreen(hwndStatus, &pt); + rc.left = pt.x - width; + rc.right = pt.x; + rc.top = pt.y; + + pt.y = rcClient.bottom; + ClientToScreen(hwndStatus, &pt); + rc.bottom = pt.y; + + /* check bounds when not maximized */ + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left, rc.top)); + expect(HTBOTTOMRIGHT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left - 1, rc.top)); + expect(HTCLIENT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left, rc.top - 1)); + expect(HTBOTTOMRIGHT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right, rc.bottom)); + expect(HTBOTTOMRIGHT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right + 1, rc.bottom)); + expect(HTBOTTOMRIGHT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right, rc.bottom + 1)); + expect(HTBOTTOMRIGHT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right - 1, rc.bottom - 1)); + expect(HTBOTTOMRIGHT, r); + + /* not maximized and right-to-left */ + SetWindowLongA(hwndStatus, GWL_EXSTYLE, WS_EX_LAYOUTRTL); + + pt.x = rcClient.right; + ClientToScreen(hwndStatus, &pt); + rc.left = pt.x + width; + rc.right = pt.x; + + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left, rc.top)); + expect(HTBOTTOMLEFT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left + 1, rc.top)); + expect(HTCLIENT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left, rc.top - 1)); + expect(HTBOTTOMLEFT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right, rc.bottom)); + expect(HTBOTTOMLEFT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right - 1, rc.bottom)); + expect(HTBOTTOMLEFT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right, rc.bottom + 1)); + expect(HTBOTTOMLEFT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right + 1, rc.bottom - 1)); + expect(HTBOTTOMLEFT, r); + + /* maximize with left-to-right */ + SetWindowLongA(g_hMainWnd, GWL_STYLE, style|WS_MAXIMIZE); + SetWindowLongA(hwndStatus, GWL_EXSTYLE, 0); + + GetClientRect(hwndStatus, &rcClient); + + pt.x = rcClient.right; + pt.y = rcClient.top; + ClientToScreen(hwndStatus, &pt); + rc.left = pt.x - width; + rc.right = pt.x; + rc.top = pt.y; + + pt.y = rcClient.bottom; + ClientToScreen(hwndStatus, &pt); + rc.bottom = pt.y; + + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left, rc.top)); + expect(HTCLIENT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left - 1, rc.top)); + expect(HTCLIENT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left, rc.top - 1)); + expect(HTNOWHERE, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right, rc.bottom)); + expect(HTNOWHERE, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right + 1, rc.bottom)); + expect(HTNOWHERE, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right, rc.bottom + 1)); + expect(HTNOWHERE, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right - 1, rc.bottom - 1)); + expect(HTCLIENT, r); + + /* maximized with right-to-left */ + SetWindowLongA(hwndStatus, GWL_EXSTYLE, WS_EX_LAYOUTRTL); + + pt.x = rcClient.right; + ClientToScreen(hwndStatus, &pt); + rc.left = pt.x + width; + rc.right = pt.x; + + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left, rc.top)); + expect(HTCLIENT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left + 1, rc.top)); + expect(HTCLIENT, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.left, rc.top - 1)); + expect(HTNOWHERE, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right, rc.bottom)); + expect(HTNOWHERE, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right - 1, rc.bottom)); + expect(HTNOWHERE, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right, rc.bottom + 1)); + expect(HTNOWHERE, r); + r = SendMessageA(hwndStatus, WM_NCHITTEST, 0, MAKELPARAM(rc.right + 1, rc.bottom - 1)); + expect(HTCLIENT, r); + + SetWindowLongA(g_hMainWnd, GWL_STYLE, style); + DestroyWindow(hwndStatus); +} + static void init_functions(void) { HMODULE hComCtl32 = LoadLibraryA("comctl32.dll"); @@ -620,4 +745,5 @@ START_TEST(status) test_status_ownerdraw(); test_gettext(); test_notify(); + test_sizegrip(); } diff --git a/modules/rostests/winetests/comctl32/subclass.c b/modules/rostests/winetests/comctl32/subclass.c index 75a134352ec..41ba06560ac 100644 --- a/modules/rostests/winetests/comctl32/subclass.c +++ b/modules/rostests/winetests/comctl32/subclass.c @@ -218,46 +218,61 @@ static LRESULT WINAPI wnd_proc_sub(HWND hwnd, UINT message, WPARAM wParam, LPARA static void test_subclass(void) { + BOOL ret; HWND hwnd = CreateWindowExA(0, "TestSubclass", "Test subclass", WS_OVERLAPPEDWINDOW, 100, 100, 200, 200, 0, 0, 0, NULL); ok(hwnd != NULL, "failed to create test subclass wnd\n"); - pSetWindowSubclass(hwnd, wnd_proc_sub, 2, 0); + ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 2, 0); + ok(ret == TRUE, "Expected TRUE\n"); SendMessageA(hwnd, WM_USER, 1, 0); SendMessageA(hwnd, WM_USER, 2, 0); ok_sequence(Sub_BasicTest, "Basic"); - pSetWindowSubclass(hwnd, wnd_proc_sub, 2, DELETE_SELF); + ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 2, DELETE_SELF); + ok(ret == TRUE, "Expected TRUE\n"); SendMessageA(hwnd, WM_USER, 1, 1); ok_sequence(Sub_DeletedTest, "Deleted"); SendMessageA(hwnd, WM_USER, 1, 0); ok_sequence(Sub_AfterDeletedTest, "After Deleted"); - pSetWindowSubclass(hwnd, wnd_proc_sub, 2, 0); + ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 2, 0); + ok(ret == TRUE, "Expected TRUE\n"); orig_proc_3 = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)wnd_proc_3); SendMessageA(hwnd, WM_USER, 1, 0); SendMessageA(hwnd, WM_USER, 2, 0); ok_sequence(Sub_OldAfterNewTest, "Old after New"); - pSetWindowSubclass(hwnd, wnd_proc_sub, 4, 0); + ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 4, 0); + ok(ret == TRUE, "Expected TRUE\n"); SendMessageA(hwnd, WM_USER, 1, 0); ok_sequence(Sub_MixTest, "Mix"); /* Now the fun starts */ - pSetWindowSubclass(hwnd, wnd_proc_sub, 4, SEND_NEST); + ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 4, SEND_NEST); + ok(ret == TRUE, "Expected TRUE\n"); SendMessageA(hwnd, WM_USER, 1, 1); ok_sequence(Sub_MixAndNestTest, "Mix and nest"); - pSetWindowSubclass(hwnd, wnd_proc_sub, 4, SEND_NEST | DELETE_SELF); + ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 4, SEND_NEST | DELETE_SELF); + ok(ret == TRUE, "Expected TRUE\n"); SendMessageA(hwnd, WM_USER, 1, 1); ok_sequence(Sub_MixNestDelTest, "Mix, nest, del"); - pSetWindowSubclass(hwnd, wnd_proc_sub, 4, 0); - pSetWindowSubclass(hwnd, wnd_proc_sub, 5, DELETE_PREV); + ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 4, 0); + ok(ret == TRUE, "Expected TRUE\n"); + ret = pSetWindowSubclass(hwnd, wnd_proc_sub, 5, DELETE_PREV); + ok(ret == TRUE, "Expected TRUE\n"); SendMessageA(hwnd, WM_USER, 1, 1); ok_sequence(Sub_MixDelPrevTest, "Mix and del prev"); + ret = pSetWindowSubclass(NULL, wnd_proc_sub, 1, 0); + ok(ret == FALSE, "Expected FALSE\n"); + + ret = pSetWindowSubclass(hwnd, NULL, 1, 0); + ok(ret == FALSE, "Expected FALSE\n"); + DestroyWindow(hwnd); } diff --git a/modules/rostests/winetests/comctl32/taskdialog.c b/modules/rostests/winetests/comctl32/taskdialog.c index 3fcc634e073..35dde1cce5b 100644 --- a/modules/rostests/winetests/comctl32/taskdialog.c +++ b/modules/rostests/winetests/comctl32/taskdialog.c @@ -29,15 +29,21 @@ #include "v6util.h" #include "msg.h" +#ifdef __REACTOS__ +#define WM_KEYF1 0x004d +#endif + #define WM_TD_CALLBACK (WM_APP) /* Custom dummy message to wrap callback notifications */ #define NUM_MSG_SEQUENCES 1 #define TASKDIALOG_SEQ_INDEX 0 #define TEST_NUM_BUTTONS 10 /* Number of custom buttons to test with */ +#define TEST_NUM_RADIO_BUTTONS 3 #define ID_START 20 /* Lower IDs might be used by the system */ #define ID_START_BUTTON (ID_START + 0) +#define ID_START_RADIO_BUTTON (ID_START + 20) static HRESULT (WINAPI *pTaskDialogIndirect)(const TASKDIALOGCONFIG *, int *, int *, BOOL *); static HRESULT (WINAPI *pTaskDialog)(HWND, HINSTANCE, const WCHAR *, const WCHAR *, const WCHAR *, @@ -121,6 +127,261 @@ static const struct message_info msg_return_press_custom10[] = { 0 } }; +static const struct message_info msg_send_click_ok[] = +{ + { TDM_CLICK_BUTTON, IDOK, 0 }, + { 0 } +}; + +static const struct message_info msg_send_f1[] = +{ + { WM_KEYF1, 0, 0, 0}, + { 0 } +}; + +static const struct message_info msg_got_tdn_help[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_f1 }, + { TDN_HELP, 0, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +/* Three radio buttons */ +static const struct message_info msg_return_default_radio_button_1[] = +{ + { TDN_CREATED, 0, 0, S_OK, NULL }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_default_radio_button_2[] = +{ + { TDN_CREATED, 0, 0, S_OK, NULL }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON + 1, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_default_radio_button_3[] = +{ + { TDN_CREATED, 0, 0, S_OK, NULL }, + { TDN_RADIO_BUTTON_CLICKED, -2, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_select_first_radio_button[] = +{ + { TDM_CLICK_RADIO_BUTTON, ID_START_RADIO_BUTTON, 0 }, + { 0 } +}; + +static const struct message_info msg_return_first_radio_button[] = +{ + { TDN_CREATED, 0, 0, S_OK, NULL }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON + 1, 0, S_OK, msg_select_first_radio_button }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_select_first_disabled_radio_button_and_press_ok[] = +{ + { TDM_ENABLE_RADIO_BUTTON, ID_START_RADIO_BUTTON, 0 }, + { TDM_CLICK_RADIO_BUTTON, ID_START_RADIO_BUTTON, 0 }, + { TDM_CLICK_BUTTON, IDOK, 0 }, + { 0 } +}; + +static const struct message_info msg_return_default_radio_button_clicking_disabled[] = +{ + { TDN_CREATED, 0, 0, S_OK, NULL }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON + 1, 0, S_OK, msg_select_first_disabled_radio_button_and_press_ok }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, NULL }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_no_default_radio_button_flag[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_click_ok }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, NULL }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_no_default_radio_button_id_and_flag[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_select_negative_id_radio_button[] = +{ + { TDM_CLICK_RADIO_BUTTON, -2, 0 }, + { 0 } +}; + +static const struct message_info msg_return_press_negative_id_radio_button[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_select_negative_id_radio_button }, + { TDN_RADIO_BUTTON_CLICKED, -2, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_send_all_common_button_click[] = +{ + { TDM_CLICK_BUTTON, IDOK, 0 }, + { TDM_CLICK_BUTTON, IDYES, 0 }, + { TDM_CLICK_BUTTON, IDNO, 0 }, + { TDM_CLICK_BUTTON, IDCANCEL, 0 }, + { TDM_CLICK_BUTTON, IDRETRY, 0 }, + { TDM_CLICK_BUTTON, IDCLOSE, 0 }, + { TDM_CLICK_BUTTON, ID_START_BUTTON + 99, 0 }, + { 0 } +}; + +static const struct message_info msg_press_nonexistent_buttons[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_all_common_button_click }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_FALSE, NULL }, + { TDN_BUTTON_CLICKED, IDYES, 0, S_FALSE, NULL }, + { TDN_BUTTON_CLICKED, IDNO, 0, S_FALSE, NULL }, + { TDN_BUTTON_CLICKED, IDCANCEL, 0, S_FALSE, NULL }, + { TDN_BUTTON_CLICKED, IDRETRY, 0, S_FALSE, NULL }, + { TDN_BUTTON_CLICKED, IDCLOSE, 0, S_FALSE, NULL }, + { TDN_BUTTON_CLICKED, ID_START_BUTTON + 99, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_send_all_common_button_click_with_command[] = +{ + { WM_COMMAND, MAKEWORD(IDOK, BN_CLICKED), 0 }, + { WM_COMMAND, MAKEWORD(IDYES, BN_CLICKED), 0 }, + { WM_COMMAND, MAKEWORD(IDNO, BN_CLICKED), 0 }, + { WM_COMMAND, MAKEWORD(IDCANCEL, BN_CLICKED), 0 }, + { WM_COMMAND, MAKEWORD(IDRETRY, BN_CLICKED), 0 }, + { WM_COMMAND, MAKEWORD(IDCLOSE, BN_CLICKED), 0 }, + { WM_COMMAND, MAKEWORD(ID_START_BUTTON + 99, BN_CLICKED), 0 }, + { WM_COMMAND, MAKEWORD(IDOK, BN_CLICKED), 0 }, + { 0 } +}; + +static const struct message_info msg_press_nonexistent_buttons_with_command[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_all_common_button_click_with_command }, + { TDN_BUTTON_CLICKED, ID_START_BUTTON, 0, S_FALSE, NULL }, + { TDN_BUTTON_CLICKED, ID_START_BUTTON, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_send_nonexistent_radio_button_click[] = +{ + { TDM_CLICK_RADIO_BUTTON, ID_START_RADIO_BUTTON + 99, 0 }, + { TDM_CLICK_BUTTON, IDOK, 0 }, + { 0 } +}; + +static const struct message_info msg_press_nonexistent_radio_button[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_nonexistent_radio_button_click }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_default_verification_unchecked[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_default_verification_checked[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_uncheck_verification[] = +{ + { TDM_CLICK_VERIFICATION, FALSE, 0 }, + { 0 } +}; + +static const struct message_info msg_return_verification_unchecked[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_uncheck_verification }, + { TDN_VERIFICATION_CLICKED, FALSE, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_check_verification[] = +{ + { TDM_CLICK_VERIFICATION, TRUE, 0 }, + { 0 } +}; + +static const struct message_info msg_return_verification_checked[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_check_verification }, + { TDN_VERIFICATION_CLICKED, TRUE, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static TASKDIALOGCONFIG navigated_info = {0}; + +static const struct message_info msg_send_navigate[] = +{ + { TDM_NAVIGATE_PAGE, 0, (LPARAM)&navigated_info, 0}, + { 0 } +}; + +static const struct message_info msg_return_navigated_page[] = +{ + { TDN_CREATED, 0, 0, S_OK, NULL }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, msg_send_navigate }, + { TDN_DIALOG_CONSTRUCTED, 0, 0, S_OK, NULL }, + { TDN_RADIO_BUTTON_CLICKED, ID_START_RADIO_BUTTON, 0, S_OK, NULL }, + { TDN_NAVIGATED, 0, 0, S_OK, msg_send_click_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_send_close[] = +{ + { WM_CLOSE, 0, 0, 0}, + { 0 } +}; + +static const struct message_info msg_handle_wm_close[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_close }, + { TDN_BUTTON_CLICKED, IDCANCEL, 0, S_FALSE, msg_send_close }, + { TDN_BUTTON_CLICKED, IDCANCEL, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_send_close_then_ok[] = +{ + { WM_CLOSE, 0, 0, 0}, + { TDM_CLICK_BUTTON, IDOK, 0 }, + { 0 } +}; + +static const struct message_info msg_handle_wm_close_without_cancel_button[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_close_then_ok }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + static void init_test_message(UINT message, WPARAM wParam, LPARAM lParam, struct message *msg) { msg->message = WM_TD_CALLBACK; @@ -131,16 +392,18 @@ static void init_test_message(UINT message, WPARAM wParam, LPARAM lParam, struct msg->stage = 0; } -#define run_test(info, expect_button, seq, context) \ - run_test_(info, expect_button, seq, context, \ - sizeof(seq)/sizeof(seq[0]) - 1, __FILE__, __LINE__) +#define run_test(info, expect_button, expect_radio_button, verification_checked, seq, context) \ + run_test_(info, expect_button, expect_radio_button, verification_checked, seq, context, \ + ARRAY_SIZE(seq) - 1, __FILE__, __LINE__) -static void run_test_(TASKDIALOGCONFIG *info, int expect_button, const struct message_info *test_messages, - const char *context, int test_messages_len, const char *file, int line) +static void run_test_(TASKDIALOGCONFIG *info, int expect_button, int expect_radio_button, BOOL verification_checked, + const struct message_info *test_messages, const char *context, int test_messages_len, + const char *file, int line) { struct message *msg, *msg_start; int ret_button = 0; int ret_radio = 0; + BOOL ret_verification = FALSE; HRESULT hr; int i; @@ -157,12 +420,14 @@ static void run_test_(TASKDIALOGCONFIG *info, int expect_button, const struct me current_message_info = test_messages; flush_sequences(sequences, NUM_MSG_SEQUENCES); - hr = pTaskDialogIndirect(info, &ret_button, &ret_radio, NULL); + hr = pTaskDialogIndirect(info, &ret_button, &ret_radio, &ret_verification); ok_(file, line)(hr == S_OK, "TaskDialogIndirect() failed, got %#x.\n", hr); ok_sequence_(sequences, TASKDIALOG_SEQ_INDEX, msg_start, context, FALSE, file, line); ok_(file, line)(ret_button == expect_button, "Wrong button. Expected %d, got %d\n", expect_button, ret_button); + ok_(file, line)(ret_radio == expect_radio_button, + "Wrong radio button. Expected %d, got %d\n", expect_radio_button, ret_radio); heap_free(msg_start); } @@ -220,16 +485,17 @@ static void test_callback(void) info.pfCallback = taskdialog_callback_proc; info.lpCallbackData = test_ref_data; - run_test(&info, IDOK, msg_return_press_ok, "Press VK_RETURN."); + run_test(&info, IDOK, 0, FALSE, msg_return_press_ok, "Press VK_RETURN."); } static void test_buttons(void) { TASKDIALOGCONFIG info = {0}; - - TASKDIALOG_BUTTON custom_buttons[TEST_NUM_BUTTONS]; + static const DWORD command_link_flags[] = {0, TDF_USE_COMMAND_LINKS, TDF_USE_COMMAND_LINKS_NO_ICON}; + TASKDIALOG_BUTTON custom_buttons[TEST_NUM_BUTTONS], radio_buttons[TEST_NUM_RADIO_BUTTONS]; const WCHAR button_format[] = {'%','0','2','d',0}; - WCHAR button_titles[TEST_NUM_BUTTONS * 3]; /* Each button has two digits as title, plus null-terminator */ + /* Each button has two digits as title, plus null-terminator */ + WCHAR button_titles[TEST_NUM_BUTTONS * 3], radio_button_titles[TEST_NUM_BUTTONS * 3]; int i; info.cbSize = sizeof(TASKDIALOGCONFIG); @@ -247,48 +513,359 @@ static void test_buttons(void) } custom_buttons[TEST_NUM_BUTTONS - 1].nButtonID = -1; + /* Init radio buttons */ + for (i = 0; i < TEST_NUM_RADIO_BUTTONS; i++) + { + WCHAR *text = &radio_button_titles[i * 3]; + wsprintfW(text, button_format, i); + + radio_buttons[i].pszButtonText = text; + radio_buttons[i].nButtonID = ID_START_RADIO_BUTTON + i; + } + radio_buttons[TEST_NUM_RADIO_BUTTONS - 1].nButtonID = -2; + /* Test nDefaultButton */ /* Test common buttons with invalid default ID */ info.nDefaultButton = 0; /* Should default to first created button */ info.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; - run_test(&info, IDOK, msg_return_press_ok, "default button: unset default"); + run_test(&info, IDOK, 0, FALSE, msg_return_press_ok, "default button: unset default"); info.dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; - run_test(&info, IDYES, msg_return_press_yes, "default button: unset default"); + run_test(&info, IDYES, 0, FALSE, msg_return_press_yes, "default button: unset default"); info.dwCommonButtons = TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; - run_test(&info, IDNO, msg_return_press_no, "default button: unset default"); + run_test(&info, IDNO, 0, FALSE, msg_return_press_no, "default button: unset default"); info.dwCommonButtons = TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; - run_test(&info, IDRETRY, msg_return_press_retry, "default button: unset default"); + run_test(&info, IDRETRY, 0, FALSE, msg_return_press_retry, "default button: unset default"); info.dwCommonButtons = TDCBF_CANCEL_BUTTON | TDCBF_CLOSE_BUTTON; - run_test(&info, IDCANCEL, msg_return_press_cancel, "default button: unset default"); + run_test(&info, IDCANCEL, 0, FALSE, msg_return_press_cancel, "default button: unset default"); - /* Test with all common and custom buttons and invalid default ID */ - info.nDefaultButton = 0xff; /* Random ID, should also default to first created button */ + /* Custom buttons could be command links */ + for (i = 0; i < ARRAY_SIZE(command_link_flags); i++) + { + info.dwFlags = command_link_flags[i]; + + /* Test with all common and custom buttons and invalid default ID */ + info.nDefaultButton = 0xff; /* Random ID, should also default to first created button */ + info.cButtons = TEST_NUM_BUTTONS; + info.pButtons = custom_buttons; + run_test(&info, ID_START_BUTTON, 0, FALSE, msg_return_press_custom1, + "default button: invalid default, with common buttons - 1"); + + info.nDefaultButton = -1; /* Should work despite button ID -1 */ + run_test(&info, -1, 0, FALSE, msg_return_press_custom10, "default button: invalid default, with common buttons - 2"); + + info.nDefaultButton = -2; /* Should also default to first created button */ + run_test(&info, ID_START_BUTTON, 0, FALSE, msg_return_press_custom1, + "default button: invalid default, with common buttons - 3"); + + /* Test with only custom buttons and invalid default ID */ + info.dwCommonButtons = 0; + run_test(&info, ID_START_BUTTON, 0, FALSE, msg_return_press_custom1, + "default button: invalid default, no common buttons"); + + /* Test with common and custom buttons and valid default ID */ + info.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_YES_BUTTON | TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON + | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; + info.nDefaultButton = IDRETRY; + run_test(&info, IDRETRY, 0, FALSE, msg_return_press_retry, "default button: valid default - 1"); + + /* Test with common and custom buttons and valid default ID */ + info.nDefaultButton = ID_START_BUTTON + 3; + run_test(&info, ID_START_BUTTON + 3, 0, FALSE, msg_return_press_custom4, "default button: valid default - 2"); + } + + /* Test radio buttons */ + info.nDefaultButton = 0; + info.cButtons = 0; + info.pButtons = 0; + info.dwCommonButtons = TDCBF_OK_BUTTON; + info.cRadioButtons = TEST_NUM_RADIO_BUTTONS; + info.pRadioButtons = radio_buttons; + + /* Test default first radio button */ + run_test(&info, IDOK, ID_START_RADIO_BUTTON, FALSE, msg_return_default_radio_button_1, + "default radio button: default first radio button"); + + /* Test default radio button */ + info.nDefaultRadioButton = ID_START_RADIO_BUTTON + 1; + run_test(&info, IDOK, info.nDefaultRadioButton, FALSE, msg_return_default_radio_button_2, + "default radio button: default radio button"); + + /* Test default radio button with -2 */ + info.nDefaultRadioButton = -2; + run_test(&info, IDOK, info.nDefaultRadioButton, FALSE, msg_return_default_radio_button_3, + "default radio button: default radio button with id -2"); + + /* Test default radio button after clicking the first, messages still work even radio button is disabled */ + info.nDefaultRadioButton = ID_START_RADIO_BUTTON + 1; + run_test(&info, IDOK, ID_START_RADIO_BUTTON, FALSE, msg_return_first_radio_button, + "default radio button: radio button after clicking"); + + /* Test radio button after disabling and clicking the first */ + info.nDefaultRadioButton = ID_START_RADIO_BUTTON + 1; + run_test(&info, IDOK, ID_START_RADIO_BUTTON, FALSE, msg_return_default_radio_button_clicking_disabled, + "default radio button: disable radio button before clicking"); + + /* Test no default radio button, TDF_NO_DEFAULT_RADIO_BUTTON is set, TDN_RADIO_BUTTON_CLICKED will still be received, just radio button not selected */ + info.nDefaultRadioButton = ID_START_RADIO_BUTTON; + info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON; + run_test(&info, IDOK, info.nDefaultRadioButton, FALSE, msg_return_no_default_radio_button_flag, + "default radio button: no default radio flag"); + + /* Test no default radio button, TDF_NO_DEFAULT_RADIO_BUTTON is set and nDefaultRadioButton is 0. + * TDN_RADIO_BUTTON_CLICKED will not be sent, and just radio button not selected */ + info.nDefaultRadioButton = 0; + info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON; + run_test(&info, IDOK, 0, FALSE, msg_return_no_default_radio_button_id_and_flag, + "default radio button: no default radio id and flag"); + + /* Test no default radio button, TDF_NO_DEFAULT_RADIO_BUTTON is set and nDefaultRadioButton is invalid. + * TDN_RADIO_BUTTON_CLICKED will not be sent, and just radio button not selected */ + info.nDefaultRadioButton = 0xff; + info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON; + run_test(&info, IDOK, 0, FALSE, msg_return_no_default_radio_button_id_and_flag, + "default radio button: no default flag, invalid id"); + + info.nDefaultRadioButton = 0; + info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON; + run_test(&info, IDOK, -2, FALSE, msg_return_press_negative_id_radio_button, + "radio button: manually click radio button with negative id"); + + /* Test sending clicks to non-existent buttons. Notification of non-existent buttons will be sent */ info.cButtons = TEST_NUM_BUTTONS; info.pButtons = custom_buttons; - run_test(&info, ID_START_BUTTON, msg_return_press_custom1, "default button: invalid default, with common buttons - 1"); - - info.nDefaultButton = -1; /* Should work despite button ID -1 */ - run_test(&info, -1, msg_return_press_custom10, "default button: invalid default, with common buttons - 2"); - - info.nDefaultButton = -2; /* Should also default to first created button */ - run_test(&info, ID_START_BUTTON, msg_return_press_custom1, "default button: invalid default, with common buttons - 3"); - - /* Test with only custom buttons and invalid default ID */ + info.cRadioButtons = TEST_NUM_RADIO_BUTTONS; + info.pRadioButtons = radio_buttons; info.dwCommonButtons = 0; - run_test(&info, ID_START_BUTTON, msg_return_press_custom1, "default button: invalid default, no common buttons"); + info.dwFlags = TDF_NO_DEFAULT_RADIO_BUTTON; + run_test(&info, ID_START_BUTTON + 99, 0, FALSE, msg_press_nonexistent_buttons, "sends click to non-existent buttons"); - /* Test with common and custom buttons and valid default ID */ - info.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_YES_BUTTON | TDCBF_NO_BUTTON - | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; - info.nDefaultButton = IDRETRY; - run_test(&info, IDRETRY, msg_return_press_retry, "default button: valid default - 1"); + /* Non-existent button clicks sent by WM_COMMAND won't generate TDN_BUTTON_CLICKED except IDOK. + * And will get the first existent button identifier instead of IDOK */ + run_test(&info, ID_START_BUTTON, 0, FALSE, msg_press_nonexistent_buttons_with_command, + "sends click to non-existent buttons with WM_COMMAND"); - /* Test with common and custom buttons and valid default ID */ - info.nDefaultButton = ID_START_BUTTON + 3; - run_test(&info, ID_START_BUTTON + 3, msg_return_press_custom4, "default button: valid default - 2"); + /* Non-existent radio button won't get notifications */ + run_test(&info, IDOK, 0, FALSE, msg_press_nonexistent_radio_button, "sends click to non-existent radio buttons"); +} + +static void test_help(void) +{ + TASKDIALOGCONFIG info = {0}; + + info.cbSize = sizeof(TASKDIALOGCONFIG); + info.pfCallback = taskdialog_callback_proc; + info.lpCallbackData = test_ref_data; + info.dwCommonButtons = TDCBF_OK_BUTTON; + + run_test(&info, IDOK, 0, FALSE, msg_got_tdn_help, "send f1"); +} + +struct timer_notification_data +{ + DWORD last_elapsed_ms; + DWORD num_fired; +}; + +static HRESULT CALLBACK taskdialog_callback_proc_timer(HWND hwnd, UINT notification, + WPARAM wParam, LPARAM lParam, LONG_PTR ref_data) +{ + struct timer_notification_data *data = (struct timer_notification_data *)ref_data; + + if (notification == TDN_TIMER) + { + DWORD elapsed_ms; + int delta; + + elapsed_ms = (DWORD)wParam; + + if (data->num_fired == 3) + ok(data->last_elapsed_ms > elapsed_ms, "Expected reference time update.\n"); + else + { + delta = elapsed_ms - data->last_elapsed_ms; + ok(delta > 0, "Expected positive time tick difference.\n"); + } + data->last_elapsed_ms = elapsed_ms; + + if (data->num_fired == 3) + PostMessageW(hwnd, TDM_CLICK_BUTTON, IDOK, 0); + + ++data->num_fired; + return data->num_fired == 3 ? S_FALSE : S_OK; + } + + return S_OK; +} + +static void test_timer(void) +{ + struct timer_notification_data data = { 0 }; + TASKDIALOGCONFIG info = { 0 }; + + info.cbSize = sizeof(TASKDIALOGCONFIG); + info.pfCallback = taskdialog_callback_proc_timer; + info.lpCallbackData = (LONG_PTR)&data; + info.dwFlags = TDF_CALLBACK_TIMER; + info.dwCommonButtons = TDCBF_OK_BUTTON; + + pTaskDialogIndirect(&info, NULL, NULL, NULL); +} + +static HRESULT CALLBACK taskdialog_callback_proc_progress_bar(HWND hwnd, UINT notification, WPARAM wParam, + LPARAM lParam, LONG_PTR ref_data) +{ + unsigned long ret; + LONG flags = (LONG)ref_data; + if (notification == TDN_CREATED) + { + /* TDM_SET_PROGRESS_BAR_STATE */ + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_STATE, PBST_NORMAL, 0); + ok(ret == PBST_NORMAL, "Expect state: %d got state: %lx\n", PBST_NORMAL, ret); + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_STATE, PBST_PAUSED, 0); + ok(ret == PBST_NORMAL, "Expect state: %d got state: %lx\n", PBST_NORMAL, ret); + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_STATE, PBST_ERROR, 0); + /* Progress bar has fixme on handling PBM_SETSTATE message */ + todo_wine ok(ret == PBST_PAUSED, "Expect state: %d got state: %lx\n", PBST_PAUSED, ret); + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_STATE, PBST_NORMAL, 0); + todo_wine ok(ret == PBST_ERROR, "Expect state: %d got state: %lx\n", PBST_ERROR, ret); + + /* TDM_SET_PROGRESS_BAR_RANGE */ + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_RANGE, 0, MAKELPARAM(0, 200)); + ok(ret == MAKELONG(0, 100), "Expect range:%x got:%lx\n", MAKELONG(0, 100), ret); + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_RANGE, 0, MAKELPARAM(0, 200)); + ok(ret == MAKELONG(0, 200), "Expect range:%x got:%lx\n", MAKELONG(0, 200), ret); + + /* TDM_SET_PROGRESS_BAR_POS */ + if (flags & TDF_SHOW_MARQUEE_PROGRESS_BAR) + { + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_POS, 1, 0); + ok(ret == 0, "Expect position:%x got:%lx\n", 0, ret); + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_POS, 2, 0); + ok(ret == 0, "Expect position:%x got:%lx\n", 0, ret); + } + else + { + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_POS, 1, 0); + ok(ret == 0, "Expect position:%x got:%lx\n", 0, ret); + ret = SendMessageW(hwnd, TDM_SET_PROGRESS_BAR_POS, 2, 0); + ok(ret == 1, "Expect position:%x got:%lx\n", 1, ret); + } + + SendMessageW(hwnd, TDM_CLICK_BUTTON, IDOK, 0); + } + + return S_OK; +} + +static void test_progress_bar(void) +{ + TASKDIALOGCONFIG info = {0}; + + info.cbSize = sizeof(TASKDIALOGCONFIG); + info.dwFlags = TDF_SHOW_PROGRESS_BAR; + info.pfCallback = taskdialog_callback_proc_progress_bar; + info.lpCallbackData = (LONG_PTR)info.dwFlags; + info.dwCommonButtons = TDCBF_OK_BUTTON; + pTaskDialogIndirect(&info, NULL, NULL, NULL); + + info.dwFlags = TDF_SHOW_MARQUEE_PROGRESS_BAR; + info.lpCallbackData = (LONG_PTR)info.dwFlags; + pTaskDialogIndirect(&info, NULL, NULL, NULL); + + info.dwFlags = TDF_SHOW_PROGRESS_BAR | TDF_SHOW_MARQUEE_PROGRESS_BAR; + info.lpCallbackData = (LONG_PTR)info.dwFlags; + pTaskDialogIndirect(&info, NULL, NULL, NULL); +} + +static void test_verification_box(void) +{ + TASKDIALOGCONFIG info = {0}; + WCHAR textW[] = {'t', 'e', 'x', 't', 0}; + + info.cbSize = sizeof(TASKDIALOGCONFIG); + info.pfCallback = taskdialog_callback_proc; + info.lpCallbackData = test_ref_data; + info.dwCommonButtons = TDCBF_OK_BUTTON; + + /* TDF_VERIFICATION_FLAG_CHECKED works even if pszVerificationText is not set */ + run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_unchecked, "default verification box: unchecked"); + + info.dwFlags = TDF_VERIFICATION_FLAG_CHECKED; + run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_checked, "default verification box: checked"); + + info.pszVerificationText = textW; + run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_unchecked, "default verification box: unchecked"); + + info.dwFlags = TDF_VERIFICATION_FLAG_CHECKED; + run_test(&info, IDOK, 0, FALSE, msg_return_default_verification_checked, "default verification box: checked"); + + run_test(&info, IDOK, 0, FALSE, msg_return_verification_unchecked, + "default verification box: default checked and then unchecked"); + + info.dwFlags = 0; + run_test(&info, IDOK, 0, FALSE, msg_return_verification_checked, + "default verification box: default unchecked and then checked"); +} + +static void test_navigate_page(void) +{ + TASKDIALOGCONFIG info = {0}; + static const WCHAR textW[] = {'t', 'e', 'x', 't', 0}; + static const WCHAR button_format[] = {'%', '0', '2', 'd', 0}; + TASKDIALOG_BUTTON radio_buttons[TEST_NUM_RADIO_BUTTONS]; + WCHAR radio_button_titles[TEST_NUM_BUTTONS * 3]; + int i; + + /* Init radio buttons */ + for (i = 0; i < TEST_NUM_RADIO_BUTTONS; i++) + { + WCHAR *text = &radio_button_titles[i * 3]; + wsprintfW(text, button_format, i); + + radio_buttons[i].pszButtonText = text; + radio_buttons[i].nButtonID = ID_START_RADIO_BUTTON + i; + } + + info.cbSize = sizeof(TASKDIALOGCONFIG); + info.pfCallback = taskdialog_callback_proc; + info.lpCallbackData = test_ref_data; + info.dwCommonButtons = TDCBF_OK_BUTTON; + info.cRadioButtons = TEST_NUM_RADIO_BUTTONS; + info.pRadioButtons = radio_buttons; + + navigated_info = info; + navigated_info.pszVerificationText = textW; + navigated_info.dwFlags = TDF_VERIFICATION_FLAG_CHECKED; + + run_test(&info, IDOK, ID_START_RADIO_BUTTON, TRUE, msg_return_navigated_page, "navigate page: default"); + + /* TDM_NAVIGATE_PAGE doesn't check cbSize. + * And null taskconfig pointer crash applicatioin, thus doesn't check pointer either */ + navigated_info.cbSize = 0; + run_test(&info, IDOK, ID_START_RADIO_BUTTON, TRUE, msg_return_navigated_page, "navigate page: invalid taskconfig cbSize"); +} + +static void test_wm_close(void) +{ + TASKDIALOGCONFIG info = {0}; + + info.cbSize = sizeof(TASKDIALOGCONFIG); + info.pfCallback = taskdialog_callback_proc; + info.lpCallbackData = test_ref_data; + + /* WM_CLOSE can end the dialog only when a cancel button is present or dwFlags has TDF_ALLOW_DIALOG_CANCELLATION */ + info.dwCommonButtons = TDCBF_OK_BUTTON; + run_test(&info, IDOK, 0, FALSE, msg_handle_wm_close_without_cancel_button, "send WM_CLOSE without cancel button"); + + info.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION; + run_test(&info, IDCANCEL, 0, FALSE, msg_handle_wm_close, "send WM_CLOSE with TDF_ALLOW_DIALOG_CANCELLATION"); + + info.dwFlags = 0; + info.dwCommonButtons = TDCBF_CANCEL_BUTTON; + run_test(&info, IDCANCEL, 0, FALSE, msg_handle_wm_close, "send WM_CLOSE with a cancel button"); } START_TEST(taskdialog) @@ -327,6 +904,12 @@ START_TEST(taskdialog) test_invalid_parameters(); test_callback(); test_buttons(); + test_help(); + test_timer(); + test_progress_bar(); + test_verification_box(); + test_navigate_page(); + test_wm_close(); unload_v6_module(ctx_cookie, hCtx); } diff --git a/modules/rostests/winetests/comctl32/toolbar.c b/modules/rostests/winetests/comctl32/toolbar.c index ce5eab3cc81..41807258f4f 100644 --- a/modules/rostests/winetests/comctl32/toolbar.c +++ b/modules/rostests/winetests/comctl32/toolbar.c @@ -56,7 +56,6 @@ static BOOL g_ResetDispTextPtr; static const struct message ttgetdispinfo_parent_seq[] = { { WM_NOTIFY, sent|id, 0, 0, TBN_GETINFOTIPA }, - /* next line is todo, currently TTN_GETDISPINFOW is raised here */ { WM_NOTIFY, sent|id, 0, 0, TTN_GETDISPINFOA }, { 0 } }; @@ -388,7 +387,7 @@ static void basic_test(void) WS_CHILD | TBSTYLE_LIST, 100, 0, NULL, 0, - buttons, sizeof(buttons)/sizeof(buttons[0]), + buttons, ARRAY_SIZE(buttons), 0, 0, 20, 16, sizeof(TBBUTTON)); ok(hToolbar != NULL, "Toolbar creation\n"); SendMessageA(hToolbar, TB_ADDSTRINGA, 0, (LPARAM)"test\000"); @@ -1316,7 +1315,7 @@ static DWORD tbsize_alt_numtests = 0; compare(buttonCount, res->nButtons, "%d"); \ for (i=0; inButtons); i++) { \ ok(SendMessageA(hToolbar, TB_GETITEMRECT, i, (LPARAM)&rc) == 1, "TB_GETITEMRECT\n"); \ - if (broken(tbsize_alt_numtests < sizeof(tbsize_alt_results)/sizeof(tbsize_alt_results[0]) && \ + if (broken(tbsize_alt_numtests < ARRAY_SIZE(tbsize_alt_results) && \ EqualRect(&rc, &tbsize_alt_results[tbsize_alt_numtests].rcButton))) { \ win_skip("Alternate rect found\n"); \ tbsize_alt_numtests++; \ @@ -1940,13 +1939,13 @@ static void test_setrows(void) | CCS_NOMOVEY | CCS_TOP, 0, 0, NULL, 0, - buttons, sizeof(buttons)/sizeof(buttons[0]), + buttons, ARRAY_SIZE(buttons), 20, 20, 0, 0, sizeof(TBBUTTON)); ok(hToolbar != NULL, "Toolbar creation\n"); ok(SendMessageA(hToolbar, TB_AUTOSIZE, 0, 0) == 0, "TB_AUTOSIZE failed\n"); /* test setting rows to each of 1-10 with bLarger true and false */ - for (i=0; i<(sizeof(tbrows_results) / sizeof(tbrows_result_t)); i++) { + for (i=0; i"), "got string %s\n", buffA); DestroyWindow(hTree); @@ -1948,7 +1991,7 @@ static void test_TVS_SINGLEEXPAND(void) SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_SINGLEEXPAND); /* to avoid painting related notifications */ ShowWindow(hTree, SW_HIDE); - for (i = 0; i < sizeof(items)/sizeof(items[0]); i++) + for (i = 0; i < ARRAY_SIZE(items); i++) { ins.hParent = items[i].parent ? *items[i].parent : TVI_ROOT; ins.hInsertAfter = TVI_FIRST; @@ -1957,7 +2000,7 @@ static void test_TVS_SINGLEEXPAND(void) *items[i].handle = TreeView_InsertItemA(hTree, &ins); } - for (i = 0; i < sizeof(sequence_tests)/sizeof(sequence_tests[0]); i++) + for (i = 0; i < ARRAY_SIZE(sequence_tests); i++) { flush_sequences(sequences, NUM_MSG_SEQUENCES); ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)(*sequence_tests[i].select)); @@ -1966,7 +2009,7 @@ static void test_TVS_SINGLEEXPAND(void) ok_sequence(sequences, PARENT_SEQ_INDEX, sequence_tests[i].sequence, context, FALSE); } - for (i = 0; i < sizeof(items)/sizeof(items[0]); i++) + for (i = 0; i < ARRAY_SIZE(items); i++) { ret = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)(*items[i].handle), 0xFFFF); ok(ret == items[i].final_state, "singleexpand items[%d]: expected state 0x%x got 0x%x\n", @@ -2132,20 +2175,64 @@ struct _ITEM_DATA HTREEITEM parent; /* for root value of parent field is unidetified */ HTREEITEM nextsibling; HTREEITEM firstchild; + void *unk[2]; + DWORD unk2; + WORD pad; + WORD width; }; -static void _check_item(HTREEITEM item, HTREEITEM parent, HTREEITEM nextsibling, HTREEITEM firstchild, int line) +struct _ITEM_DATA_V6 { - struct _ITEM_DATA *data = (struct _ITEM_DATA*)item; + HTREEITEM parent; /* for root value of parent field is unidetified */ + HTREEITEM nextsibling; + HTREEITEM firstchild; + void *unk[3]; + DWORD unk2[2]; + WORD pad; + WORD width; +}; - ok_(__FILE__, line)(data->parent == parent, "parent %p, got %p\n", parent, data->parent); - ok_(__FILE__, line)(data->nextsibling == nextsibling, "sibling %p, got %p\n", nextsibling, data->nextsibling); - ok_(__FILE__, line)(data->firstchild == firstchild, "firstchild %p, got %p\n", firstchild, data->firstchild); +static void _check_item(HWND hwnd, HTREEITEM item, BOOL is_version_6, int line) +{ + struct _ITEM_DATA *data = (struct _ITEM_DATA *)item; + HTREEITEM parent, nextsibling, firstchild, root; + RECT rect; + BOOL ret; + + root = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_ROOT, (LPARAM)item); + parent = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)item); + nextsibling = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)item); + firstchild = (HTREEITEM)SendMessageA(hwnd, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)item); + + *(HTREEITEM*)&rect = item; + ret = SendMessageA(hwnd, TVM_GETITEMRECT, TRUE, (LPARAM)&rect); + + ok_(__FILE__, line)(item == root ? data->parent != NULL : data->parent == parent, + "Unexpected parent item %p, got %p, %p\n", parent, data->parent, hwnd); + ok_(__FILE__, line)(data->nextsibling == nextsibling, "Unexpected sibling %p, got %p\n", + nextsibling, data->nextsibling); + ok_(__FILE__, line)(data->firstchild == firstchild, "Unexpected first child %p, got %p\n", + firstchild, data->firstchild); + if (ret) + { + WORD width; + + if (is_version_6) + { + struct _ITEM_DATA_V6 *data_v6 = (struct _ITEM_DATA_V6 *)item; + width = data_v6->width; + } + else + width = data->width; + todo_wine + ok_(__FILE__, line)(width == (rect.right - rect.left) || broken(is_version_6 && width == 0) /* XP */, + "Width %d, rect width %d.\n", width, rect.right - rect.left); + } } -#define check_item(a, b, c, d) _check_item(a, b, c, d, __LINE__) +#define CHECK_ITEM(a, b) _check_item(a, b, is_version_6, __LINE__) -static void test_htreeitem_layout(void) +static void test_htreeitem_layout(BOOL is_version_6) { TVINSERTSTRUCTA ins; HTREEITEM item1, item2; @@ -2155,27 +2242,27 @@ static void test_htreeitem_layout(void) fill_tree(hTree); /* root has some special pointer in parent field */ - check_item(hRoot, ((struct _ITEM_DATA*)hRoot)->parent, 0, hChild); - check_item(hChild, hRoot, 0, 0); + CHECK_ITEM(hTree, hRoot); + CHECK_ITEM(hTree, hChild); ins.hParent = hChild; ins.hInsertAfter = TVI_FIRST; U(ins).item.mask = 0; item1 = TreeView_InsertItemA(hTree, &ins); - check_item(item1, hChild, 0, 0); + CHECK_ITEM(hTree, item1); ins.hParent = hRoot; ins.hInsertAfter = TVI_FIRST; U(ins).item.mask = 0; item2 = TreeView_InsertItemA(hTree, &ins); - check_item(item2, hRoot, hChild, 0); + CHECK_ITEM(hTree, item2); SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hChild); /* without children now */ - check_item(hRoot, ((struct _ITEM_DATA*)hRoot)->parent, 0, item2); + CHECK_ITEM(hTree, hRoot); DestroyWindow(hTree); } @@ -2722,15 +2809,7 @@ static void test_right_click(void) HTREEITEM selected; RECT rc; LRESULT result; - POINT pt; - -#ifdef __REACTOS__ - if (!winetest_interactive) - { - skip("test_right_click() (set WINETEST_INTERACTIVE=1), until CORE-14975 is fixed upstream and WINESYNC\n"); - return; - } -#endif + POINT pt, orig_pos; hTree = create_treeview_control(0); fill_tree(hTree); @@ -2749,6 +2828,8 @@ static void test_right_click(void) pt.x = (rc.left + rc.right) / 2; pt.y = (rc.top + rc.bottom) / 2; ClientToScreen(hMainWnd, &pt); + GetCursorPos(&orig_pos); + SetCursorPos(pt.x, pt.y); flush_events(); flush_sequences(sequences, NUM_MSG_SEQUENCES); @@ -2764,6 +2845,7 @@ static void test_right_click(void) selected = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CARET, 0); ok(selected == hChild, "child item should still be selected\n"); + SetCursorPos(orig_pos.x, orig_pos.y); DestroyWindow(hTree); } @@ -2835,7 +2917,7 @@ START_TEST(treeview) test_WM_PAINT(); test_delete_items(); test_cchildren(); - test_htreeitem_layout(); + test_htreeitem_layout(FALSE); test_TVS_CHECKBOXES(); test_TVM_GETNEXTITEM(); test_TVM_HITTEST(); @@ -2867,11 +2949,12 @@ START_TEST(treeview) test_get_set_tooltips(); test_get_set_unicodeformat(); test_expandinvisible(); + test_expand(); test_itemedit(); test_treeview_classinfo(); test_delete_items(); test_cchildren(); - test_htreeitem_layout(); + test_htreeitem_layout(TRUE); test_TVM_GETNEXTITEM(); test_TVM_HITTEST(); test_WM_GETDLGCODE(); diff --git a/modules/rostests/winetests/comctl32/updown.c b/modules/rostests/winetests/comctl32/updown.c index f549c41583e..1f4e38f0ebd 100644 --- a/modules/rostests/winetests/comctl32/updown.c +++ b/modules/rostests/winetests/comctl32/updown.c @@ -697,13 +697,13 @@ static void test_updown_base(void) r = SendMessageA(updown, UDM_SETPOS, 0, 10); expect(50, r); - GetWindowTextA(g_edit, text, sizeof(text)/sizeof(CHAR)); + GetWindowTextA(g_edit, text, ARRAY_SIZE(text)); ok(lstrcmpA(text, "10") == 0, "Expected '10', got '%s'\n", text); r = SendMessageA(updown, UDM_SETBASE, 16, 0); expect(10, r); - GetWindowTextA(g_edit, text, sizeof(text)/sizeof(CHAR)); + GetWindowTextA(g_edit, text, ARRAY_SIZE(text)); /* FIXME: currently hex output isn't properly formatted, but for this test only change from initial text matters */ ok(lstrcmpA(text, "10") != 0, "Expected '0x000A', got '%s'\n", text); @@ -837,20 +837,20 @@ static void test_UDS_SETBUDDYINT(void) style = GetWindowLongA(updown, GWL_STYLE); ok(style & UDS_SETBUDDYINT, "Expected UDS_SETBUDDY to be set\n"); SendMessageA(updown, UDM_SETPOS, 0, 20); - GetWindowTextA(g_edit, text, sizeof(text)/sizeof(CHAR)); + GetWindowTextA(g_edit, text, ARRAY_SIZE(text)); ok(lstrlenA(text) == 0, "Expected empty string\n"); DestroyWindow(updown); /* creating with UDS_SETBUDDYINT */ updown = create_updown_control(UDS_SETBUDDYINT | UDS_ALIGNRIGHT, g_edit); - GetWindowTextA(g_edit, text, sizeof(text)/sizeof(CHAR)); + GetWindowTextA(g_edit, text, ARRAY_SIZE(text)); /* 50 is initial value here */ ok(lstrcmpA(text, "50") == 0, "Expected '50', got '%s'\n", text); /* now remove style flag */ style = GetWindowLongA(updown, GWL_STYLE); SetWindowLongA(updown, GWL_STYLE, style & ~UDS_SETBUDDYINT); SendMessageA(updown, UDM_SETPOS, 0, 20); - GetWindowTextA(g_edit, text, sizeof(text)/sizeof(CHAR)); + GetWindowTextA(g_edit, text, ARRAY_SIZE(text)); ok(lstrcmpA(text, "20") == 0, "Expected '20', got '%s'\n", text); /* set edit text directly, check position */ strcpy(text, "10"); @@ -872,7 +872,7 @@ static void test_UDS_SETBUDDYINT(void) style = GetWindowLongA(updown, GWL_STYLE); SetWindowLongA(updown, GWL_STYLE, style | UDS_SETBUDDYINT); SendMessageA(updown, UDM_SETPOS, 0, 30); - GetWindowTextA(g_edit, text, sizeof(text)/sizeof(CHAR)); + GetWindowTextA(g_edit, text, ARRAY_SIZE(text)); ok(lstrcmpA(text, "30") == 0, "Expected '30', got '%s'\n", text); DestroyWindow(updown); }