diff --git a/modules/rostests/winetests/comctl32/button.c b/modules/rostests/winetests/comctl32/button.c index edfa447baba..ff008ba8e0c 100644 --- a/modules/rostests/winetests/comctl32/button.c +++ b/modules/rostests/winetests/comctl32/button.c @@ -47,8 +47,9 @@ static BOOL (WINAPI *pImageList_Destroy)(HIMAGELIST); /****************** button message test *************************/ #define ID_BUTTON 0x000e -#define COMBINED_SEQ_INDEX 0 -#define NUM_MSG_SEQUENCES 1 +#define COMBINED_SEQ_INDEX 0 +#define PARENT_CD_SEQ_INDEX 1 +#define NUM_MSG_SEQUENCES 2 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES]; @@ -160,11 +161,40 @@ static LRESULT CALLBACK button_subclass_proc(HWND hwnd, UINT message, WPARAM wPa return ret; } +static struct +{ + DWORD button; + UINT line; + UINT state; + DWORD ret; + BOOL empty; +} test_cd; + +#define set_test_cd_state(s) do { \ + test_cd.state = (s); \ + test_cd.empty = TRUE; \ + test_cd.line = __LINE__; \ +} while (0) + +#define set_test_cd_ret(r) do { \ + test_cd.ret = (r); \ + test_cd.empty = TRUE; \ + test_cd.line = __LINE__; \ +} while (0) + +static void disable_test_cd(void) +{ + test_cd.line = 0; +} + static LRESULT WINAPI test_parent_wndproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static LONG defwndproc_counter = 0; static LONG beginpaint_counter = 0; + static HDC cd_first_hdc; struct message msg = { 0 }; + NMCUSTOMDRAW *cd = (NMCUSTOMDRAW*)lParam; + NMBCDROPDOWN *bcd = (NMBCDROPDOWN*)lParam; LRESULT ret; if (ignore_message( message )) return 0; @@ -184,6 +214,76 @@ static LRESULT WINAPI test_parent_wndproc(HWND hwnd, UINT message, WPARAM wParam add_message(sequences, COMBINED_SEQ_INDEX, &msg); } + if (message == WM_NOTIFY && cd->hdr.code == NM_CUSTOMDRAW && test_cd.line) + { + /* Ignore an inconsistency across Windows versions */ + UINT state = cd->uItemState & ~CDIS_SHOWKEYBOARDCUES; + + /* Some Windows configurations paint twice with different DC */ + if (test_cd.empty) + { + cd_first_hdc = cd->hdc; + test_cd.empty = FALSE; + } + + ok_(__FILE__,test_cd.line)(!(cd->dwDrawStage & CDDS_ITEM), + "[%u] CDDS_ITEM is set\n", test_cd.button); + + ok_(__FILE__,test_cd.line)(state == test_cd.state, + "[%u] expected uItemState %u, got %u\n", test_cd.button, + test_cd.state, state); + + msg.message = message; + msg.flags = sent|parent|wparam|lparam|id|custdraw; + msg.wParam = wParam; + msg.lParam = lParam; + msg.id = NM_CUSTOMDRAW; + msg.stage = cd->dwDrawStage; + if (cd->hdc == cd_first_hdc) + add_message(sequences, PARENT_CD_SEQ_INDEX, &msg); + + ret = test_cd.ret; + switch (msg.stage) + { + case CDDS_PREERASE: + ret &= ~CDRF_NOTIFYPOSTPAINT; + cd->dwItemSpec = 0xdeadbeef; + break; + case CDDS_PREPAINT: + ret &= ~CDRF_NOTIFYPOSTERASE; + break; + case CDDS_POSTERASE: + case CDDS_POSTPAINT: + ok_(__FILE__,test_cd.line)(cd->dwItemSpec == 0xdeadbeef, + "[%u] NMCUSTOMDRAW was not shared, stage %u\n", test_cd.button, msg.stage); + break; + } + return ret; + } + + if (message == WM_NOTIFY && bcd->hdr.code == BCN_DROPDOWN) + { + UINT button = GetWindowLongW(bcd->hdr.hwndFrom, GWL_STYLE) & BS_TYPEMASK; + RECT rc; + + GetClientRect(bcd->hdr.hwndFrom, &rc); + + ok(bcd->hdr.hwndFrom != NULL, "Received BCN_DROPDOWN with no hwnd attached, wParam %lu id %lu\n", + wParam, bcd->hdr.idFrom); + ok(bcd->hdr.idFrom == wParam, "[%u] Mismatch between wParam (%lu) and idFrom (%lu)\n", + button, wParam, bcd->hdr.idFrom); + ok(EqualRect(&rc, &bcd->rcButton), "[%u] Wrong rcButton, expected %s got %s\n", + button, wine_dbgstr_rect(&rc), wine_dbgstr_rect(&bcd->rcButton)); + + msg.message = message; + msg.flags = sent|parent|wparam|lparam|id; + msg.wParam = wParam; + msg.lParam = lParam; + msg.id = BCN_DROPDOWN; + add_message(sequences, COMBINED_SEQ_INDEX, &msg); + return 0; + } + if (message == WM_PAINT) { PAINTSTRUCT ps; @@ -461,6 +561,60 @@ static const struct message setcheck_radio_redraw_seq[] = { 0 } }; +static const struct message empty_cd_seq[] = { { 0 } }; + +static const struct message pre_cd_seq[] = +{ + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE }, + { 0 } +}; + +static const struct message pre_pre_cd_seq[] = +{ + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE }, + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREPAINT }, + { 0 } +}; + +static const struct message pre_post_pre_cd_seq[] = +{ + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE }, + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_POSTERASE }, + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREPAINT }, + { 0 } +}; + +static const struct message pre_pre_post_cd_seq[] = +{ + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE }, + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREPAINT }, + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_POSTPAINT }, + { 0 } +}; + +static const struct message pre_post_pre_post_cd_seq[] = +{ + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE }, + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_POSTERASE }, + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREPAINT }, + { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_POSTPAINT }, + { 0 } +}; + +static const struct message bcn_dropdown_seq[] = +{ + { WM_KEYDOWN, sent|wparam|lparam, VK_DOWN, 0 }, + { BCM_SETDROPDOWNSTATE, sent|wparam|lparam|defwinproc, 1, 0 }, + { WM_NOTIFY, sent|parent|id, 0, 0, BCN_DROPDOWN }, + { BCM_SETDROPDOWNSTATE, sent|wparam|lparam|defwinproc, 0, 0 }, + { WM_KEYUP, sent|wparam|lparam, VK_DOWN, 0xc0000000 }, + { WM_PAINT, sent }, + { WM_DRAWITEM, sent|parent|optional }, /* for owner draw button */ + { WM_PAINT, sent|optional }, /* sometimes sent rarely */ + { WM_DRAWITEM, sent|parent|optional }, + { 0 } +}; + static HWND create_button(DWORD style, HWND parent) { HMENU menuid = 0; @@ -479,6 +633,13 @@ static HWND create_button(DWORD style, HWND parent) static void test_button_messages(void) { + enum cd_seq_type + { + cd_seq_empty, + cd_seq_normal, + cd_seq_optional + }; + static const struct { DWORD style; @@ -489,55 +650,74 @@ static void test_button_messages(void) const struct message *setstate; const struct message *clearstate; const struct message *setcheck; + enum cd_seq_type cd_setfocus_type; + enum cd_seq_type cd_setstyle_type; + enum cd_seq_type cd_setstate_type; + enum cd_seq_type cd_setcheck_type; } button[] = { { BS_PUSHBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON, setfocus_seq, killfocus_seq, setstyle_seq, - setstate_seq, setstate_seq, setcheck_ignored_seq }, + setstate_seq, setstate_seq, setcheck_ignored_seq, + cd_seq_normal, cd_seq_normal, cd_seq_normal, cd_seq_optional }, { BS_DEFPUSHBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON, setfocus_seq, killfocus_seq, setstyle_seq, - setstate_seq, setstate_seq, setcheck_ignored_seq }, + setstate_seq, setstate_seq, setcheck_ignored_seq, + cd_seq_normal, cd_seq_normal, cd_seq_normal, cd_seq_optional }, { BS_CHECKBOX, DLGC_BUTTON, setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, - setstate_static_seq, setstate_static_seq, setcheck_static_seq }, + setstate_static_seq, setstate_static_seq, setcheck_static_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional }, { BS_AUTOCHECKBOX, DLGC_BUTTON, setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, - setstate_static_seq, setstate_static_seq, setcheck_static_seq }, + setstate_static_seq, setstate_static_seq, setcheck_static_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional }, { BS_RADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON, setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, - setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq }, + setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional }, { BS_3STATE, DLGC_BUTTON, setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, - setstate_static_seq, setstate_static_seq, setcheck_static_seq }, + setstate_static_seq, setstate_static_seq, setcheck_static_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional }, { BS_AUTO3STATE, DLGC_BUTTON, setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, - setstate_static_seq, setstate_static_seq, setcheck_static_seq }, + setstate_static_seq, setstate_static_seq, setcheck_static_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional }, { BS_GROUPBOX, DLGC_STATIC, setfocus_groupbox_seq, killfocus_static_seq, setstyle_static_seq, - setstate_static_seq, setstate_static_seq, setcheck_ignored_seq }, + setstate_static_seq, setstate_static_seq, setcheck_ignored_seq, + cd_seq_empty, cd_seq_empty, cd_seq_empty, cd_seq_empty }, { BS_USERBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON, setfocus_seq, killfocus_seq, setstyle_user_seq, - setstate_user_seq, clearstate_seq, setcheck_ignored_seq }, + setstate_user_seq, clearstate_seq, setcheck_ignored_seq, + cd_seq_normal, cd_seq_empty, cd_seq_empty, cd_seq_empty }, { BS_AUTORADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON, setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, - setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq }, + setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional }, { BS_OWNERDRAW, DLGC_BUTTON, setfocus_ownerdraw_seq, killfocus_ownerdraw_seq, setstyle_ownerdraw_seq, - setstate_ownerdraw_seq, clearstate_ownerdraw_seq, setcheck_ignored_seq }, + setstate_ownerdraw_seq, clearstate_ownerdraw_seq, setcheck_ignored_seq, + cd_seq_empty, cd_seq_empty, cd_seq_empty, cd_seq_empty }, { BS_SPLITBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON | DLGC_WANTARROWS, setfocus_seq, killfocus_seq, setstyle_seq, - setstate_seq, setstate_seq, setcheck_ignored_seq }, + setstate_seq, setstate_seq, setcheck_ignored_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_empty }, { BS_DEFSPLITBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON | DLGC_WANTARROWS, setfocus_seq, killfocus_seq, setstyle_seq, - setstate_seq, setstate_seq, setcheck_ignored_seq }, + setstate_seq, setstate_seq, setcheck_ignored_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_empty }, { BS_COMMANDLINK, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON, setfocus_seq, killfocus_seq, setstyle_seq, - setstate_seq, setstate_seq, setcheck_ignored_seq }, + setstate_seq, setstate_seq, setcheck_ignored_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_empty }, { BS_DEFCOMMANDLINK, DLGC_BUTTON | DLGC_DEFPUSHBUTTON, setfocus_seq, killfocus_seq, setstyle_seq, - setstate_seq, setstate_seq, setcheck_ignored_seq }, + setstate_seq, setstate_seq, setcheck_ignored_seq, + cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_empty } }; LOGFONTA logfont = { 0 }; - const struct message *seq; + const struct message *seq, *cd_seq; HFONT zfont, hfont2; unsigned int i; HWND hwnd, parent; @@ -565,6 +745,11 @@ static void test_button_messages(void) hfont2 = CreateFontIndirectA(&logfont); ok(hfont2 != NULL, "Failed to create Tahoma font\n"); +#define check_cd_seq(type, context) do { \ + if (button[i].type != cd_seq_optional || !test_cd.empty) \ + ok_sequence(sequences, PARENT_CD_SEQ_INDEX, cd_seq, "[CustomDraw] " context, FALSE); \ + } while(0) + for (i = 0; i < ARRAY_SIZE(button); i++) { HFONT prevfont, hfont; @@ -572,6 +757,7 @@ static void test_button_messages(void) DWORD style, state; HDC hdc; + test_cd.button = button[i].style; hwnd = create_button(button[i].style, parent); ok(hwnd != NULL, "Failed to create a button.\n"); @@ -599,27 +785,35 @@ static void test_button_messages(void) SetFocus(0); flush_events(); SetFocus(0); + cd_seq = (button[i].cd_setfocus_type == cd_seq_empty) ? empty_cd_seq : pre_pre_cd_seq; flush_sequences(sequences, NUM_MSG_SEQUENCES); + set_test_cd_ret(CDRF_DODEFAULT); + set_test_cd_state(CDIS_FOCUS); - todo = button[i].style != BS_OWNERDRAW; ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus()); SetFocus(hwnd); SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); - ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setfocus, "SetFocus(hwnd) on a button", todo); + ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setfocus, "SetFocus(hwnd) on a button", FALSE); + check_cd_seq(cd_setfocus_type, "SetFocus(hwnd)"); - todo = button[i].style == BS_OWNERDRAW; + set_test_cd_state(0); SetFocus(0); SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); - ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].killfocus, "SetFocus(0) on a button", todo); + ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].killfocus, "SetFocus(0) on a button", FALSE); + check_cd_seq(cd_setfocus_type, "SetFocus(0)"); ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus()); + cd_seq = (button[i].cd_setstyle_type == cd_seq_empty) ? empty_cd_seq : pre_pre_cd_seq; + set_test_cd_state(0); + SendMessageA(hwnd, BM_SETSTYLE, button[i].style | BS_BOTTOM, TRUE); SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); todo = button[i].style == BS_OWNERDRAW; ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setstyle, "BM_SETSTYLE on a button", todo); + check_cd_seq(cd_setstyle_type, "BM_SETSTYLE"); style = GetWindowLongA(hwnd, GWL_STYLE); style &= ~(WS_VISIBLE | WS_CHILD | BS_NOTIFY); @@ -629,12 +823,15 @@ static void test_button_messages(void) state = SendMessageA(hwnd, BM_GETSTATE, 0, 0); ok(state == 0, "expected state 0, got %04x\n", state); + cd_seq = (button[i].cd_setstate_type == cd_seq_empty) ? empty_cd_seq : pre_pre_cd_seq; flush_sequences(sequences, NUM_MSG_SEQUENCES); + set_test_cd_state(CDIS_SELECTED); SendMessageA(hwnd, BM_SETSTATE, TRUE, 0); SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setstate, "BM_SETSTATE/TRUE on a button", FALSE); + check_cd_seq(cd_setstate_type, "BM_SETSTATE/TRUE"); state = SendMessageA(hwnd, BM_GETSTATE, 0, 0); ok(state == BST_PUSHED, "expected state 0x0004, got %04x\n", state); @@ -644,11 +841,13 @@ static void test_button_messages(void) ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style); flush_sequences(sequences, NUM_MSG_SEQUENCES); + set_test_cd_state(0); SendMessageA(hwnd, BM_SETSTATE, FALSE, 0); SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].clearstate, "BM_SETSTATE/FALSE on a button", FALSE); + check_cd_seq(cd_setstate_type, "BM_SETSTATE/FALSE"); state = SendMessageA(hwnd, BM_GETSTATE, 0, 0); ok(state == 0, "expected state 0, got %04x\n", state); @@ -660,7 +859,9 @@ static void test_button_messages(void) state = SendMessageA(hwnd, BM_GETCHECK, 0, 0); ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state); + cd_seq = (button[i].cd_setcheck_type == cd_seq_empty) ? empty_cd_seq : pre_pre_cd_seq; flush_sequences(sequences, NUM_MSG_SEQUENCES); + set_test_cd_state(0); if (button[i].style == BS_RADIOBUTTON || button[i].style == BS_AUTORADIOBUTTON) @@ -674,6 +875,7 @@ static void test_button_messages(void) SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); ok_sequence(sequences, COMBINED_SEQ_INDEX, seq, "BM_SETCHECK on a button", FALSE); + check_cd_seq(cd_setcheck_type, "BM_SETCHECK"); state = SendMessageA(hwnd, BM_GETCHECK, 0, 0); ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state); @@ -683,11 +885,13 @@ static void test_button_messages(void) ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style); flush_sequences(sequences, NUM_MSG_SEQUENCES); + set_test_cd_state(0); SendMessageA(hwnd, BM_SETCHECK, BST_CHECKED, 0); SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setcheck, "BM_SETCHECK on a button", FALSE); + check_cd_seq(cd_setcheck_type, "BM_SETCHECK"); state = SendMessageA(hwnd, BM_GETCHECK, 0, 0); if (button[i].style == BS_PUSHBUTTON || @@ -735,9 +939,60 @@ static void test_button_messages(void) DeleteDC(hdc); + /* Test Custom Draw return values */ + if (button[i].cd_setfocus_type != cd_seq_empty && + broken(button[i].style != BS_USERBUTTON) /* WinXP */) + { + static const struct + { + const char *context; + LRESULT val; + const struct message *seq; + } ret[] = { + { "CDRF_DODEFAULT", CDRF_DODEFAULT, pre_pre_cd_seq }, + { "CDRF_DOERASE", CDRF_DOERASE, pre_pre_cd_seq }, + { "CDRF_SKIPDEFAULT", CDRF_SKIPDEFAULT, pre_cd_seq }, + { "CDRF_SKIPDEFAULT | CDRF_NOTIFYPOSTERASE | CDRF_NOTIFYPOSTPAINT", + CDRF_SKIPDEFAULT | CDRF_NOTIFYPOSTERASE | CDRF_NOTIFYPOSTPAINT, pre_cd_seq }, + { "CDRF_NOTIFYPOSTERASE", CDRF_NOTIFYPOSTERASE, pre_post_pre_cd_seq }, + { "CDRF_NOTIFYPOSTPAINT", CDRF_NOTIFYPOSTPAINT, pre_pre_post_cd_seq }, + { "CDRF_NOTIFYPOSTERASE | CDRF_NOTIFYPOSTPAINT", + CDRF_NOTIFYPOSTERASE | CDRF_NOTIFYPOSTPAINT, pre_post_pre_post_cd_seq }, + }; + UINT k; + + for (k = 0; k < ARRAY_SIZE(ret); k++) + { + disable_test_cd(); + SetFocus(0); + set_test_cd_ret(ret[k].val); + set_test_cd_state(CDIS_FOCUS); + SetFocus(hwnd); + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + if (button[i].cd_setfocus_type != cd_seq_optional || !test_cd.empty) + ok_sequence(sequences, PARENT_CD_SEQ_INDEX, ret[k].seq, ret[k].context, FALSE); + } + } + + disable_test_cd(); + + if (!broken(LOBYTE(LOWORD(GetVersion())) < 6)) /* not available pre-Vista */ + { + /* Send down arrow key to make the buttons send the drop down notification */ + flush_sequences(sequences, NUM_MSG_SEQUENCES); + SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0); + SendMessageW(hwnd, WM_KEYUP, VK_DOWN, 0xc0000000); + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + ok_sequence(sequences, COMBINED_SEQ_INDEX, bcn_dropdown_seq, "BCN_DROPDOWN from the button", FALSE); + } + DestroyWindow(hwnd); } +#undef check_cd_seq + DeleteObject(hfont2); DestroyWindow(parent); @@ -1228,6 +1483,214 @@ static void register_parent_class(void) RegisterClassA(&cls); } +static void test_bcm_splitinfo(HWND hwnd) +{ + UINT button = GetWindowLongA(hwnd, GWL_STYLE) & BS_TYPEMASK; + int glyph_size = GetSystemMetrics(SM_CYMENUCHECK); + int border_w = GetSystemMetrics(SM_CXEDGE) * 2; + BUTTON_SPLITINFO info, dummy; + HIMAGELIST img; + BOOL ret; + + memset(&info, 0xCC, sizeof(info)); + info.mask = 0; + memcpy(&dummy, &info, sizeof(info)); + + ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info); + if (ret != TRUE) + { + static BOOL once; + if (!once) + win_skip("BCM_GETSPLITINFO message is unavailable. Skipping related tests\n"); /* Pre-Vista */ + once = TRUE; + return; + } + ok(!memcmp(&info, &dummy, sizeof(info)), "[%u] split info struct was changed with mask = 0\n", button); + + ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, 0); + ok(ret == FALSE, "[%u] expected FALSE, got %d\n", button, ret); + ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, 0); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + + info.mask = BCSIF_GLYPH | BCSIF_SIZE | BCSIF_STYLE; + ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + ok(info.mask == (BCSIF_GLYPH | BCSIF_SIZE | BCSIF_STYLE), "[%u] wrong mask, got %u\n", button, info.mask); + ok(info.himlGlyph == (HIMAGELIST)0x36, "[%u] expected 0x36 default glyph, got 0x%p\n", button, info.himlGlyph); + ok(info.uSplitStyle == BCSS_STRETCH, "[%u] expected 0x%08x default style, got 0x%08x\n", button, BCSS_STRETCH, info.uSplitStyle); + ok(info.size.cx == glyph_size, "[%u] expected %d default size.cx, got %d\n", button, glyph_size, info.size.cx); + ok(info.size.cy == 0, "[%u] expected 0 default size.cy, got %d\n", button, info.size.cy); + + info.mask = BCSIF_SIZE; + info.size.cx = glyph_size + 7; + info.size.cy = 0; + ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + info.size.cx = info.size.cy = 0xdeadbeef; + ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + ok(info.mask == BCSIF_SIZE, "[%u] wrong mask, got %u\n", button, info.mask); + ok(info.size.cx == glyph_size + 7, "[%u] expected %d, got %d\n", button, glyph_size + 7, info.size.cx); + ok(info.size.cy == 0, "[%u] expected 0, got %d\n", button, info.size.cy); + + /* Invalid size.cx resets it to default glyph size, while size.cy is stored */ + info.size.cx = 0; + info.size.cy = -20; + ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + info.size.cx = info.size.cy = 0xdeadbeef; + ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + ok(info.mask == BCSIF_SIZE, "[%u] wrong mask, got %u\n", button, info.mask); + ok(info.size.cx == glyph_size, "[%u] expected %d, got %d\n", button, glyph_size, info.size.cx); + ok(info.size.cy == -20, "[%u] expected -20, got %d\n", button, info.size.cy); + + info.size.cx = -glyph_size - 7; + info.size.cy = -10; + ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + info.size.cx = info.size.cy = 0xdeadbeef; + ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + ok(info.mask == BCSIF_SIZE, "[%u] wrong mask, got %u\n", button, info.mask); + ok(info.size.cx == glyph_size, "[%u] expected %d, got %d\n", button, glyph_size, info.size.cx); + ok(info.size.cy == -10, "[%u] expected -10, got %d\n", button, info.size.cy); + + /* Set to a valid size other than glyph_size */ + info.mask = BCSIF_SIZE; + info.size.cx = glyph_size + 7; + info.size.cy = 11; + ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + info.size.cx = info.size.cy = 0xdeadbeef; + ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + ok(info.mask == BCSIF_SIZE, "[%u] wrong mask, got %u\n", button, info.mask); + ok(info.size.cx == glyph_size + 7, "[%u] expected %d, got %d\n", button, glyph_size + 7, info.size.cx); + ok(info.size.cy == 11, "[%u] expected 11, got %d\n", button, info.size.cy); + + /* Change the glyph, size.cx should be automatically adjusted and size.cy set to 0 */ + dummy.mask = BCSIF_GLYPH; + dummy.himlGlyph = (HIMAGELIST)0x35; + ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&dummy); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + info.mask = BCSIF_GLYPH | BCSIF_SIZE; + ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + ok(info.mask == (BCSIF_GLYPH | BCSIF_SIZE), "[%u] wrong mask, got %u\n", button, info.mask); + ok(info.himlGlyph == (HIMAGELIST)0x35, "[%u] expected 0x35, got %p\n", button, info.himlGlyph); + ok(info.size.cx == glyph_size, "[%u] expected %d, got %d\n", button, glyph_size, info.size.cx); + ok(info.size.cy == 0, "[%u] expected 0, got %d\n", button, info.size.cy); + + /* Unless the size is specified manually */ + dummy.mask = BCSIF_GLYPH | BCSIF_SIZE; + dummy.himlGlyph = (HIMAGELIST)0x34; + dummy.size.cx = glyph_size + 11; + dummy.size.cy = 7; + ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&dummy); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + ok(info.mask == (BCSIF_GLYPH | BCSIF_SIZE), "[%u] wrong mask, got %u\n", button, info.mask); + ok(info.himlGlyph == (HIMAGELIST)0x34, "[%u] expected 0x34, got %p\n", button, info.himlGlyph); + ok(info.size.cx == glyph_size + 11, "[%u] expected %d, got %d\n", button, glyph_size, info.size.cx); + ok(info.size.cy == 7, "[%u] expected 7, got %d\n", button, info.size.cy); + + /* Add the BCSS_IMAGE style manually with the wrong BCSIF_GLYPH mask, should treat it as invalid image */ + info.mask = BCSIF_GLYPH | BCSIF_STYLE; + info.himlGlyph = (HIMAGELIST)0x37; + info.uSplitStyle = BCSS_IMAGE; + ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + info.mask |= BCSIF_SIZE; + ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + ok(info.mask == (BCSIF_GLYPH | BCSIF_STYLE | BCSIF_SIZE), "[%u] wrong mask, got %u\n", button, info.mask); + ok(info.himlGlyph == (HIMAGELIST)0x37, "[%u] expected 0x37, got %p\n", button, info.himlGlyph); + ok(info.uSplitStyle == BCSS_IMAGE, "[%u] expected 0x%08x style, got 0x%08x\n", button, BCSS_IMAGE, info.uSplitStyle); + ok(info.size.cx == border_w, "[%u] expected %d, got %d\n", button, border_w, info.size.cx); + ok(info.size.cy == 0, "[%u] expected 0, got %d\n", button, info.size.cy); + + /* Change the size to prevent ambiguity */ + dummy.mask = BCSIF_SIZE; + dummy.size.cx = glyph_size + 5; + dummy.size.cy = 4; + ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&dummy); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + ok(info.mask == (BCSIF_GLYPH | BCSIF_STYLE | BCSIF_SIZE), "[%u] wrong mask, got %u\n", button, info.mask); + ok(info.himlGlyph == (HIMAGELIST)0x37, "[%u] expected 0x37, got %p\n", button, info.himlGlyph); + ok(info.uSplitStyle == BCSS_IMAGE, "[%u] expected 0x%08x style, got 0x%08x\n", button, BCSS_IMAGE, info.uSplitStyle); + ok(info.size.cx == glyph_size + 5, "[%u] expected %d, got %d\n", button, glyph_size + 5, info.size.cx); + ok(info.size.cy == 4, "[%u] expected 4, got %d\n", button, info.size.cy); + + /* Now remove the BCSS_IMAGE style manually with the wrong BCSIF_IMAGE mask */ + info.mask = BCSIF_IMAGE | BCSIF_STYLE; + info.himlGlyph = (HIMAGELIST)0x35; + info.uSplitStyle = BCSS_STRETCH; + ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + info.mask |= BCSIF_SIZE; + ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + ok(info.mask == (BCSIF_IMAGE | BCSIF_STYLE | BCSIF_SIZE), "[%u] wrong mask, got %u\n", button, info.mask); + ok(info.himlGlyph == (HIMAGELIST)0x35, "[%u] expected 0x35, got %p\n", button, info.himlGlyph); + ok(info.uSplitStyle == BCSS_STRETCH, "[%u] expected 0x%08x style, got 0x%08x\n", button, BCSS_STRETCH, info.uSplitStyle); + ok(info.size.cx == glyph_size, "[%u] expected %d, got %d\n", button, glyph_size, info.size.cx); + ok(info.size.cy == 0, "[%u] expected 0, got %d\n", button, info.size.cy); + + /* Add a proper valid image, the BCSS_IMAGE style should be set automatically */ + img = pImageList_Create(42, 33, ILC_COLOR, 1, 1); + ok(img != NULL, "[%u] failed to create ImageList\n", button); + info.mask = BCSIF_IMAGE; + info.himlGlyph = img; + ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + info.mask |= BCSIF_STYLE | BCSIF_SIZE; + ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + ok(info.mask == (BCSIF_IMAGE | BCSIF_STYLE | BCSIF_SIZE), "[%u] wrong mask, got %u\n", button, info.mask); + ok(info.himlGlyph == img, "[%u] expected %p, got %p\n", button, img, info.himlGlyph); + ok(info.uSplitStyle == (BCSS_IMAGE | BCSS_STRETCH), "[%u] expected 0x%08x style, got 0x%08x\n", button, BCSS_IMAGE | BCSS_STRETCH, info.uSplitStyle); + ok(info.size.cx == 42 + border_w, "[%u] expected %d, got %d\n", button, 42 + border_w, info.size.cx); + ok(info.size.cy == 0, "[%u] expected 0, got %d\n", button, info.size.cy); + pImageList_Destroy(img); + dummy.mask = BCSIF_SIZE; + dummy.size.cx = glyph_size + 5; + dummy.size.cy = 4; + ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&dummy); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + + /* Change it to a glyph; when both specified, BCSIF_GLYPH takes priority */ + info.mask = BCSIF_GLYPH | BCSIF_IMAGE; + info.himlGlyph = (HIMAGELIST)0x37; + ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + info.mask |= BCSIF_STYLE | BCSIF_SIZE; + ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + ok(info.mask == (BCSIF_GLYPH | BCSIF_IMAGE | BCSIF_STYLE | BCSIF_SIZE), "[%u] wrong mask, got %u\n", button, info.mask); + ok(info.himlGlyph == (HIMAGELIST)0x37, "[%u] expected 0x37, got %p\n", button, info.himlGlyph); + ok(info.uSplitStyle == BCSS_STRETCH, "[%u] expected 0x%08x style, got 0x%08x\n", button, BCSS_STRETCH, info.uSplitStyle); + ok(info.size.cx == glyph_size, "[%u] expected %d, got %d\n", button, glyph_size, info.size.cx); + ok(info.size.cy == 0, "[%u] expected 0, got %d\n", button, info.size.cy); + + /* Try a NULL image */ + info.mask = BCSIF_IMAGE; + info.himlGlyph = NULL; + ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + info.mask |= BCSIF_STYLE | BCSIF_SIZE; + ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info); + ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret); + ok(info.mask == (BCSIF_IMAGE | BCSIF_STYLE | BCSIF_SIZE), "[%u] wrong mask, got %u\n", button, info.mask); + ok(info.himlGlyph == NULL, "[%u] expected NULL, got %p\n", button, info.himlGlyph); + ok(info.uSplitStyle == (BCSS_IMAGE | BCSS_STRETCH), "[%u] expected 0x%08x style, got 0x%08x\n", button, BCSS_IMAGE | BCSS_STRETCH, info.uSplitStyle); + ok(info.size.cx == border_w, "[%u] expected %d, got %d\n", button, border_w, info.size.cx); + ok(info.size.cy == 0, "[%u] expected 0, got %d\n", button, info.size.cy); +} + static void test_button_data(void) { static const DWORD styles[] = @@ -1280,6 +1743,9 @@ static void test_button_data(void) ok(desc->style == (WS_CHILD | BS_NOTIFY | styles[i]), "Unexpected 'style' field.\n"); } + /* Data set and retrieved by these messages is valid for all buttons */ + test_bcm_splitinfo(hwnd); + DestroyWindow(hwnd); } @@ -1431,14 +1897,31 @@ static void test_bcm_get_ideal_size(void) { static const char *button_text2 = "WWWW\nWWWW"; static const char *button_text = "WWWW"; + static const WCHAR button_note_short[] = { 'W',0 }; + static const WCHAR button_note_long[] = { 'W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W',0 }; + static const WCHAR button_note_wordy[] = { 'T','h','i','s',' ','i','s',' ','a',' ','l','o','n','g',' ','n','o','t','e',' ','f','o','r',' ','t','h','e',' ','b','u','t','t','o','n',',',' ', + 'w','i','t','h',' ','m','a','n','y',' ','w','o','r','d','s',',',' ','w','h','i','c','h',' ','s','h','o','u','l','d',' ','b','e',' ', + 'o','v','e','r','a','l','l',' ','l','o','n','g','e','r',' ','t','h','a','n',' ','t','h','e',' ','t','e','x','t',' ','(','g','i','v','e','n',' ', + 't','h','e',' ','s','m','a','l','l','e','r',' ','f','o','n','t',')',' ','a','n','d',' ','t','h','u','s',' ','w','r','a','p','.',0 }; 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; + const LONG client_width = 400, client_height = 200, extra_width = 123, large_height = 500; + struct + { + DWORD style; + LONG extra_width; + } pushtype[] = + { + { BS_PUSHBUTTON, 0 }, + { BS_DEFPUSHBUTTON, 0 }, + { BS_SPLITBUTTON, extra_width * 2 + GetSystemMetrics(SM_CXEDGE) }, + { BS_DEFSPLITBUTTON, extra_width * 2 + GetSystemMetrics(SM_CXEDGE) } + }; + LONG image_width = 48, height = 48, line_count, text_width; HFONT hfont, prev_font; DWORD style, type; BOOL ret; @@ -1453,7 +1936,7 @@ static void test_bcm_get_ideal_size(void) HIMAGELIST himl; BUTTON_IMAGELIST biml = {0}; RECT rect; - INT i, j; + INT i, j, k; /* Check for NULL pointer handling */ hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_PUSHBUTTON | default_style, 0, 0, client_width, client_height, @@ -1496,116 +1979,141 @@ static void test_bcm_get_ideal_size(void) /* 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++) + +#define set_split_info(hwnd) do { \ + BUTTON_SPLITINFO _info; \ + int _ret; \ + _info.mask = BCSIF_SIZE; \ + _info.size.cx = extra_width; \ + _info.size.cy = large_height; \ + _ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&_info); \ + ok(_ret == TRUE, "Expected BCM_SETSPLITINFO message to return true\n"); \ +} while (0) + + for (k = 0; k < ARRAY_SIZE(pushtype); k++) { - 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); + /* Only bitmap for push button, ideal size should be enough for image and text */ + hwnd = CreateWindowA(WC_BUTTONA, button_text, pushtype[k].style | BS_BITMAP | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); ok(hwnd != NULL, "Expect hwnd not NULL\n"); + set_split_info(hwnd); + SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp); 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); + /* Ideal size contains text rect even show bitmap only */ + ok(size.cx >= image_width + text_width + pushtype[k].extra_width && size.cy >= max(height, tm.tmHeight), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, + image_width + text_width + pushtype[k].extra_width, size.cy, max(height, tm.tmHeight)); + ok(size.cy < large_height, "Expect ideal cy %d < %d\n", size.cy, large_height); 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 = pushtype[k].style | 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"); + set_split_info(hwnd); + 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 + pushtype[k].extra_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 + pushtype[k].extra_width, size.cy, max(height, tm.tmHeight)); + else + ok(size.cx >= max(text_width, height) + pushtype[k].extra_width && 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) + pushtype[k].extra_width, size.cy, height + tm.tmHeight); + ok(size.cy < large_height, "Expect ideal cy %d < %d\n", size.cy, large_height); + DestroyWindow(hwnd); + } + + /* Image list alignments */ + biml.himl = himl; + for (i = 0; i < ARRAY_SIZE(imagelist_aligns); i++) + { + biml.uAlign = imagelist_aligns[i]; + hwnd = CreateWindowA(WC_BUTTONA, button_text, pushtype[k].style | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + set_split_info(hwnd); + 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) + pushtype[k].extra_width && 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) + pushtype[k].extra_width, 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 + pushtype[k].extra_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 + pushtype[k].extra_width, size.cy, max(height, tm.tmHeight)); + else + ok(size.cx >= image_width + pushtype[k].extra_width && size.cy >= height, + "Align:%d expect ideal cx %d >= %d and ideal cy %d >= %d\n", + biml.uAlign, size.cx, image_width + pushtype[k].extra_width, size.cy, height); + ok(size.cy < large_height, "Expect ideal cy %d < %d\n", size.cy, large_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, pushtype[k].style | BS_ICON | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + set_split_info(hwnd); + 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 + pushtype[k].extra_width && size.cy >= max(height, tm.tmHeight), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, + image_width + text_width + pushtype[k].extra_width, size.cy, max(height, tm.tmHeight)); + ok(size.cy < large_height, "Expect ideal cy %d < %d\n", size.cy, large_height); + DestroyWindow(hwnd); + + /* Show icon and text */ + hwnd = CreateWindowA(WC_BUTTONA, button_text, pushtype[k].style | default_style, 0, 0, client_width, + client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expect hwnd not NULL\n"); + set_split_info(hwnd); + 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 + pushtype[k].extra_width && size.cy >= max(height, tm.tmHeight), + "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, + image_width + text_width + pushtype[k].extra_width, size.cy, max(height, tm.tmHeight)); + ok(size.cy < large_height, "Expect ideal cy %d < %d\n", size.cy, large_height); + DestroyWindow(hwnd); + DestroyIcon(hicon); } - /* 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); +#undef set_split_info /* Checkbox */ /* Both bitmap and text for checkbox, ideal size is only enough for text because it doesn't support image(but not image list)*/ @@ -1667,8 +2175,8 @@ static void test_bcm_get_ideal_size(void) 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); + 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 { @@ -1704,9 +2212,9 @@ static void test_bcm_get_ideal_size(void) } 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); + 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 { @@ -1718,8 +2226,58 @@ static void test_bcm_get_ideal_size(void) } } + /* Command Link with note */ + hwnd = CreateWindowA(WC_BUTTONA, "a", style, 0, 0, client_width, client_height, NULL, NULL, 0, NULL); + ok(hwnd != NULL, "Expected hwnd not NULL\n"); + SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp); + ret = SendMessageA(hwnd, BCM_SETNOTE, 0, (LPARAM)button_note_short); + ok(ret == TRUE, "Expected BCM_SETNOTE to return true\n"); + size.cx = 13; + size.cy = 0; + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret == TRUE, "Expected BCM_GETIDEALSIZE message to return true\n"); + ok(size.cx == 13 && size.cy > 0, "Expected ideal cx %d == %d and ideal cy %d > %d\n", size.cx, 13, size.cy, 0); + height = size.cy; + size.cx = 32767; + size.cy = 7; + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret == TRUE, "Expected BCM_GETIDEALSIZE message to return true\n"); + ok(size.cx < 32767, "Expected ideal cx to have been adjusted\n"); + ok(size.cx > image_width && size.cy == height, "Expected ideal cx %d > %d and ideal cy %d == %d\n", size.cx, image_width, size.cy, height); + + /* Try longer note without word breaks, shouldn't extend height because no word splitting */ + ret = SendMessageA(hwnd, BCM_SETNOTE, 0, (LPARAM)button_note_long); + ok(ret == TRUE, "Expected BCM_SETNOTE to return true\n"); + k = size.cx; + size.cy = 0; + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret == TRUE, "Expected BCM_GETIDEALSIZE message to return true\n"); + ok(size.cx == k && size.cy == height, "Expected ideal cx %d == %d and ideal cy %d == %d\n", size.cx, k, size.cy, height); + + /* Now let it extend the width */ + size.cx = 32767; + size.cy = 0; + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret == TRUE, "Expected BCM_GETIDEALSIZE message to return true\n"); + ok(size.cx > k && size.cy == height, "Expected ideal cx %d > %d and ideal cy %d == %d\n", size.cx, k, size.cy, height); + + /* Use a very long note with words and the same width, should extend the height due to word wrap */ + ret = SendMessageA(hwnd, BCM_SETNOTE, 0, (LPARAM)button_note_wordy); + ok(ret == TRUE, "Expected BCM_SETNOTE to return true\n"); + k = size.cx; + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret == TRUE, "Expected BCM_GETIDEALSIZE message to return true\n"); + ok(size.cx <= k && size.cy > height, "Expected ideal cx %d <= %d and ideal cy %d > %d\n", size.cx, k, size.cy, height); + + /* Now try the wordy note with a width smaller than the image itself, which prevents wrapping */ + size.cx = 13; + ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size); + ok(ret == TRUE, "Expected BCM_GETIDEALSIZE message to return true\n"); + ok(size.cx == 13 && size.cy == height, "Expected ideal cx %d == %d and ideal cy %d == %d\n", size.cx, 13, size.cy, height); + DestroyWindow(hwnd); + + pImageList_Destroy(himl); - DestroyIcon(hicon); DeleteObject(hbmp); DeleteObject(hmask); ReleaseDC(0, hdc); diff --git a/modules/rostests/winetests/comctl32/edit.c b/modules/rostests/winetests/comctl32/edit.c index bc44d0c2404..6013cafb59d 100644 --- a/modules/rostests/winetests/comctl32/edit.c +++ b/modules/rostests/winetests/comctl32/edit.c @@ -1485,16 +1485,31 @@ static void test_edit_control_scroll(void) DestroyWindow (hwEdit); } +static BOOL is_cjk(HDC dc) +{ + const DWORD FS_DBCS_MASK = FS_JISJAPAN|FS_CHINESESIMP|FS_WANSUNG|FS_CHINESETRAD|FS_JOHAB; + FONTSIGNATURE fs; + + switch (GdiGetCodePage(dc)) { + case 932: case 936: case 949: case 950: case 1361: + return TRUE; + default: + return (GetTextCharsetInfo(dc, &fs, 0) != DEFAULT_CHARSET && + (fs.fsCsb[0] & FS_DBCS_MASK)); + } +} + static void test_margins_usefontinfo(UINT charset) { - INT margins, threshold, expect, empty_expect, small_expect; HWND hwnd; HDC hdc; + TEXTMETRICW tm; SIZE size; - BOOL cjk; LOGFONTA lf; HFONT hfont; RECT rect; + INT margins, threshold, expect, empty_expect; + const UINT small_margins = MAKELONG(1, 5); memset(&lf, 0, sizeof(lf)); lf.lfHeight = -11; @@ -1513,43 +1528,31 @@ static void test_margins_usefontinfo(UINT charset) hdc = GetDC(hwnd); hfont = SelectObject(hdc, hfont); - size.cx = GdiGetCharDimensions( hdc, NULL, &size.cy ); - expect = MAKELONG(size.cx / 2, size.cx / 2); - small_expect = 0; - empty_expect = size.cx >= 28 ? small_expect : expect; - - charset = GetTextCharset(hdc); - switch (charset) - { - case SHIFTJIS_CHARSET: - case HANGUL_CHARSET: - case GB2312_CHARSET: - case CHINESEBIG5_CHARSET: - cjk = TRUE; - break; - default: - cjk = FALSE; + size.cx = GdiGetCharDimensions( hdc, &tm, &size.cy ); + if ((charset != tm.tmCharSet && charset != DEFAULT_CHARSET) || + !(tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR))) { + skip("%s for charset %d isn't available\n", lf.lfFaceName, charset); + hfont = SelectObject(hdc, hfont); + ReleaseDC(hwnd, hdc); + DestroyWindow(hwnd); + DeleteObject(hfont); + return; } - + expect = MAKELONG(size.cx / 2, size.cx / 2); hfont = SelectObject(hdc, hfont); ReleaseDC(hwnd, hdc); margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); ok(margins == 0, "got %x\n", margins); SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0)); - margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); - if (!cjk) - ok(margins == expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins)); - else - { - ok(HIWORD(margins) > 0 && LOWORD(margins) > 0, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins)); - expect = empty_expect = small_expect = margins; - } + SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO)); + expect = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); DestroyWindow(hwnd); - threshold = (size.cx / 2 + size.cx) * 2; + threshold = HIWORD(expect) + LOWORD(expect) + size.cx * 2; + empty_expect = threshold > 80 ? small_margins : expect; - /* Size below which non-cjk margins are zero */ + /* Size below the threshold, margins remain unchanged */ hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, threshold - 1, 100, NULL, NULL, NULL, NULL); ok(hwnd != NULL, "got %p\n", hwnd); GetClientRect(hwnd, &rect); @@ -1559,11 +1562,13 @@ static void test_margins_usefontinfo(UINT charset) ok(margins == 0, "got %x\n", margins); SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0)); + SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, small_margins); + SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO)); margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); - ok(margins == small_expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins)); + ok(margins == small_margins, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins)); DestroyWindow(hwnd); - /* Size at which non-cjk margins become non-zero */ + /* Size at the threshold, margins become non-zero */ hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, threshold, 100, NULL, NULL, NULL, NULL); ok(hwnd != NULL, "got %p\n", hwnd); GetClientRect(hwnd, &rect); @@ -1573,6 +1578,8 @@ static void test_margins_usefontinfo(UINT charset) ok(margins == 0, "got %x\n", margins); SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0)); + SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, small_margins); + SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO)); margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); ok(margins == expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins)); DestroyWindow(hwnd); @@ -1587,6 +1594,8 @@ static void test_margins_usefontinfo(UINT charset) ok(margins == 0, "got %x\n", margins); SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0)); + SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, small_margins); + SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO)); margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); ok(margins == empty_expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins)); DestroyWindow(hwnd); @@ -1594,6 +1603,151 @@ static void test_margins_usefontinfo(UINT charset) DeleteObject(hfont); } +static INT get_cjk_fontinfo_margin(INT width, INT side_bearing) +{ + INT margin; + if (side_bearing < 0) + margin = min(-side_bearing, width/2); + else + margin = 0; + return margin; +} + +static DWORD get_cjk_font_margins(HDC hdc) +{ + ABC abc[256]; + SHORT left, right; + UINT i; + + if (!GetCharABCWidthsW(hdc, 0, 255, abc)) + return 0; + + left = right = 0; + for (i = 0; i < ARRAY_SIZE(abc); i++) { + if (-abc[i].abcA > right) right = -abc[i].abcA; + if (-abc[i].abcC > left) left = -abc[i].abcC; + } + return MAKELONG(left, right); +} + +static void test_margins_default(const char* facename, UINT charset) +{ + HWND hwnd; + HDC hdc; + TEXTMETRICW tm; + SIZE size; + BOOL cjk; + LOGFONTA lf; + HFONT hfont; + RECT rect; + INT margins, expect, font_expect; + const UINT small_margins = MAKELONG(1, 5); + const WCHAR EditW[] = {'E','d','i','t',0}, strW[] = {'W',0}; + struct char_width_info { + INT lsb, rsb, unknown; + } info; + HMODULE hgdi32; + BOOL (WINAPI *pGetCharWidthInfo)(HDC, struct char_width_info *); + + hgdi32 = GetModuleHandleA("gdi32.dll"); + pGetCharWidthInfo = (void *)GetProcAddress(hgdi32, "GetCharWidthInfo"); + + memset(&lf, 0, sizeof(lf)); + lf.lfHeight = -11; + lf.lfWeight = FW_NORMAL; + lf.lfCharSet = charset; + strcpy(lf.lfFaceName, facename); + + hfont = CreateFontIndirectA(&lf); + ok(hfont != NULL, "got %p\n", hfont); + + /* Unicode version */ + hwnd = CreateWindowExW(0, EditW, strW, WS_POPUP, 0, 0, 5000, 1000, NULL, NULL, NULL, NULL); + ok(hwnd != NULL, "got %p\n", hwnd); + GetClientRect(hwnd, &rect); + ok(!IsRectEmpty(&rect), "got rect %s\n", wine_dbgstr_rect(&rect)); + + hdc = GetDC(hwnd); + hfont = SelectObject(hdc, hfont); + size.cx = GdiGetCharDimensions( hdc, &tm, &size.cy ); + if ((charset != tm.tmCharSet && charset != DEFAULT_CHARSET) || + !(tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR))) { + skip("%s for charset %d isn't available\n", lf.lfFaceName, charset); + hfont = SelectObject(hdc, hfont); + ReleaseDC(hwnd, hdc); + DestroyWindow(hwnd); + DeleteObject(hfont); + return; + } + cjk = is_cjk(hdc); + if (cjk && pGetCharWidthInfo && pGetCharWidthInfo(hdc, &info)) { + short left, right; + + left = get_cjk_fontinfo_margin(size.cx, info.lsb); + right = get_cjk_fontinfo_margin(size.cx, info.rsb); + expect = MAKELONG(left, right); + + font_expect = get_cjk_font_margins(hdc); + if (!font_expect) + /* In this case, margins aren't updated */ + font_expect = small_margins; + } + else + font_expect = expect = MAKELONG(size.cx / 2, size.cx / 2); + + hfont = SelectObject(hdc, hfont); + ReleaseDC(hwnd, hdc); + + margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); + ok(margins == 0, "got %x\n", margins); + SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, small_margins); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0)); + margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); + ok(margins == font_expect, "%s:%d: got %d, %d\n", facename, charset, HIWORD(margins), LOWORD(margins)); + SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, small_margins); + SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO)); + margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); + ok(margins == expect, "%s:%d: expected %d, %d, got %d, %d\n", facename, charset, HIWORD(expect), LOWORD(expect), HIWORD(margins), LOWORD(margins)); + DestroyWindow(hwnd); + + /* ANSI version */ + hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, 5000, 1000, NULL, NULL, NULL, NULL); + ok(hwnd != NULL, "got %p\n", hwnd); + GetClientRect(hwnd, &rect); + ok(!IsRectEmpty(&rect), "got rect %s\n", wine_dbgstr_rect(&rect)); + + margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); + ok(margins == 0, "got %x\n", margins); + SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, small_margins); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0)); + margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); + ok(margins == font_expect, "%s:%d: got %d, %d\n", facename, charset, HIWORD(margins), LOWORD(margins)); + SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, small_margins); + SendMessageA(hwnd, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO)); + margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); + ok(margins == expect, "%s:%d: expected %d, %d, got %d, %d\n", facename, charset, HIWORD(expect), LOWORD(expect), HIWORD(margins), LOWORD(margins)); + DestroyWindow(hwnd); + + DeleteObject(hfont); +} + +static INT CALLBACK find_font_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam) +{ + return 0; +} + +static BOOL is_font_installed(const char*name) +{ + HDC hdc = GetDC(NULL); + BOOL ret = FALSE; + + if (!EnumFontFamiliesA(hdc, name, find_font_proc, 0)) + ret = TRUE; + + ReleaseDC(NULL, hdc); + return ret; +} + static void test_margins(void) { DWORD old_margins, new_margins; @@ -1668,27 +1822,45 @@ static void test_margins(void) but not by < Win 8 and Win 10. */ test_margins_usefontinfo(DEFAULT_CHARSET); -} -static INT CALLBACK find_font_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam) -{ - return 0; + test_margins_default("Tahoma", ANSI_CHARSET); + test_margins_default("Tahoma", EASTEUROPE_CHARSET); + + test_margins_default("Tahoma", HANGUL_CHARSET); + test_margins_default("Tahoma", CHINESEBIG5_CHARSET); + + if (is_font_installed("MS PGothic")) { + test_margins_default("MS PGothic", SHIFTJIS_CHARSET); + test_margins_default("MS PGothic", GREEK_CHARSET); + } + else + skip("MS PGothic is not available, skipping some margin tests\n"); + + if (is_font_installed("Ume P Gothic")) { + test_margins_default("Ume P Gothic", SHIFTJIS_CHARSET); + test_margins_default("Ume P Gothic", GREEK_CHARSET); + } + else + skip("Ume P Gothic is not available, skipping some margin tests\n"); + + if (is_font_installed("SimSun")) { + test_margins_default("SimSun", GB2312_CHARSET); + test_margins_default("SimSun", ANSI_CHARSET); + } + else + skip("SimSun is not available, skipping some margin tests\n"); } static void test_margins_font_change(void) { - DWORD margins, font_margins, ret; + DWORD margins, font_margins; HFONT hfont, hfont2; HWND hwEdit; LOGFONTA lf; - HDC hdc; - hdc = GetDC(0); - ret = EnumFontFamiliesA(hdc, "Arial", find_font_proc, 0); - ReleaseDC(0, hdc); - if (ret) + if (!is_font_installed("Arial")) { - trace("Arial not found - skipping font change margin tests\n"); + skip("Arial not found - skipping font change margin tests\n"); return; } @@ -1699,7 +1871,7 @@ static void test_margins_font_change(void) memset(&lf, 0, sizeof(lf)); strcpy(lf.lfFaceName, "Arial"); lf.lfHeight = 16; - lf.lfCharSet = DEFAULT_CHARSET; + lf.lfCharSet = GREEK_CHARSET; /* to avoid associated charset feature */ hfont = CreateFontIndirectA(&lf); lf.lfHeight = 30; hfont2 = CreateFontIndirectA(&lf); @@ -3143,6 +3315,9 @@ static void test_change_focus(void) HWND hwnd, parent_wnd; WNDPROC oldproc; MSG msg; + POINT orig_pos; + + GetCursorPos(&orig_pos); parent_wnd = CreateWindowA("ParentWnd", "", WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, NULL, NULL, GetModuleHandleA(NULL), NULL); @@ -3157,6 +3332,8 @@ static void test_change_focus(void) oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)edit_subclass_proc); SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc); + SetCursorPos(400, 400); + SetFocus(parent_wnd); flush_sequences(sequences, NUM_MSG_SEQUENCES); SetFocus(hwnd); @@ -3168,6 +3345,8 @@ static void test_change_focus(void) while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); ok_sequence(sequences, COMBINED_SEQ_INDEX, killfocus_combined_seq, "Kill focus", TRUE); + SetCursorPos(orig_pos.x, orig_pos.y); + DestroyWindow(hwnd); } diff --git a/modules/rostests/winetests/comctl32/imagelist.c b/modules/rostests/winetests/comctl32/imagelist.c index 8064a2b98ea..3e538c7c0ce 100644 --- a/modules/rostests/winetests/comctl32/imagelist.c +++ b/modules/rostests/winetests/comctl32/imagelist.c @@ -1563,8 +1563,7 @@ cleanup: if(hbmDst) DeleteObject(hbmDst); - if(hdcDst) - DeleteDC(hdcDst); + DeleteDC(hdcDst); if(hbmMask) DeleteObject(hbmMask); @@ -2181,7 +2180,7 @@ static void test_color_table(UINT ilc) rgb[2].rgbBlue = 0xff; check_color_table("remove all, add 8", hdc, himl, ilc, rgb, default_table); - /* remove all, add 4. Color table remains the same since it's inplicitly + /* remove all, add 4. Color table remains the same since it's implicitly been set by the previous _Add */ ret = pImageList_Remove(himl, -1); ok(ret, "got %d\n", ret); diff --git a/modules/rostests/winetests/comctl32/listbox.c b/modules/rostests/winetests/comctl32/listbox.c index e7894839838..70e212892a4 100644 --- a/modules/rostests/winetests/comctl32/listbox.c +++ b/modules/rostests/winetests/comctl32/listbox.c @@ -784,6 +784,135 @@ static void test_listbox_height(void) DestroyWindow( hList ); } +static void test_changing_selection_styles(void) +{ + static const DWORD styles[] = + { + 0, + LBS_NODATA | LBS_OWNERDRAWFIXED + }; + static const DWORD selstyles[] = + { + 0, + LBS_MULTIPLESEL, + LBS_EXTENDEDSEL, + LBS_MULTIPLESEL | LBS_EXTENDEDSEL + }; + static const LONG selexpect_single[] = { 0, 0, 1 }; + static const LONG selexpect_single2[] = { 1, 0, 0 }; + static const LONG selexpect_multi[] = { 1, 0, 1 }; + static const LONG selexpect_multi2[] = { 1, 1, 0 }; + + HWND parent, listbox; + DWORD style; + LONG ret; + UINT i, j, k; + + parent = create_parent(); + ok(parent != NULL, "Failed to create parent window.\n"); + for (i = 0; i < ARRAY_SIZE(styles); i++) + { + /* Test if changing selection styles affects selection storage */ + for (j = 0; j < ARRAY_SIZE(selstyles); j++) + { + LONG setcursel_expect, selitemrange_expect, getselcount_expect; + const LONG *selexpect; + + listbox = CreateWindowA(WC_LISTBOXA, "TestList", styles[i] | selstyles[j] | WS_CHILD | WS_VISIBLE, + 0, 0, 100, 100, parent, (HMENU)ID_LISTBOX, NULL, 0); + ok(listbox != NULL, "%u: Failed to create ListBox window.\n", j); + + if (selstyles[j] & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) + { + setcursel_expect = LB_ERR; + selitemrange_expect = LB_OKAY; + getselcount_expect = 2; + selexpect = selexpect_multi; + } + else + { + setcursel_expect = 2; + selitemrange_expect = LB_ERR; + getselcount_expect = LB_ERR; + selexpect = selexpect_single; + } + + for (k = 0; k < ARRAY_SIZE(selexpect_multi); k++) + { + ret = SendMessageA(listbox, LB_INSERTSTRING, -1, (LPARAM)"x"); + ok(ret == k, "%u: Unexpected return value %d, expected %d.\n", j, ret, k); + } + ret = SendMessageA(listbox, LB_GETCOUNT, 0, 0); + ok(ret == ARRAY_SIZE(selexpect_multi), "%u: Unexpected count %d.\n", j, ret); + + /* Select items with different methods */ + ret = SendMessageA(listbox, LB_SETCURSEL, 2, 0); + ok(ret == setcursel_expect, "%u: Unexpected return value %d.\n", j, ret); + ret = SendMessageA(listbox, LB_SELITEMRANGE, TRUE, MAKELPARAM(0, 0)); + ok(ret == selitemrange_expect, "%u: Unexpected return value %d.\n", j, ret); + ret = SendMessageA(listbox, LB_SELITEMRANGE, TRUE, MAKELPARAM(2, 2)); + ok(ret == selitemrange_expect, "%u: Unexpected return value %d.\n", j, ret); + + /* Verify that the proper items are selected */ + for (k = 0; k < ARRAY_SIZE(selexpect_multi); k++) + { + ret = SendMessageA(listbox, LB_GETSEL, k, 0); + ok(ret == selexpect[k], "%u: Unexpected selection state %d, expected %d.\n", + j, ret, selexpect[k]); + } + + /* Now change the selection style */ + style = GetWindowLongA(listbox, GWL_STYLE); + ok((style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) == selstyles[j], + "%u: unexpected window styles %#x.\n", j, style); + if (selstyles[j] & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) + style &= ~selstyles[j]; + else + style |= LBS_MULTIPLESEL | LBS_EXTENDEDSEL; + SetWindowLongA(listbox, GWL_STYLE, style); + style = GetWindowLongA(listbox, GWL_STYLE); + ok(!(style & selstyles[j]), "%u: unexpected window styles %#x.\n", j, style); + + /* Verify that the same items are selected */ + ret = SendMessageA(listbox, LB_GETSELCOUNT, 0, 0); + ok(ret == getselcount_expect, "%u: expected %d from LB_GETSELCOUNT, got %d\n", + j, getselcount_expect, ret); + + for (k = 0; k < ARRAY_SIZE(selexpect_multi); k++) + { + ret = SendMessageA(listbox, LB_GETSEL, k, 0); + ok(ret == selexpect[k], "%u: Unexpected selection state %d, expected %d.\n", + j, ret, selexpect[k]); + } + + /* Lastly see if we can still change the selection as before with old style */ + if (setcursel_expect != LB_ERR) setcursel_expect = 0; + ret = SendMessageA(listbox, LB_SETCURSEL, 0, 0); + ok(ret == setcursel_expect, "%u: Unexpected return value %d.\n", j, ret); + ret = SendMessageA(listbox, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 1)); + ok(ret == selitemrange_expect, "%u: Unexpected return value %d.\n", j, ret); + ret = SendMessageA(listbox, LB_SELITEMRANGE, FALSE, MAKELPARAM(2, 2)); + ok(ret == selitemrange_expect, "%u: Unexpected return value %d.\n", j, ret); + + /* And verify the selections */ + selexpect = (selstyles[j] & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) ? selexpect_multi2 : selexpect_single2; + ret = SendMessageA(listbox, LB_GETSELCOUNT, 0, 0); + ok(ret == getselcount_expect, "%u: expected %d from LB_GETSELCOUNT, got %d\n", + j, getselcount_expect, ret); + + for (k = 0; k < ARRAY_SIZE(selexpect_multi); k++) + { + ret = SendMessageA(listbox, LB_GETSEL, k, 0); + ok(ret == selexpect[k], "%u: Unexpected selection state %d, expected %d.\n", + j, ret, selexpect[k]); + } + + DestroyWindow(listbox); + } + } + DestroyWindow(parent); +} + static void test_itemfrompoint(void) { /* WS_POPUP is required in order to have a more accurate size calculation ( @@ -1812,7 +1941,7 @@ static void test_listbox_dlgdir(void) strcpy(pathBuffer, "C:\\"); res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, 0, DDL_DIRECTORY | DDL_EXCLUSIVE); ok(res, "DlgDirList failed to list C:\\ folders\n"); - todo_wine ok(!strcmp(pathBuffer, "*"), "DlgDirList set the invalid path spec '%s', expected '*'\n", pathBuffer); + ok(!strcmp(pathBuffer, "*"), "DlgDirList set the invalid path spec '%s', expected '*'\n", pathBuffer); strcpy(pathBuffer, "C:\\*"); res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, 0, DDL_DIRECTORY | DDL_EXCLUSIVE); @@ -1823,8 +1952,8 @@ static void test_listbox_dlgdir(void) SetLastError(0xdeadbeef); strcpy(pathBuffer, "C:\\INVALID$$DIR"); res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, 0, DDL_DIRECTORY | DDL_EXCLUSIVE); - todo_wine ok(!res, "DlgDirList should have failed with 0 but %d was returned\n", res); - todo_wine ok(GetLastError() == ERROR_NO_WILDCARD_CHARACTERS, + ok(!res, "DlgDirList should have failed with 0 but %d was returned\n", res); + ok(GetLastError() == ERROR_NO_WILDCARD_CHARACTERS, "GetLastError should return 0x589, got 0x%X\n",GetLastError()); DestroyWindow(hWnd); @@ -1867,6 +1996,11 @@ static void test_set_count( void ) GetUpdateRect( listbox, &r, TRUE ); ok( !IsRectEmpty( &r ), "got empty rect\n"); + ret = SendMessageA( listbox, LB_SETCOUNT, -5, 0 ); + ok( ret == 0, "got %d\n", ret ); + ret = SendMessageA( listbox, LB_GETCOUNT, 0, 0 ); + ok( ret == -5, "got %d\n", ret ); + DestroyWindow( listbox ); for (i = 0; i < ARRAY_SIZE(styles); ++i) @@ -1906,6 +2040,69 @@ static void test_GetListBoxInfo(void) DestroyWindow(parent); } +static void test_init_storage( void ) +{ + static const DWORD styles[] = + { + LBS_HASSTRINGS, + LBS_NODATA | LBS_OWNERDRAWFIXED, + }; + HWND parent, listbox; + LONG ret, items_size; + int i, j; + + parent = create_parent(); + for (i = 0; i < ARRAY_SIZE(styles); i++) + { + listbox = CreateWindowA(WC_LISTBOXA, "TestList", styles[i] | WS_CHILD, + 0, 0, 100, 100, parent, (HMENU)ID_LISTBOX, NULL, 0); + + items_size = SendMessageA(listbox, LB_INITSTORAGE, 100, 0); + ok(items_size >= 100, "expected at least 100, got %d\n", items_size); + + ret = SendMessageA(listbox, LB_INITSTORAGE, 0, 0); + ok(ret == items_size, "expected %d, got %d\n", items_size, ret); + + /* it doesn't grow since the space was already reserved */ + ret = SendMessageA(listbox, LB_INITSTORAGE, items_size, 0); + ok(ret == items_size, "expected %d, got %d\n", items_size, ret); + + /* it doesn't shrink the reserved space */ + ret = SendMessageA(listbox, LB_INITSTORAGE, 42, 0); + ok(ret == items_size, "expected %d, got %d\n", items_size, ret); + + /* now populate almost all of it so it's not reserved anymore */ + if (styles[i] & LBS_NODATA) + { + ret = SendMessageA(listbox, LB_SETCOUNT, items_size - 1, 0); + ok(ret == 0, "unexpected return value %d\n", ret); + } + else + { + for (j = 0; j < items_size - 1; j++) + { + ret = SendMessageA(listbox, LB_INSERTSTRING, -1, (LPARAM)""); + ok(ret == j, "expected %d, got %d\n", j, ret); + } + } + + /* we still have one more reserved slot, so it doesn't grow yet */ + ret = SendMessageA(listbox, LB_INITSTORAGE, 1, 0); + ok(ret == items_size, "expected %d, got %d\n", items_size, ret); + + /* fill the slot and check again, it should grow this time */ + ret = SendMessageA(listbox, LB_INSERTSTRING, -1, (LPARAM)""); + ok(ret == items_size - 1, "expected %d, got %d\n", items_size - 1, ret); + ret = SendMessageA(listbox, LB_INITSTORAGE, 0, 0); + ok(ret == items_size, "expected %d, got %d\n", items_size, ret); + ret = SendMessageA(listbox, LB_INITSTORAGE, 1, 0); + ok(ret > items_size, "expected it to grow past %d, got %d\n", items_size, ret); + + DestroyWindow(listbox); + } + DestroyWindow(parent); +} + static void test_missing_lbuttonup(void) { HWND listbox, parent, capture; @@ -2420,11 +2617,13 @@ START_TEST(listbox) test_LB_SELITEMRANGE(); test_LB_SETCURSEL(); test_listbox_height(); + test_changing_selection_styles(); test_itemfrompoint(); test_listbox_item_data(); test_listbox_LB_DIR(); test_listbox_dlgdir(); test_set_count(); + test_init_storage(); test_GetListBoxInfo(); test_missing_lbuttonup(); test_extents(); diff --git a/modules/rostests/winetests/comctl32/listview.c b/modules/rostests/winetests/comctl32/listview.c index ec289fa3f23..45f2b60de2c 100644 --- a/modules/rostests/winetests/comctl32/listview.c +++ b/modules/rostests/winetests/comctl32/listview.c @@ -6475,6 +6475,86 @@ static void test_LVM_GETCOUNTPERPAGE(void) ok(ret, "Failed to unregister test class.\n"); } +static void test_item_state_change(void) +{ + static const DWORD styles[] = { LVS_ICON, LVS_LIST, LVS_REPORT, LVS_SMALLICON }; + LVITEMA item; + HWND hwnd; + DWORD res; + int i; + + for (i = 0; i < ARRAY_SIZE(styles); i++) + { + hwnd = create_listview_control(styles[i]); + + insert_item(hwnd, 0); + + /* LVM_SETITEMSTATE with mask */ + memset(&g_nmlistview, 0xcc, sizeof(g_nmlistview)); + memset(&item, 0, sizeof(item)); + item.mask = LVIF_STATE; + item.stateMask = LVIS_SELECTED; + item.state = LVIS_SELECTED; + res = SendMessageA(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item); + ok(res, "Failed to set item state.\n"); + + ok(g_nmlistview.iItem == item.iItem, "Unexpected item %d.\n", g_nmlistview.iItem); + ok(g_nmlistview.iSubItem == item.iSubItem, "Unexpected subitem %d.\n", g_nmlistview.iSubItem); + ok(g_nmlistview.lParam == item.lParam, "Unexpected lParam.\n"); + ok(g_nmlistview.uNewState == LVIS_SELECTED, "got new state 0x%08x\n", g_nmlistview.uNewState); + ok(g_nmlistview.uOldState == 0, "got old state 0x%08x\n", g_nmlistview.uOldState); + ok(g_nmlistview.uChanged == LVIF_STATE, "got changed 0x%08x\n", g_nmlistview.uChanged); + + /* LVM_SETITEMSTATE 0 mask */ + memset(&g_nmlistview, 0xcc, sizeof(g_nmlistview)); + memset(&item, 0, sizeof(item)); + item.stateMask = LVIS_SELECTED; + item.state = 0; + res = SendMessageA(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item); + ok(res, "Failed to set item state.\n"); + + ok(g_nmlistview.iItem == item.iItem, "Unexpected item %d.\n", g_nmlistview.iItem); + ok(g_nmlistview.iSubItem == item.iSubItem, "Unexpected subitem %d.\n", g_nmlistview.iSubItem); + ok(g_nmlistview.lParam == item.lParam, "Unexpected lParam.\n"); + ok(g_nmlistview.uNewState == 0, "Unexpected new state %#x.\n", g_nmlistview.uNewState); + ok(g_nmlistview.uOldState == LVIS_SELECTED, "Unexpected old state %#x.\n", g_nmlistview.uOldState); + ok(g_nmlistview.uChanged == LVIF_STATE, "Unexpected change mask %#x.\n", g_nmlistview.uChanged); + + /* LVM_SETITEM changes state */ + memset(&g_nmlistview, 0xcc, sizeof(g_nmlistview)); + memset(&item, 0, sizeof(item)); + item.stateMask = LVIS_SELECTED; + item.state = LVIS_SELECTED; + item.mask = LVIF_STATE; + res = SendMessageA(hwnd, LVM_SETITEMA, 0, (LPARAM)&item); + ok(res, "Failed to set item.\n"); + + ok(g_nmlistview.iItem == item.iItem, "Unexpected item %d.\n", g_nmlistview.iItem); + ok(g_nmlistview.iSubItem == item.iSubItem, "Unexpected subitem %d.\n", g_nmlistview.iSubItem); + ok(g_nmlistview.lParam == item.lParam, "Unexpected lParam.\n"); + ok(g_nmlistview.uNewState == LVIS_SELECTED, "Unexpected new state %#x.\n", g_nmlistview.uNewState); + ok(g_nmlistview.uOldState == 0, "Unexpected old state %#x.\n", g_nmlistview.uOldState); + ok(g_nmlistview.uChanged == LVIF_STATE, "Unexpected change mask %#x.\n", g_nmlistview.uChanged); + + /* LVM_SETITEM no state changes */ + memset(&g_nmlistview, 0xcc, sizeof(g_nmlistview)); + memset(&item, 0, sizeof(item)); + item.lParam = 11; + item.mask = LVIF_PARAM; + res = SendMessageA(hwnd, LVM_SETITEMA, 0, (LPARAM)&item); + ok(res, "Failed to set item.\n"); + + ok(g_nmlistview.iItem == item.iItem, "Unexpected item %d.\n", g_nmlistview.iItem); + ok(g_nmlistview.iSubItem == item.iSubItem, "Unexpected subitem %d.\n", g_nmlistview.iSubItem); + ok(g_nmlistview.lParam == item.lParam, "Unexpected lParam.\n"); + ok(g_nmlistview.uNewState == 0, "Unexpected new state %#x.\n", g_nmlistview.uNewState); + ok(g_nmlistview.uOldState == 0, "Unexpected old state %#x.\n", g_nmlistview.uOldState); + ok(g_nmlistview.uChanged == LVIF_PARAM, "Unexpected change mask %#x.\n", g_nmlistview.uChanged); + + DestroyWindow(hwnd); + } +} + START_TEST(listview) { ULONG_PTR ctx_cookie; @@ -6538,6 +6618,7 @@ START_TEST(listview) test_LVSCW_AUTOSIZE(); test_LVN_ENDLABELEDIT(); test_LVM_GETCOUNTPERPAGE(); + test_item_state_change(); if (!load_v6_module(&ctx_cookie, &hCtx)) { @@ -6582,6 +6663,7 @@ START_TEST(listview) test_LVSCW_AUTOSIZE(); test_LVN_ENDLABELEDIT(); test_LVM_GETCOUNTPERPAGE(); + test_item_state_change(); unload_v6_module(ctx_cookie, hCtx); diff --git a/modules/rostests/winetests/comctl32/pager.c b/modules/rostests/winetests/comctl32/pager.c index f48dc039543..4b5dfe6d96b 100644 --- a/modules/rostests/winetests/comctl32/pager.c +++ b/modules/rostests/winetests/comctl32/pager.c @@ -564,9 +564,11 @@ static void test_wm_notifyformat(void) static const INT formats[] = {NFR_UNICODE, NFR_ANSI}; HWND parent, pager, child; LRESULT ret; + BOOL bret; INT i; - ok(register_notifyformat_class(), "Register test class failed, error 0x%08x\n", GetLastError()); + bret = register_notifyformat_class(); + ok(bret, "Register test class failed, error 0x%08x\n", GetLastError()); for (i = 0; i < ARRAY_SIZE(formats); i++) { @@ -1280,9 +1282,11 @@ static void test_wm_notify(void) {&nmtv, sizeof(nmtv), &nmtv.itemOld.mask, TVIF_TEXT, &nmtv.itemOld.pszText, &nmtv.itemOld.cchTextMax, TVN_SELCHANGEDW, TVN_SELCHANGEDA, CONVERT_SEND, TVITEM_OLD_HANDLER} }; + BOOL bret; INT i; - ok(register_test_notify_class(), "Register test class failed, error 0x%08x\n", GetLastError()); + bret = register_test_notify_class(); + ok(bret, "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"); diff --git a/modules/rostests/winetests/comctl32/progress.c b/modules/rostests/winetests/comctl32/progress.c index 497cb47d3c3..91ea6eec2a9 100644 --- a/modules/rostests/winetests/comctl32/progress.c +++ b/modules/rostests/winetests/comctl32/progress.c @@ -254,6 +254,13 @@ static void test_PBM_STEPIT(void) { 3, 15, 5 }, { 3, 15, -5 }, { 3, 15, 50 }, + { -15, 15, 5 }, + { -3, -2, -5 }, + { 0, 0, 1 }, + { 5, 5, 1 }, + { 0, 0, -1 }, + { 5, 5, -1 }, + { 10, 5, 2 }, }; HWND progress; int i, j; @@ -261,6 +268,7 @@ static void test_PBM_STEPIT(void) for (i = 0; i < ARRAY_SIZE(stepit_tests); i++) { struct stepit_test *test = &stepit_tests[i]; + PBRANGE range; LRESULT ret; progress = create_progress(0); @@ -268,6 +276,9 @@ static void test_PBM_STEPIT(void) ret = SendMessageA(progress, PBM_SETRANGE32, test->min, test->max); ok(ret != 0, "Unexpected return value.\n"); + SendMessageA(progress, PBM_GETRANGE, 0, (LPARAM)&range); + ok(range.iLow == test->min && range.iHigh == test->max, "Unexpected range.\n"); + SendMessageA(progress, PBM_SETPOS, test->min, 0); SendMessageA(progress, PBM_SETSTEP, test->step, 0); @@ -277,15 +288,20 @@ static void test_PBM_STEPIT(void) int current; pos += test->step; - if (pos > test->max) - pos = (pos - test->min) % (test->max - test->min) + test->min; - if (pos < test->min) - pos = (pos - test->min) % (test->max - test->min) + test->max; + if (test->min != test->max) + { + if (pos > test->max) + pos = (pos - test->min) % (test->max - test->min) + test->min; + if (pos < test->min) + pos = (pos - test->min) % (test->max - test->min) + test->max; + } + else + pos = test->min; SendMessageA(progress, PBM_STEPIT, 0, 0); current = SendMessageA(progress, PBM_GETPOS, 0, 0); - ok(current == pos, "Unexpected position %d, expected %d.\n", current, pos); + ok(current == pos, "%u: unexpected position %d, expected %d.\n", i, current, pos); } DestroyWindow(progress); diff --git a/modules/rostests/winetests/comctl32/subclass.c b/modules/rostests/winetests/comctl32/subclass.c index 41ba06560ac..fc778d67115 100644 --- a/modules/rostests/winetests/comctl32/subclass.c +++ b/modules/rostests/winetests/comctl32/subclass.c @@ -273,6 +273,9 @@ static void test_subclass(void) ret = pSetWindowSubclass(hwnd, NULL, 1, 0); ok(ret == FALSE, "Expected FALSE\n"); + pRemoveWindowSubclass(hwnd, wnd_proc_sub, 2); + pRemoveWindowSubclass(hwnd, wnd_proc_sub, 5); + DestroyWindow(hwnd); } diff --git a/modules/rostests/winetests/comctl32/trackbar.c b/modules/rostests/winetests/comctl32/trackbar.c index 7f17e9e4b68..eb5464483d0 100644 --- a/modules/rostests/winetests/comctl32/trackbar.c +++ b/modules/rostests/winetests/comctl32/trackbar.c @@ -931,7 +931,7 @@ static void test_selection(void) static void test_thumb_length(void) { HWND hWndTrackbar; - int r; + int r, r2; hWndTrackbar = create_trackbar(defaultstyle, hWndParent); ok(hWndTrackbar != NULL, "Expected non NULL value\n"); @@ -963,6 +963,22 @@ static void test_thumb_length(void) ok_sequence(sequences, PARENT_SEQ_INDEX, parent_thumb_length_test_seq, "parent thumb length test sequence", TRUE); DestroyWindow(hWndTrackbar); + + /* Fixed thumb length does not depend on window size. */ + hWndTrackbar = CreateWindowA(TRACKBAR_CLASSA, "Trackbar Control", WS_VISIBLE | TBS_ENABLESELRANGE + | TBS_FIXEDLENGTH, 0, 0, 0, 0, hWndParent, NULL, GetModuleHandleA(NULL), NULL); + + r = SendMessageA(hWndTrackbar, TBM_GETTHUMBLENGTH, 0, 0); + + DestroyWindow(hWndTrackbar); + + hWndTrackbar = CreateWindowA(TRACKBAR_CLASSA, "Trackbar Control", WS_VISIBLE | TBS_ENABLESELRANGE + | TBS_FIXEDLENGTH, 0, 0, 200, 200, hWndParent, NULL, GetModuleHandleA(NULL), NULL); + + r2 = SendMessageA(hWndTrackbar, TBM_GETTHUMBLENGTH, 0, 0); + ok(r2 == r, "Unexpected thumb length %d.\n", r); + + DestroyWindow(hWndTrackbar); } static void test_tic_settings(void) diff --git a/modules/rostests/winetests/comctl32/treeview.c b/modules/rostests/winetests/comctl32/treeview.c index 5fe4a5f646d..ff76a83326d 100644 --- a/modules/rostests/winetests/comctl32/treeview.c +++ b/modules/rostests/winetests/comctl32/treeview.c @@ -1364,7 +1364,7 @@ static LRESULT CALLBACK parent_wnd_proc(HWND hWnd, UINT message, WPARAM wParam, visibleItem = (HTREEITEM)SendMessageA(pHdr->hwndFrom, TVM_GETNEXTITEM, TVGN_NEXTVISIBLE, (LPARAM)visibleItem); *(HTREEITEM*)&rect = visibleItem; - ok(visibleItem != NULL, "There must be a visible item after the first visisble item.\n"); + ok(visibleItem != NULL, "There must be a visible item after the first one.\n"); ok(SendMessageA(pHdr->hwndFrom, TVM_GETITEMRECT, TRUE, (LPARAM)&rect), "Failed to get rect for second visible item.\n"); } diff --git a/modules/rostests/winetests/comctl32/updown.c b/modules/rostests/winetests/comctl32/updown.c index 1f4e38f0ebd..e7ceeffd4cd 100644 --- a/modules/rostests/winetests/comctl32/updown.c +++ b/modules/rostests/winetests/comctl32/updown.c @@ -28,7 +28,6 @@ * - check UDM_SETBUDDY message * - check UDM_GETBUDDY message * - up-down control and buddy control must have the same parent - * - up-down control notifies its parent window when its position changes with UDN_DELTAPOS + WM_VSCROLL or WM_HSCROLL * - check UDS_ALIGN[LEFT,RIGHT]...check that width of buddy window is decreased * - check that UDS_SETBUDDYINT sets the caption of the buddy window when it is changed * - check that the thousands operator is set for large numbers @@ -165,6 +164,32 @@ static const struct message test_updown_pos_nochange_seq[] = { { 0 } }; +static const struct message test_updown_pos_notifications_seq[] = { + { WM_CTLCOLOREDIT, sent|optional }, + { WM_COMMAND, sent|wparam, MAKELONG(0, EN_SETFOCUS) }, + { WM_NOTIFY, sent|id, 0, 0, UDN_DELTAPOS }, + { WM_COMMAND, sent|wparam, MAKELONG(0, EN_UPDATE) }, + { WM_COMMAND, sent|wparam, MAKELONG(0, EN_CHANGE) }, + { WM_VSCROLL, sent|wparam, MAKELONG(SB_THUMBPOSITION, 51) }, + { WM_CTLCOLOREDIT, sent|optional }, + { WM_VSCROLL, sent|wparam, MAKELONG(SB_ENDSCROLL, 51) }, + /* no WM_NOTIFY(NM_RELEASEDCAPTURE) message */ + { 0 } +}; + +static const struct message test_updown_pos_notifications_horz_seq[] = { + { WM_CTLCOLOREDIT, sent|optional }, + { WM_COMMAND, sent|wparam, MAKELONG(0, EN_SETFOCUS) }, + { WM_NOTIFY, sent|id, 0, 0, UDN_DELTAPOS }, + { WM_COMMAND, sent|wparam, MAKELONG(0, EN_UPDATE) }, + { WM_COMMAND, sent|wparam, MAKELONG(0, EN_CHANGE) }, + { WM_HSCROLL, sent|wparam, MAKELONG(SB_THUMBPOSITION, 51) }, + { WM_CTLCOLOREDIT, sent|optional }, + { WM_HSCROLL, sent|wparam, MAKELONG(SB_ENDSCROLL, 51) }, + /* no WM_NOTIFY(NM_RELEASEDCAPTURE) message */ + { 0 } +}; + static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static LONG defwndproc_counter = 0; @@ -186,6 +211,8 @@ static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LP if (defwndproc_counter) msg.flags |= defwinproc; msg.wParam = wParam; msg.lParam = lParam; + if (message == WM_NOTIFY && lParam) + msg.id = ((NMHDR*)lParam)->code; add_message(sequences, PARENT_SEQ_INDEX, &msg); } @@ -900,6 +927,52 @@ static void test_CreateUpDownControl(void) DestroyWindow(updown); } +static void test_updown_pos_notifications(void) +{ + HWND updown; + RECT rect; + UINT x, y; + int result; + + /* test updown control notifications without UDS_HORZ style */ + updown = create_updown_control(UDS_ALIGNRIGHT | UDS_SETBUDDYINT, g_edit); + SetFocus(updown); + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + /* click on the up-arrow button */ + GetClientRect(updown, &rect); + x = rect.left + (rect.right - rect.left) / 2; + y = rect.top + (rect.bottom - rect.top) / 4; + result = SendMessageA(updown, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y)); + expect(result, 0); + result = SendMessageA(updown, WM_LBUTTONUP, 0, MAKELPARAM(x, y)); + expect(result, 0); + + ok_sequence(sequences, PARENT_SEQ_INDEX, test_updown_pos_notifications_seq, + "test updown to parent notify (vertical)", FALSE); + + DestroyWindow(updown); + + /* test updown control notifications with UDS_HORZ style */ + updown = create_updown_control(UDS_ALIGNRIGHT | UDS_SETBUDDYINT | UDS_HORZ, g_edit); + SetFocus(updown); + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + /* click on the right-arrow button */ + GetClientRect(updown, &rect); + x = rect.left + (rect.right - rect.left) * 3 / 4; + y = rect.top + (rect.bottom - rect.top) / 2; + result = SendMessageA(updown, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y)); + expect(result, 0); + result = SendMessageA(updown, WM_LBUTTONUP, 0, MAKELPARAM(x, y)); + expect(result, 0); + + ok_sequence(sequences, PARENT_SEQ_INDEX, test_updown_pos_notifications_horz_seq, + "test updown to parent notify (horizontal)", FALSE); + + DestroyWindow(updown); +} + static void init_functions(void) { HMODULE hComCtl32 = LoadLibraryA("comctl32.dll"); @@ -931,6 +1004,7 @@ START_TEST(updown) test_updown_unicode(); test_UDS_SETBUDDYINT(); test_CreateUpDownControl(); + test_updown_pos_notifications(); DestroyWindow(g_edit); DestroyWindow(parent_wnd);