[USER32_WINETEST] Sync with Wine Staging 4.18 except win.c (PR #1980). CORE-16441
[reactos.git] / modules / rostests / winetests / user32 / listbox.c
index 88d3adb..8d81151 100644 (file)
@@ -51,7 +51,7 @@ static int strcmp_aw(LPCWSTR strw, const char *stra)
     WCHAR buf[1024];
 
     if (!stra) return 1;
-    MultiByteToWideChar(CP_ACP, 0, stra, -1, buf, sizeof(buf)/sizeof(WCHAR));
+    MultiByteToWideChar(CP_ACP, 0, stra, -1, buf, ARRAY_SIZE(buf));
     return lstrcmpW(strw, buf);
 }
 
@@ -90,7 +90,6 @@ struct listbox_stat {
 };
 
 struct listbox_test {
-  struct listbox_prop prop;
   struct listbox_stat  init,  init_todo;
   struct listbox_stat click, click_todo;
   struct listbox_stat  step,  step_todo;
@@ -130,8 +129,7 @@ keypress (HWND handle, WPARAM keycode, BYTE scancode, BOOL extended)
 
 #define listbox_field_ok(t, s, f, got) \
   ok (t.s.f==got.f, "style %#x, step " #s ", field " #f \
-      ": expected %d, got %d\n", (unsigned int)t.prop.add_style, \
-      t.s.f, got.f)
+      ": expected %d, got %d\n", style, t.s.f, got.f)
 
 #define listbox_todo_field_ok(t, s, f, got) \
   todo_wine_if (t.s##_todo.f) { listbox_field_ok(t, s, f, got); }
@@ -143,13 +141,15 @@ keypress (HWND handle, WPARAM keycode, BYTE scancode, BOOL extended)
   listbox_todo_field_ok(t, s, selcount, got)
 
 static void
-check (const struct listbox_test test)
+check (DWORD style, const struct listbox_test test)
 {
   struct listbox_stat answer;
-  HWND hLB=create_listbox (test.prop.add_style, 0);
   RECT second_item;
   int i;
   int res;
+  HWND hLB;
+
+  hLB = create_listbox (style, 0);
 
   listbox_query (hLB, &answer);
   listbox_ok (test, init, answer);
@@ -166,13 +166,13 @@ check (const struct listbox_test test)
   listbox_ok (test, step, answer);
 
   DestroyWindow (hLB);
-  hLB=create_listbox (test.prop.add_style, 0);
+  hLB = create_listbox(style, 0);
 
   SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 2));
   listbox_query (hLB, &answer);
   listbox_ok (test, sel, answer);
 
-  for (i=0;i<4;i++) {
+  for (i = 0; i < 4 && !(style & LBS_NODATA); i++) {
        DWORD size = SendMessageA(hLB, LB_GETTEXTLEN, i, 0);
        CHAR *txt;
        WCHAR *txtw;
@@ -184,13 +184,9 @@ check (const struct listbox_test test)
 
        txtw = HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, 2*size+2);
        resW=SendMessageW(hLB, LB_GETTEXT, i, (LPARAM)txtw);
-       if (resA != resW) {
-            trace("SendMessageW(LB_GETTEXT) not supported on this platform (resA=%d resW=%d), skipping...\n",
-                resA, resW);
-       } else {
-           WideCharToMultiByte( CP_ACP, 0, txtw, -1, txt, size, NULL, NULL );
-            ok(!strcmp (txt, strings[i]), "returned string for item %d does not match %s vs %s\n", i, txt, strings[i]);
-       }
+       ok(resA == resW, "Unexpected text length.\n");
+       WideCharToMultiByte( CP_ACP, 0, txtw, -1, txt, size, NULL, NULL );
+        ok(!strcmp (txt, strings[i]), "returned string for item %d does not match %s vs %s\n", i, txt, strings[i]);
 
        HeapFree (GetProcessHeap(), 0, txtw);
        HeapFree (GetProcessHeap(), 0, txt);
@@ -342,35 +338,73 @@ static HWND create_parent( void )
 
 static void test_ownerdraw(void)
 {
+    static const DWORD styles[] =
+    {
+        0,
+        LBS_NODATA
+    };
     HWND parent, hLB;
     INT ret;
     RECT rc;
+    UINT i;
 
     parent = create_parent();
     assert(parent);
 
-    hLB = create_listbox(LBS_OWNERDRAWFIXED | WS_CHILD | WS_VISIBLE, parent);
-    assert(hLB);
+    for (i = 0; i < ARRAY_SIZE(styles); i++)
+    {
+        hLB = create_listbox(LBS_OWNERDRAWFIXED | WS_CHILD | WS_VISIBLE | styles[i], parent);
+        assert(hLB);
 
-    SetForegroundWindow(hLB);
-    UpdateWindow(hLB);
+        SetForegroundWindow(hLB);
+        UpdateWindow(hLB);
 
-    /* make height short enough */
-    SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc);
-    SetWindowPos(hLB, 0, 0, 0, 100, rc.bottom - rc.top + 1,
-                 SWP_NOZORDER | SWP_NOMOVE);
+        /* make height short enough */
+        SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc);
+        SetWindowPos(hLB, 0, 0, 0, 100, rc.bottom - rc.top + 1,
+                     SWP_NOZORDER | SWP_NOMOVE);
 
-    /* make 0 item invisible */
-    SendMessageA(hLB, LB_SETTOPINDEX, 1, 0);
-    ret = SendMessageA(hLB, LB_GETTOPINDEX, 0, 0);
-    ok(ret == 1, "wrong top index %d\n", ret);
+        /* make 0 item invisible */
+        SendMessageA(hLB, LB_SETTOPINDEX, 1, 0);
+        ret = SendMessageA(hLB, LB_GETTOPINDEX, 0, 0);
+        ok(ret == 1, "wrong top index %d\n", ret);
 
-    SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc);
-    trace("item 0 rect %s\n", wine_dbgstr_rect(&rc));
-    ok(!IsRectEmpty(&rc), "empty item rect\n");
-    ok(rc.top < 0, "rc.top is not negative (%d)\n", rc.top);
+        SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc);
+        trace("item 0 rect %s\n", wine_dbgstr_rect(&rc));
+        ok(!IsRectEmpty(&rc), "empty item rect\n");
+        ok(rc.top < 0, "rc.top is not negative (%d)\n", rc.top);
 
-    DestroyWindow(hLB);
+        DestroyWindow(hLB);
+
+        /* Both FIXED and VARIABLE, FIXED should override VARIABLE. */
+        hLB = CreateWindowA("listbox", "TestList", LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE | styles[i],
+            0, 0, 100, 100, NULL, NULL, NULL, 0);
+        ok(hLB != NULL, "last error 0x%08x\n", GetLastError());
+
+        ok(GetWindowLongA(hLB, GWL_STYLE) & LBS_OWNERDRAWVARIABLE, "Unexpected window style.\n");
+
+        ret = SendMessageA(hLB, LB_INSERTSTRING, -1, 0);
+        ok(ret == 0, "Unexpected return value %d.\n", ret);
+        ret = SendMessageA(hLB, LB_INSERTSTRING, -1, 0);
+        ok(ret == 1, "Unexpected return value %d.\n", ret);
+
+        ret = SendMessageA(hLB, LB_SETITEMHEIGHT, 0, 13);
+        ok(ret == LB_OKAY, "Failed to set item height, %d.\n", ret);
+
+        ret = SendMessageA(hLB, LB_GETITEMHEIGHT, 0, 0);
+        ok(ret == 13, "Unexpected item height %d.\n", ret);
+
+        ret = SendMessageA(hLB, LB_SETITEMHEIGHT, 1, 42);
+        ok(ret == LB_OKAY, "Failed to set item height, %d.\n", ret);
+
+        ret = SendMessageA(hLB, LB_GETITEMHEIGHT, 0, 0);
+        ok(ret == 42, "Unexpected item height %d.\n", ret);
+
+        ret = SendMessageA(hLB, LB_GETITEMHEIGHT, 1, 0);
+        ok(ret == 42, "Unexpected item height %d.\n", ret);
+
+        DestroyWindow (hLB);
+    }
     DestroyWindow(parent);
 }
 
@@ -475,19 +509,123 @@ static void test_LB_SETCURSEL(void)
 
     SendMessageA(hLB, LB_SETITEMHEIGHT, 0, 32);
 
+    ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
     ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0);
     ok(ret == 2, "LB_SETCURSEL returned %d instead of 2\n", ret);
     ret = GetScrollPos(hLB, SB_VERT);
     ok(ret == 0, "expected vscroll 0, got %d\n", ret);
 
+    ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
     ret = SendMessageA(hLB, LB_SETCURSEL, 3, 0);
     ok(ret == 3, "LB_SETCURSEL returned %d instead of 3\n", ret);
     ret = GetScrollPos(hLB, SB_VERT);
     ok(ret == 1, "expected vscroll 1, got %d\n", ret);
 
+    ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
+    DestroyWindow(hLB);
+
+    hLB = create_listbox(0, 0);
+    ok(hLB != NULL, "Failed to create ListBox window.\n");
+
+    ret = SendMessageA(hLB, LB_SETCURSEL, 1, 0);
+    ok(ret == 1, "Unexpected return value %d.\n", ret);
+
+    ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
+    DestroyWindow(hLB);
+
+    /* LBS_EXTENDEDSEL */
+    hLB = create_listbox(LBS_EXTENDEDSEL, 0);
+    ok(hLB != NULL, "Failed to create ListBox window.\n");
+
+    ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
+    ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0);
+    ok(ret == -1, "Unexpected return value %d.\n", ret);
+
+    ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
+    DestroyWindow(hLB);
+
+    /* LBS_MULTIPLESEL */
+    hLB = create_listbox(LBS_MULTIPLESEL, 0);
+    ok(hLB != NULL, "Failed to create ListBox window.\n");
+
+    ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
+    ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0);
+    ok(ret == -1, "Unexpected return value %d.\n", ret);
+
+    ret = SendMessageA(hLB, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
     DestroyWindow(hLB);
 }
 
+static void test_LB_SETSEL(void)
+{
+    HWND list;
+    int ret;
+
+    /* LBS_EXTENDEDSEL */
+    list = create_listbox(LBS_EXTENDEDSEL, 0);
+    ok(list != NULL, "Failed to create ListBox window.\n");
+
+    ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
+    ret = SendMessageA(list, LB_SETSEL, TRUE, 0);
+    ok(ret == 0, "Unexpected return value %d.\n", ret);
+    ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == 0, "Unexpected anchor index %d.\n", ret);
+
+    ret = SendMessageA(list, LB_SETSEL, TRUE, 1);
+    ok(ret == 0, "Unexpected return value %d.\n", ret);
+    ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == 1, "Unexpected anchor index %d.\n", ret);
+
+    ret = SendMessageA(list, LB_SETSEL, FALSE, 1);
+    ok(ret == 0, "Unexpected return value %d.\n", ret);
+    ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == 1, "Unexpected anchor index %d.\n", ret);
+
+    DestroyWindow(list);
+
+    /* LBS_MULTIPLESEL */
+    list = create_listbox(LBS_MULTIPLESEL, 0);
+    ok(list != NULL, "Failed to create ListBox window.\n");
+
+    ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == -1, "Unexpected anchor index %d.\n", ret);
+
+    ret = SendMessageA(list, LB_SETSEL, TRUE, 0);
+    ok(ret == 0, "Unexpected return value %d.\n", ret);
+    ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == 0, "Unexpected anchor index %d.\n", ret);
+
+    ret = SendMessageA(list, LB_SETSEL, TRUE, 1);
+    ok(ret == 0, "Unexpected return value %d.\n", ret);
+    ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == 1, "Unexpected anchor index %d.\n", ret);
+
+    ret = SendMessageA(list, LB_SETSEL, FALSE, 1);
+    ok(ret == 0, "Unexpected return value %d.\n", ret);
+    ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
+    ok(ret == 1, "Unexpected anchor index %d.\n", ret);
+
+    DestroyWindow(list);
+}
+
 static void test_listbox_height(void)
 {
     HWND hList;
@@ -527,6 +665,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("listbox", "TestList", styles[i] | selstyles[j] | WS_CHILD | WS_VISIBLE,
+                                    0, 0, 100, 100, parent, (HMENU)1, 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 (
@@ -637,6 +904,7 @@ static void test_listbox_item_data(void)
 
 static void test_listbox_LB_DIR(void)
 {
+    char path[MAX_PATH], curdir[MAX_PATH];
     HWND hList;
     int res, itemCount;
     int itemCount_justFiles;
@@ -649,6 +917,16 @@ static void test_listbox_LB_DIR(void)
     char driveletter;
     const char *wildcard = "*";
     HANDLE file;
+    BOOL ret;
+
+    GetCurrentDirectoryA(ARRAY_SIZE(curdir), curdir);
+
+    GetTempPathA(ARRAY_SIZE(path), path);
+    ret = SetCurrentDirectoryA(path);
+    ok(ret, "Failed to set current directory.\n");
+
+    ret = CreateDirectoryA("lb_dir_test", NULL);
+    ok(ret, "Failed to create test directory.\n");
 
     file = CreateFileA( "wtest1.tmp.c", GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
     ok(file != INVALID_HANDLE_VALUE, "Error creating the test file: %d\n", GetLastError());
@@ -980,11 +1258,11 @@ static void test_listbox_LB_DIR(void)
         itemCount, itemCount_allDirs);
     ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, *) returned incorrect index!\n");
 
-    if (itemCount && GetCurrentDirectoryA( MAX_PATH, pathBuffer ) > 3)  /* there's no [..] in drive root */
+    if (itemCount)
     {
         memset(pathBuffer, 0, MAX_PATH);
         SendMessageA(hList, LB_GETTEXT, 0, (LPARAM)pathBuffer);
-        ok( !strcmp(pathBuffer, "[..]"), "First element is not [..]\n");
+        ok( !strcmp(pathBuffer, "[..]"), "First element is %s, not [..]\n", pathBuffer);
     }
 
     /* This tests behavior when no files match the wildcard */
@@ -1068,6 +1346,9 @@ static void test_listbox_LB_DIR(void)
     DestroyWindow(hList);
 
     DeleteFileA( "wtest1.tmp.c" );
+    RemoveDirectoryA("lb_dir_test");
+
+    SetCurrentDirectoryA(curdir);
 }
 
 static HWND g_listBox;
@@ -1555,7 +1836,7 @@ static void test_listbox_dlgdir(void)
     strcpy(pathBuffer, "C:\\");
     res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, 0, DDL_DIRECTORY | DDL_EXCLUSIVE);
     ok(res || broken(!res) /* NT4/W2K */, "DlgDirList failed to list C:\\ folders\n");
-    todo_wine ok(!strcmp(pathBuffer, "*") || broken(!res) /* NT4/W2K */,
+    ok(!strcmp(pathBuffer, "*") || broken(!res) /* NT4/W2K */,
        "DlgDirList set the invalid path spec '%s', expected '*'\n", pathBuffer);
 
     strcpy(pathBuffer, "C:\\*");
@@ -1568,8 +1849,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);
@@ -1577,7 +1858,13 @@ static void test_listbox_dlgdir(void)
 
 static void test_set_count( void )
 {
+    static const DWORD styles[] =
+    {
+        LBS_OWNERDRAWFIXED,
+        LBS_HASSTRINGS,
+    };
     HWND parent, listbox;
+    unsigned int i;
     LONG ret;
     RECT r;
 
@@ -1606,7 +1893,25 @@ 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)
+    {
+        listbox = create_listbox( styles[i] | WS_CHILD | WS_VISIBLE, parent );
+
+        SetLastError( 0xdeadbeef );
+        ret = SendMessageA( listbox, LB_SETCOUNT, 100, 0 );
+        ok( ret == LB_ERR, "expected %d, got %d\n", LB_ERR, ret );
+        ok( GetLastError() == ERROR_SETCOUNT_ON_BAD_LB, "Unexpected error %d.\n", GetLastError() );
+
+        DestroyWindow( listbox );
+    }
+
     DestroyWindow( parent );
 }
 
@@ -1653,6 +1958,69 @@ todo_wine
     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("listbox", "TestList", styles[i] | WS_CHILD,
+                                0, 0, 100, 100, parent, (HMENU)1, 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;
@@ -1876,88 +2244,216 @@ static void test_WM_MEASUREITEM(void)
     DestroyWindow(parent);
 }
 
+static void test_LBS_NODATA(void)
+{
+    static const DWORD invalid_styles[] =
+    {
+        0,
+        LBS_OWNERDRAWVARIABLE,
+        LBS_SORT,
+        LBS_HASSTRINGS,
+        LBS_OWNERDRAWFIXED | LBS_SORT,
+        LBS_OWNERDRAWFIXED | LBS_HASSTRINGS,
+    };
+    static const UINT invalid_idx[] = { -2, 2 };
+    static const UINT valid_idx[] = { 0, 1 };
+    static const ULONG_PTR zero_data;
+    HWND listbox, parent;
+    unsigned int i;
+    ULONG_PTR data;
+    INT ret;
+
+    listbox = CreateWindowA("listbox", "TestList", LBS_NODATA | LBS_OWNERDRAWFIXED | WS_VISIBLE,
+        0, 0, 100, 100, NULL, NULL, NULL, 0);
+    ok(listbox != NULL, "Failed to create ListBox window.\n");
+
+    ret = SendMessageA(listbox, LB_INSERTSTRING, -1, 0);
+    ok(ret == 0, "Unexpected return value %d.\n", ret);
+    ret = SendMessageA(listbox, LB_INSERTSTRING, -1, 0);
+    ok(ret == 1, "Unexpected return value %d.\n", ret);
+    ret = SendMessageA(listbox, LB_GETCOUNT, 0, 0);
+    ok(ret == 2, "Unexpected return value %d.\n", ret);
+
+    /* Invalid indices. */
+    for (i = 0; i < ARRAY_SIZE(invalid_idx); ++i)
+    {
+        ret = SendMessageA(listbox, LB_SETITEMDATA, invalid_idx[i], 42);
+        ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+        ret = SendMessageA(listbox, LB_GETTEXTLEN, invalid_idx[i], 0);
+        ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+        if (ret == LB_ERR)
+        {
+            ret = SendMessageA(listbox, LB_GETTEXT, invalid_idx[i], (LPARAM)&data);
+            ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+        }
+        ret = SendMessageA(listbox, LB_GETITEMDATA, invalid_idx[i], 0);
+        ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+    }
+
+    /* Valid indices. */
+    for (i = 0; i < ARRAY_SIZE(valid_idx); ++i)
+    {
+        ret = SendMessageA(listbox, LB_SETITEMDATA, valid_idx[i], 42);
+        ok(ret == TRUE, "Unexpected return value %d.\n", ret);
+        ret = SendMessageA(listbox, LB_GETTEXTLEN, valid_idx[i], 0);
+        ok(ret == sizeof(data), "Unexpected return value %d.\n", ret);
+
+        memset(&data, 0xee, sizeof(data));
+        ret = SendMessageA(listbox, LB_GETTEXT, valid_idx[i], (LPARAM)&data);
+        ok(ret == sizeof(data), "Unexpected return value %d.\n", ret);
+        ok(!memcmp(&data, &zero_data, sizeof(data)), "Unexpected item data.\n");
+
+        ret = SendMessageA(listbox, LB_GETITEMDATA, valid_idx[i], 0);
+        ok(ret == 0, "Unexpected return value %d.\n", ret);
+    }
+
+    /* More messages that don't work with LBS_NODATA. */
+    SetLastError(0xdeadbeef);
+    ret = SendMessageA(listbox, LB_FINDSTRING, 1, 0);
+    ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError should return 0x57, got 0x%X\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = SendMessageA(listbox, LB_FINDSTRING, 1, 42);
+    ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError should return 0x57, got 0x%X\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = SendMessageA(listbox, LB_FINDSTRINGEXACT, 1, 0);
+    ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError should return 0x57, got 0x%X\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = SendMessageA(listbox, LB_FINDSTRINGEXACT, 1, 42);
+    ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError should return 0x57, got 0x%X\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = SendMessageA(listbox, LB_SELECTSTRING, 1, 0);
+    ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError should return 0x57, got 0x%X\n", GetLastError());
+    SetLastError(0xdeadbeef);
+    ret = SendMessageA(listbox, LB_SELECTSTRING, 1, 42);
+    ok(ret == LB_ERR, "Unexpected return value %d.\n", ret);
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError should return 0x57, got 0x%X\n", GetLastError());
+
+    DestroyWindow(listbox);
+
+    /* Invalid window style combinations. */
+    parent = create_parent();
+    ok(parent != NULL, "Failed to create parent window.\n");
+
+    for (i = 0; i < ARRAY_SIZE(invalid_styles); ++i)
+    {
+        DWORD style;
+
+        listbox = CreateWindowA("listbox", "TestList", LBS_NODATA | WS_CHILD | invalid_styles[i],
+                0, 0, 100, 100, parent, (HMENU)1, NULL, 0);
+        ok(listbox != NULL, "Failed to create a listbox.\n");
+
+        style = GetWindowLongA(listbox, GWL_STYLE);
+        ok((style & invalid_styles[i]) == invalid_styles[i], "%u: unexpected window styles %#x.\n", i, style);
+        ret = SendMessageA(listbox, LB_SETCOUNT, 100, 0);
+        ok(ret == LB_ERR, "%u: unexpected return value %d.\n", i, ret);
+        DestroyWindow(listbox);
+    }
+
+    DestroyWindow(parent);
+}
+
 START_TEST(listbox)
 {
   const struct listbox_test SS =
 /*   {add_style} */
-    {{0},
-     {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
+    {{LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
      {     1,      1,      1, LB_ERR}, {0,0,0,0},
      {     2,      2,      2, LB_ERR}, {0,0,0,0},
      {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
 /* {selected, anchor,  caret, selcount}{TODO fields} */
   const struct listbox_test SS_NS =
-    {{LBS_NOSEL},
-     {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
+    {{LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
      {     1,      1,      1, LB_ERR}, {0,0,0,0},
      {     2,      2,      2, LB_ERR}, {0,0,0,0},
      {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
   const struct listbox_test MS =
-    {{LBS_MULTIPLESEL},
-     {     0, LB_ERR,      0,      0}, {0,0,0,0},
+    {{     0, LB_ERR,      0,      0}, {0,0,0,0},
      {     1,      1,      1,      1}, {0,0,0,0},
      {     2,      1,      2,      1}, {0,0,0,0},
      {     0, LB_ERR,      0,      2}, {0,0,0,0}};
   const struct listbox_test MS_NS =
-    {{LBS_MULTIPLESEL | LBS_NOSEL},
-     {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
+    {{LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
      {     1,      1,      1, LB_ERR}, {0,0,0,0},
      {     2,      2,      2, LB_ERR}, {0,0,0,0},
      {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
   const struct listbox_test ES =
-    {{LBS_EXTENDEDSEL},
-     {     0, LB_ERR,      0,      0}, {0,0,0,0},
+    {{     0, LB_ERR,      0,      0}, {0,0,0,0},
      {     1,      1,      1,      1}, {0,0,0,0},
      {     2,      2,      2,      1}, {0,0,0,0},
      {     0, LB_ERR,      0,      2}, {0,0,0,0}};
   const struct listbox_test ES_NS =
-    {{LBS_EXTENDEDSEL | LBS_NOSEL},
-     {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
+    {{LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
      {     1,      1,      1, LB_ERR}, {0,0,0,0},
      {     2,      2,      2, LB_ERR}, {0,0,0,0},
      {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
   const struct listbox_test EMS =
-    {{LBS_EXTENDEDSEL | LBS_MULTIPLESEL},
-     {     0, LB_ERR,      0,      0}, {0,0,0,0},
+    {{     0, LB_ERR,      0,      0}, {0,0,0,0},
      {     1,      1,      1,      1}, {0,0,0,0},
      {     2,      2,      2,      1}, {0,0,0,0},
      {     0, LB_ERR,      0,      2}, {0,0,0,0}};
   const struct listbox_test EMS_NS =
-    {{LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL},
-     {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
+    {{LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0},
      {     1,      1,      1, LB_ERR}, {0,0,0,0},
      {     2,      2,      2, LB_ERR}, {0,0,0,0},
      {LB_ERR, LB_ERR,      0, LB_ERR}, {0,0,0,0}};
 
   trace (" Testing single selection...\n");
-  check (SS);
+  check (0, SS);
+  trace (" ... with NOSEL\n");
+  check (LBS_NOSEL, SS_NS);
+  trace (" ... LBS_NODATA variant ...\n");
+  check (LBS_NODATA | LBS_OWNERDRAWFIXED, SS);
   trace (" ... with NOSEL\n");
-  check (SS_NS);
+  check (LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_NOSEL, SS_NS);
+
   trace (" Testing multiple selection...\n");
-  check (MS);
+  check (LBS_MULTIPLESEL, MS);
+  trace (" ... with NOSEL\n");
+  check (LBS_MULTIPLESEL | LBS_NOSEL, MS_NS);
+  trace (" ... LBS_NODATA variant ...\n");
+  check (LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_MULTIPLESEL, MS);
   trace (" ... with NOSEL\n");
-  check (MS_NS);
+  check (LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_MULTIPLESEL | LBS_NOSEL, MS_NS);
+
   trace (" Testing extended selection...\n");
-  check (ES);
+  check (LBS_EXTENDEDSEL, ES);
+  trace (" ... with NOSEL\n");
+  check (LBS_EXTENDEDSEL | LBS_NOSEL, ES_NS);
+  trace (" ... LBS_NODATA variant ...\n");
+  check (LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL, ES);
   trace (" ... with NOSEL\n");
-  check (ES_NS);
+  check (LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL | LBS_NOSEL, ES_NS);
+
   trace (" Testing extended and multiple selection...\n");
-  check (EMS);
+  check (LBS_EXTENDEDSEL | LBS_MULTIPLESEL, EMS);
+  trace (" ... with NOSEL\n");
+  check (LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL, EMS_NS);
+  trace (" ... LBS_NODATA variant ...\n");
+  check (LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL | LBS_MULTIPLESEL, EMS);
   trace (" ... with NOSEL\n");
-  check (EMS_NS);
+  check (LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL, EMS_NS);
 
   check_item_height();
   test_ownerdraw();
   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();
   test_WM_MEASUREITEM();
+  test_LB_SETSEL();
+  test_LBS_NODATA();
 }