Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/comctl32/tests/button.c | 240 ++++++++++++++++++++++++++++++++--- 1 file changed, 222 insertions(+), 18 deletions(-)
diff --git a/dlls/comctl32/tests/button.c b/dlls/comctl32/tests/button.c index 753aa24..25208b9 100644 --- a/dlls/comctl32/tests/button.c +++ b/dlls/comctl32/tests/button.c @@ -39,8 +39,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];
@@ -152,11 +153,38 @@ 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; struct message msg = { 0 }; + NMCUSTOMDRAW *cd = (NMCUSTOMDRAW*)lParam; LRESULT ret;
if (ignore_message( message )) return 0; @@ -176,6 +204,46 @@ 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; + 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; + 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_PAINT) { PAINTSTRUCT ps; @@ -453,6 +521,50 @@ 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 }, + + /* Some Windows configurations paint twice under certain conditions */ + { WM_NOTIFY, sent|parent|id|custdraw|optional, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE }, + { WM_NOTIFY, sent|parent|id|custdraw|optional, 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 HWND create_button(DWORD style, HWND parent) { HMENU menuid = 0; @@ -471,6 +583,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; @@ -481,55 +600,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; @@ -557,6 +695,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; @@ -564,6 +707,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");
@@ -591,7 +735,10 @@ 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()); @@ -599,18 +746,25 @@ 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, button[i].setfocus, "SetFocus(hwnd) on a button", todo); + check_cd_seq(cd_setfocus_type, "SetFocus(hwnd)");
+ 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", 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); @@ -620,12 +774,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); @@ -635,11 +792,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); @@ -651,7 +810,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) @@ -665,6 +826,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); @@ -674,11 +836,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 || @@ -726,9 +890,49 @@ 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 = 0; + + 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(); DestroyWindow(hwnd); }
+ #undef check_cd_seq + DeleteObject(hfont2); DestroyWindow(parent);