[COMCTL32_WINETEST] Sync with Wine Staging 4.18. CORE-16441
authorAmine Khaldi <amine.khaldi@reactos.org>
Sat, 26 Oct 2019 21:52:00 +0000 (22:52 +0100)
committerAmine Khaldi <amine.khaldi@reactos.org>
Sat, 26 Oct 2019 21:52:00 +0000 (22:52 +0100)
modules/rostests/winetests/comctl32/button.c
modules/rostests/winetests/comctl32/edit.c
modules/rostests/winetests/comctl32/imagelist.c
modules/rostests/winetests/comctl32/listbox.c
modules/rostests/winetests/comctl32/listview.c
modules/rostests/winetests/comctl32/pager.c
modules/rostests/winetests/comctl32/progress.c
modules/rostests/winetests/comctl32/subclass.c
modules/rostests/winetests/comctl32/trackbar.c
modules/rostests/winetests/comctl32/treeview.c
modules/rostests/winetests/comctl32/updown.c

index edfa447..ff008ba 100644 (file)
@@ -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);
+    himl = pImageList_Create(image_width, height, ILC_COLOR, 1, 1);
+    pImageList_Add(himl, hbmp, 0);
 
-    /* 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);
+#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++)
+    {
+        /* 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);
+        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 + 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++)
+        /* 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++)
         {
-            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);
+            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");
-            SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp);
+            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 (!(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));
+            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 >= 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);
+                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);
         }
 
-    /* Image list alignments */
-    himl = pImageList_Create(image_width, height, ILC_COLOR, 1, 1);
-    pImageList_Add(himl, hbmp, 0);
-    biml.himl = himl;
-    for (i = 0; i < ARRAY_SIZE(imagelist_aligns); i++)
-    {
-        biml.uAlign = imagelist_aligns[i];
-        hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFPUSHBUTTON | default_style, 0, 0, client_width,
+        /* 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);
-        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);
+        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);
index bc44d0c..6013caf 100644 (file)
@@ -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);
 }
 
index 8064a2b..3e538c7 100644 (file)
@@ -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);
index e789483..70e2128 100644 (file)
@@ -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();
index ec289fa..45f2b60 100644 (file)
@@ -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);
 
index f48dc03..4b5dfe6 100644 (file)
@@ -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");
index 497cb47..91ea6ee 100644 (file)
@@ -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);
index 41ba065..fc778d6 100644 (file)
@@ -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);
 }
 
index 7f17e9e..eb54644 100644 (file)
@@ -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)
index 5fe4a5f..ff76a83 100644 (file)
@@ -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");
                 }
index 1f4e38f..e7ceeff 100644 (file)
@@ -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);