[COMCTL32_WINETEST]
authorChristoph von Wittich <christoph_vw@reactos.org>
Sun, 30 May 2010 15:19:09 +0000 (15:19 +0000)
committerChristoph von Wittich <christoph_vw@reactos.org>
Sun, 30 May 2010 15:19:09 +0000 (15:19 +0000)
sync to wine 1.2 RC2

svn path=/trunk/; revision=47448

29 files changed:
rostests/winetests/comctl32/comboex.c
rostests/winetests/comctl32/comctl32.rbuild
rostests/winetests/comctl32/comctl32_ros.diff [deleted file]
rostests/winetests/comctl32/datetime.c
rostests/winetests/comctl32/dpa.c
rostests/winetests/comctl32/header.c
rostests/winetests/comctl32/imagelist.c
rostests/winetests/comctl32/ipaddress.c
rostests/winetests/comctl32/listview.c
rostests/winetests/comctl32/misc.c
rostests/winetests/comctl32/monthcal.c
rostests/winetests/comctl32/mru.c
rostests/winetests/comctl32/msg.c [deleted file]
rostests/winetests/comctl32/msg.h
rostests/winetests/comctl32/progress.c
rostests/winetests/comctl32/propsheet.c
rostests/winetests/comctl32/rebar.c
rostests/winetests/comctl32/resources.h
rostests/winetests/comctl32/rsrc.rc
rostests/winetests/comctl32/status.c
rostests/winetests/comctl32/subclass.c
rostests/winetests/comctl32/tab.c
rostests/winetests/comctl32/testlist.c
rostests/winetests/comctl32/toolbar.c
rostests/winetests/comctl32/tooltips.c
rostests/winetests/comctl32/trackbar.c
rostests/winetests/comctl32/treeview.c
rostests/winetests/comctl32/updown.c
rostests/winetests/comctl32/v6util.h [new file with mode: 0644]

index 1e74192..f9a42df 100644 (file)
 #include <commctrl.h>
 
 #include "wine/test.h"
+#include "msg.h"
+
+#define EDITBOX_SEQ_INDEX  0
+#define NUM_MSG_SEQUENCES  1
+
+#define EDITBOX_ID         0
+
+#define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
+
+static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
 
 static HWND hComboExParentWnd;
 static HINSTANCE hMainHinst;
 static const char ComboExTestClass[] = "ComboExTestClass";
 
+static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
+
 #define MAX_CHARS 100
 static char *textBuffer = NULL;
 
@@ -42,7 +54,7 @@ static LONG addItem(HWND cbex, int idx, LPTSTR text) {
     cbexItem.iItem = idx;
     cbexItem.pszText    = text;
     cbexItem.cchTextMax = 0;
-    return (LONG)SendMessage(cbex, CBEM_INSERTITEM, 0,(LPARAM)&cbexItem);
+    return SendMessage(cbex, CBEM_INSERTITEM, 0, (LPARAM)&cbexItem);
 }
 
 static LONG setItem(HWND cbex, int idx, LPTSTR text) {
@@ -52,11 +64,11 @@ static LONG setItem(HWND cbex, int idx, LPTSTR text) {
     cbexItem.iItem = idx;
     cbexItem.pszText    = text;
     cbexItem.cchTextMax = 0;
-    return (LONG)SendMessage(cbex, CBEM_SETITEM, 0,(LPARAM)&cbexItem);
+    return SendMessage(cbex, CBEM_SETITEM, 0, (LPARAM)&cbexItem);
 }
 
 static LONG delItem(HWND cbex, int idx) {
-    return (LONG)SendMessage(cbex, CBEM_DELETEITEM, (LPARAM)idx, 0);
+    return SendMessage(cbex, CBEM_DELETEITEM, idx, 0);
 }
 
 static LONG getItem(HWND cbex, int idx, COMBOBOXEXITEM *cbItem) {
@@ -65,7 +77,51 @@ static LONG getItem(HWND cbex, int idx, COMBOBOXEXITEM *cbItem) {
     cbItem->pszText      = textBuffer;
     cbItem->iItem        = idx;
     cbItem->cchTextMax   = 100;
-    return (LONG)SendMessage(cbex, CBEM_GETITEM, 0, (LPARAM)cbItem);
+    return SendMessage(cbex, CBEM_GETITEM, 0, (LPARAM)cbItem);
+}
+
+static LRESULT WINAPI editbox_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
+    static LONG defwndproc_counter = 0;
+    LRESULT ret;
+    struct message msg;
+
+    msg.message = message;
+    msg.flags = sent|wparam|lparam;
+    if (defwndproc_counter) msg.flags |= defwinproc;
+    msg.wParam = wParam;
+    msg.lParam = lParam;
+    msg.id     = EDITBOX_ID;
+
+    if (message != WM_PAINT &&
+        message != WM_ERASEBKGND &&
+        message != WM_NCPAINT &&
+        message != WM_NCHITTEST &&
+        message != WM_GETTEXT &&
+        message != WM_GETICON &&
+        message != WM_DEVICECHANGE)
+    {
+        add_message(sequences, EDITBOX_SEQ_INDEX, &msg);
+    }
+
+    defwndproc_counter++;
+    ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
+    defwndproc_counter--;
+    return ret;
+}
+
+static HWND subclass_editbox(HWND hwndComboEx)
+{
+    WNDPROC oldproc;
+    HWND hwnd;
+
+    hwnd = (HWND)SendMessage(hwndComboEx, CBEM_GETEDITCONTROL, 0, 0);
+    oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
+                                         (LONG_PTR)editbox_subclass_proc);
+    SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc);
+
+    return hwnd;
 }
 
 static void test_comboboxex(void) {
@@ -288,6 +344,41 @@ static void test_WM_LBUTTONDOWN(void)
     DestroyWindow(hComboEx);
 }
 
+static void test_CB_GETLBTEXT(void)
+{
+    HWND hCombo;
+    CHAR buff[1];
+    COMBOBOXEXITEMA item;
+    LRESULT ret;
+
+    hCombo = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
+
+    /* set text to null */
+    addItem(hCombo, 0, NULL);
+
+    buff[0] = 'a';
+    item.mask = CBEIF_TEXT;
+    item.iItem = 0;
+    item.pszText = buff;
+    item.cchTextMax = 1;
+    ret = SendMessage(hCombo, CBEM_GETITEMA, 0, (LPARAM)&item);
+    ok(ret != 0, "CBEM_GETITEM failed\n");
+    ok(buff[0] == 0, "\n");
+
+    ret = SendMessage(hCombo, CB_GETLBTEXTLEN, 0, 0);
+    ok(ret == 0, "Expected zero length\n");
+
+    ret = SendMessage(hCombo, CB_GETLBTEXTLEN, 0, 0);
+    ok(ret == 0, "Expected zero length\n");
+
+    buff[0] = 'a';
+    ret = SendMessage(hCombo, CB_GETLBTEXT, 0, (LPARAM)buff);
+    ok(ret == 0, "Expected zero length\n");
+    ok(buff[0] == 0, "Expected null terminator as a string, got %s\n", buff);
+
+    DestroyWindow(hCombo);
+}
+
 static LRESULT CALLBACK ComboExTestWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
     switch(msg) {
@@ -321,6 +412,8 @@ static int init(void)
     iccex.dwICC  = ICC_USEREX_CLASSES;
     pInitCommonControlsEx(&iccex);
 
+    pSetWindowSubclass = (void*)GetProcAddress(hComctl32, (LPSTR)410);
+
     wc.style = CS_HREDRAW | CS_VREDRAW;
     wc.cbClsExtra = 0;
     wc.cbWndExtra = 0;
@@ -355,13 +448,87 @@ static void cleanup(void)
     UnregisterClassA(ComboExTestClass, GetModuleHandleA(NULL));
 }
 
+static void test_comboboxex_subclass(void)
+{
+    HWND hComboEx, hCombo, hEdit;
+
+    hComboEx = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
+
+    hCombo = (HWND)SendMessage(hComboEx, CBEM_GETCOMBOCONTROL, 0, 0);
+    ok(hCombo != NULL, "Failed to get internal combo\n");
+    hEdit = (HWND)SendMessage(hComboEx, CBEM_GETEDITCONTROL, 0, 0);
+    ok(hEdit != NULL, "Failed to get internal edit\n");
+
+    if (pSetWindowSubclass)
+    {
+        ok(GetPropA(hCombo, "CC32SubclassInfo") != NULL, "Expected CC32SubclassInfo property\n");
+        ok(GetPropA(hEdit, "CC32SubclassInfo") != NULL, "Expected CC32SubclassInfo property\n");
+    }
+
+    DestroyWindow(hComboEx);
+}
+
+static const struct message test_setitem_edit_seq[] = {
+    { WM_SETTEXT, sent|id, 0, 0, EDITBOX_ID },
+    { EM_SETSEL, sent|id|wparam|lparam, 0,  0, EDITBOX_ID },
+    { EM_SETSEL, sent|id|wparam|lparam, 0, -1, EDITBOX_ID },
+    { 0 }
+};
+
+static void test_get_set_item(void)
+{
+    char textA[] = "test";
+    HWND hComboEx;
+    COMBOBOXEXITEMA item;
+    BOOL ret;
+
+    hComboEx = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
+
+    subclass_editbox(hComboEx);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    memset(&item, 0, sizeof(item));
+    item.mask = CBEIF_TEXT;
+    item.pszText = textA;
+    item.iItem = -1;
+    ret = SendMessage(hComboEx, CBEM_SETITEMA, 0, (LPARAM)&item);
+    expect(TRUE, ret);
+
+    ok_sequence(sequences, EDITBOX_SEQ_INDEX, test_setitem_edit_seq, "set item data for edit", FALSE);
+
+    /* get/set lParam */
+    item.mask = CBEIF_LPARAM;
+    item.iItem = -1;
+    item.lParam = 0xdeadbeef;
+    ret = SendMessage(hComboEx, CBEM_GETITEMA, 0, (LPARAM)&item);
+    expect(TRUE, ret);
+    ok(item.lParam == 0, "Expected zero, got %ld\n", item.lParam);
+
+    item.lParam = 0xdeadbeef;
+    ret = SendMessage(hComboEx, CBEM_SETITEMA, 0, (LPARAM)&item);
+    expect(TRUE, ret);
+
+    item.lParam = 0;
+    ret = SendMessage(hComboEx, CBEM_GETITEMA, 0, (LPARAM)&item);
+    expect(TRUE, ret);
+    ok(item.lParam == 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", item.lParam);
+
+    DestroyWindow(hComboEx);
+}
+
 START_TEST(comboex)
 {
     if (!init())
         return;
 
+    init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
+
     test_comboboxex();
     test_WM_LBUTTONDOWN();
+    test_CB_GETLBTEXT();
+    test_comboboxex_subclass();
+    test_get_set_item();
 
     cleanup();
 }
index 9480462..d1e44b5 100644 (file)
@@ -4,6 +4,8 @@
 <module name="comctl32_winetest" type="win32cui" installbase="bin" installname="comctl32_winetest.exe" allowwarnings="true">
        <include base="comctl32_winetest">.</include>
        <define name="__ROS_LONG64__" />
+       <redefine name="_WIN32_WINNT">0x0600</redefine>
+       <redefine name="_WIN32_IE">0x0500</redefine>
        <file>comboex.c</file>
        <file>datetime.c</file>
        <file>dpa.c</file>
@@ -14,7 +16,6 @@
        <file>misc.c</file>
        <file>monthcal.c</file>
        <file>mru.c</file>
-       <file>msg.c</file>
        <file>progress.c</file>
        <file>propsheet.c</file>
        <file>rebar.c</file>
diff --git a/rostests/winetests/comctl32/comctl32_ros.diff b/rostests/winetests/comctl32/comctl32_ros.diff
deleted file mode 100644 (file)
index d8b962d..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-Index: dpa.c
-===================================================================
---- dpa.c      (revision 25766)
-+++ dpa.c      (working copy)
-@@ -25,6 +25,7 @@
- #include "windef.h"
- #include "winbase.h"
-+#include "wingdi.h"
- #include "winuser.h"
- #include "commctrl.h"
- #include "objidl.h"
-Index: monthcal.c
-===================================================================
---- monthcal.c (revision 25766)
-+++ monthcal.c (working copy)
-@@ -23,6 +23,7 @@
- #include "windef.h"
- #include "winbase.h"
-+#include "wingdi.h"
- #include "winuser.h"
- #include "commctrl.h"
-Index: mru.c
-===================================================================
---- mru.c      (revision 25766)
-+++ mru.c      (working copy)
-@@ -75,7 +75,7 @@
- /* Based on RegDeleteTreeW from dlls/advapi32/registry.c */
--static LSTATUS mru_RegDeleteTreeA(HKEY hKey, LPCSTR lpszSubKey)
-+static LONG mru_RegDeleteTreeA(HKEY hKey, LPCSTR lpszSubKey)
- {
-     LONG ret;
-     DWORD dwMaxSubkeyLen, dwMaxValueLen;
index 8fdbbe5..a97ab34 100644 (file)
 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
 
 static const struct message test_dtm_set_format_seq[] = {
-    { DTM_SETFORMATA, sent|wparam|lparam, 0x00000000, 0x00000000 },
-    { DTM_SETFORMATA, sent|wparam, 0x00000000 },
+    { DTM_SETFORMATA, sent|wparam|lparam, 00 },
+    { DTM_SETFORMATA, sent|wparam, 0 },
     { 0 }
 };
 
 static const struct message test_dtm_set_and_get_mccolor_seq[] = {
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000000, 0x00000000 },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000000, 0x00ffffff },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000000, 0x00dcb464 },
-    { DTM_GETMCCOLOR, sent|wparam|lparam, 0x00000000, 0x00000000 },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000004, 0x00000000 },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000004, 0x00ffffff },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000004, 0x00dcb464 },
-    { DTM_GETMCCOLOR, sent|wparam|lparam, 0x00000004, 0x00000000 },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000001, 0x00000000 },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000001, 0x00ffffff },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000001, 0x00dcb464 },
-    { DTM_GETMCCOLOR, sent|wparam|lparam, 0x00000001, 0x00000000 },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000002, 0x00000000 },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000002, 0x00ffffff },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000002, 0x00dcb464 },
-    { DTM_GETMCCOLOR, sent|wparam|lparam, 0x00000002, 0x00000000 },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000003, 0x00000000 },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000003, 0x00ffffff },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000003, 0x00dcb464 },
-    { DTM_GETMCCOLOR, sent|wparam|lparam, 0x00000003, 0x00000000 },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000005, 0x00000000 },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000005, 0x00ffffff },
-    { DTM_SETMCCOLOR, sent|wparam|lparam, 0x00000005, 0x00dcb464 },
-    { DTM_GETMCCOLOR, sent|wparam|lparam, 0x00000005, 0x00000000 },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_BACKGROUND, 0 },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_BACKGROUND, RGB(255, 255, 255) },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_BACKGROUND, RGB(100, 180, 220) },
+    { DTM_GETMCCOLOR, sent|wparam|lparam, MCSC_BACKGROUND, 0 },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_MONTHBK, 0 },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_MONTHBK, RGB(255, 255, 255) },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_MONTHBK, RGB(100, 180, 220) },
+    { DTM_GETMCCOLOR, sent|wparam|lparam, MCSC_MONTHBK, 0 },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TEXT, 0 },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TEXT, RGB(255, 255, 255) },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TEXT, RGB(100, 180, 220) },
+    { DTM_GETMCCOLOR, sent|wparam|lparam, MCSC_TEXT, 0 },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TITLEBK, 0 },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TITLEBK, RGB(255, 255, 255) },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TITLEBK, RGB(100, 180, 220) },
+    { DTM_GETMCCOLOR, sent|wparam|lparam, MCSC_TITLEBK, 0 },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TITLETEXT, 0 },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TITLETEXT, RGB(255, 255, 255) },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TITLETEXT, RGB(100, 180, 220) },
+    { DTM_GETMCCOLOR, sent|wparam|lparam, MCSC_TITLETEXT, 0 },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TRAILINGTEXT, 0 },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TRAILINGTEXT, RGB(255, 255, 255) },
+    { DTM_SETMCCOLOR, sent|wparam|lparam, MCSC_TRAILINGTEXT, RGB(100, 180, 220) },
+    { DTM_GETMCCOLOR, sent|wparam|lparam, MCSC_TRAILINGTEXT, 0 },
     { 0 }
 };
 
 static const struct message test_dtm_set_and_get_mcfont_seq[] = {
-    { DTM_SETMCFONT, sent|lparam, 0, 0x00000001 },
-    { DTM_GETMCFONT, sent|wparam|lparam, 0x00000000, 0x00000000 },
+    { DTM_SETMCFONT, sent|lparam, 0, 1 },
+    { DTM_GETMCFONT, sent|wparam|lparam, 00 },
     { 0 }
 };
 
 static const struct message test_dtm_get_monthcal_seq[] = {
-    { DTM_GETMONTHCAL, sent|wparam|lparam, 0x00000000, 0x00000000 },
+    { DTM_GETMONTHCAL, sent|wparam|lparam, 00 },
     { 0 }
 };
 
 static const struct message test_dtm_set_and_get_range_seq[] = {
-    { DTM_SETRANGE, sent|wparam, 0x00000001 },
-    { DTM_GETRANGE, sent|wparam, 0x00000000 },
-    { DTM_SETRANGE, sent|wparam, 0x00000002 },
-    { DTM_SETRANGE, sent|wparam, 0x00000002 },
-    { DTM_GETRANGE, sent|wparam, 0x00000000},
-    { DTM_SETRANGE, sent|wparam, 0x00000001 },
-    { DTM_SETRANGE, sent|wparam, 0x00000003 },
-    { DTM_SETRANGE, sent|wparam, 0x00000003 },
-    { DTM_GETRANGE, sent|wparam, 0x00000000 },
-    { DTM_SETRANGE, sent|wparam, 0x00000003 },
-    { DTM_GETRANGE, sent|wparam, 0x00000000 },
-    { DTM_SETRANGE, sent|wparam, 0x00000003 },
-    { DTM_GETRANGE, sent|wparam, 0x00000000 },
+    { DTM_SETRANGE, sent|wparam, GDTR_MIN },
+    { DTM_GETRANGE, sent|wparam, 0 },
+    { DTM_SETRANGE, sent|wparam, GDTR_MAX },
+    { DTM_SETRANGE, sent|wparam, GDTR_MAX },
+    { DTM_GETRANGE, sent|wparam, 0 },
+    { DTM_SETRANGE, sent|wparam, GDTR_MIN },
+    { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
+    { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
+    { DTM_GETRANGE, sent|wparam, 0 },
+    { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
+    { DTM_GETRANGE, sent|wparam, 0 },
+    { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
+    { DTM_GETRANGE, sent|wparam, 0 },
     { 0 }
 };
 
 static const struct message test_dtm_set_range_swap_min_max_seq[] = {
-    { DTM_SETSYSTEMTIME, sent|wparam, 0x00000000 },
-    { DTM_GETSYSTEMTIME, sent|wparam, 0x00000000 },
-    { DTM_SETRANGE, sent|wparam, 0x00000003 },
-    { DTM_GETRANGE, sent|wparam, 0x00000000 },
-    { DTM_SETSYSTEMTIME, sent|wparam, 0x00000000 },
-    { DTM_GETSYSTEMTIME, sent|wparam, 0x00000000 },
-    { DTM_SETRANGE, sent|wparam, 0x00000003 },
-    { DTM_GETRANGE, sent|wparam, 0x00000000 },
-    { DTM_SETRANGE, sent|wparam, 0x00000003 },
-    { DTM_GETRANGE, sent|wparam, 0x00000000 },
-    { DTM_SETRANGE, sent|wparam, 0x00000003 },
-    { DTM_GETRANGE, sent|wparam, 0x00000000 },
+    { DTM_SETSYSTEMTIME, sent|wparam, 0 },
+    { DTM_GETSYSTEMTIME, sent|wparam, 0 },
+    { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
+    { DTM_GETRANGE, sent|wparam, 0 },
+    { DTM_SETSYSTEMTIME, sent|wparam, 0 },
+    { DTM_GETSYSTEMTIME, sent|wparam, 0 },
+    { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
+    { DTM_GETRANGE, sent|wparam, 0 },
+    { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
+    { DTM_GETRANGE, sent|wparam, 0 },
+    { DTM_SETRANGE, sent|wparam, GDTR_MIN | GDTR_MAX },
+    { DTM_GETRANGE, sent|wparam, 0 },
     { 0 }
 };
 
 static const struct message test_dtm_set_and_get_system_time_seq[] = {
-    { DTM_SETSYSTEMTIME, sent|wparam, 0x00000001 },
-    { 0x0090, sent|optional }, /* Vista */
-    { WM_DESTROY, sent|wparam|lparam, 0x00000000, 0x00000000 },
-    { WM_NCDESTROY, sent|wparam|lparam, 0x00000000, 0x00000000 },
-    { DTM_SETSYSTEMTIME, sent|wparam, 0x00000001 },
-    { DTM_GETSYSTEMTIME, sent|wparam, 0x00000000 },
-    { DTM_SETSYSTEMTIME, sent|wparam, 0x00000000 },
-    { DTM_SETSYSTEMTIME, sent|wparam, 0x00000000 },
-    { DTM_SETSYSTEMTIME, sent|wparam, 0x00000000 },
-    { DTM_GETSYSTEMTIME, sent|wparam, 0x00000000 },
-    { DTM_SETSYSTEMTIME, sent|wparam, 0x00000000 },
+    { DTM_SETSYSTEMTIME, sent|wparam, GDT_NONE },
+    { DTM_GETSYSTEMTIME, sent|wparam, 0 },
+    { DTM_SETSYSTEMTIME, sent|wparam, 0 },
+    { DTM_SETSYSTEMTIME, sent|wparam, 0 },
+    { DTM_SETSYSTEMTIME, sent|wparam, 0 },
+    { DTM_GETSYSTEMTIME, sent|wparam, 0 },
+    { DTM_SETSYSTEMTIME, sent|wparam, 0 },
     { 0 }
 };
 
-static const struct message destroy_window_seq[] = {
-    { 0x0090, sent|optional }, /* Vista */
-    { WM_DESTROY, sent|wparam|lparam, 0x00000000, 0x00000000 },
-    { WM_NCDESTROY, sent|wparam|lparam, 0x00000000, 0x00000000 },
-    { 0 }
-};
-
-struct subclass_info
-{
-    WNDPROC oldproc;
-};
-
 static LRESULT WINAPI datetime_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
-    struct subclass_info *info = (struct subclass_info *)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
+    WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
     static LONG defwndproc_counter = 0;
     LRESULT ret;
     struct message msg;
@@ -154,21 +138,17 @@ static LRESULT WINAPI datetime_subclass_proc(HWND hwnd, UINT message, WPARAM wPa
     add_message(sequences, DATETIME_SEQ_INDEX, &msg);
 
     defwndproc_counter++;
-    ret = CallWindowProcA(info->oldproc, hwnd, message, wParam, lParam);
+    ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
     defwndproc_counter--;
 
     return ret;
 }
 
-static HWND create_datetime_control(DWORD style, DWORD exstyle)
+static HWND create_datetime_control(DWORD style)
 {
-    struct subclass_info *info;
+    WNDPROC oldproc;
     HWND hWndDateTime = NULL;
 
-    info = HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info));
-    if (!info)
-        return NULL;
-
     hWndDateTime = CreateWindowEx(0,
         DATETIMEPICK_CLASS,
         NULL,
@@ -179,44 +159,47 @@ static HWND create_datetime_control(DWORD style, DWORD exstyle)
         NULL,
         NULL);
 
-    if (!hWndDateTime) {
-        HeapFree(GetProcessHeap(), 0, info);
-        return NULL;
-    }
+    if (!hWndDateTime) return NULL;
 
-    info->oldproc = (WNDPROC)SetWindowLongPtrA(hWndDateTime, GWLP_WNDPROC,
-                                            (LONG_PTR)datetime_subclass_proc);
-    SetWindowLongPtrA(hWndDateTime, GWLP_USERDATA, (LONG_PTR)info);
+    oldproc = (WNDPROC)SetWindowLongPtrA(hWndDateTime, GWLP_WNDPROC,
+                                         (LONG_PTR)datetime_subclass_proc);
+    SetWindowLongPtrA(hWndDateTime, GWLP_USERDATA, (LONG_PTR)oldproc);
 
     return hWndDateTime;
 }
 
-static void test_dtm_set_format(HWND hWndDateTime)
+static void test_dtm_set_format(void)
 {
+    HWND hWnd;
     CHAR txt[256];
     SYSTEMTIME systime;
     LRESULT r;
 
-    r = SendMessage(hWndDateTime, DTM_SETFORMAT, 0, 0);
+    hWnd = create_datetime_control(DTS_SHOWNONE);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    r = SendMessage(hWnd, DTM_SETFORMAT, 0, 0);
     expect(1, r);
 
-    r = SendMessage(hWndDateTime, DTM_SETFORMAT, 0,
+    r = SendMessage(hWnd, DTM_SETFORMAT, 0,
                    (LPARAM)"'Today is: 'hh':'m':'s dddd MMM dd', 'yyyy");
     expect(1, r);
 
     ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_set_format_seq, "test_dtm_set_format", FALSE);
 
-    r = SendMessage(hWndDateTime, DTM_SETFORMAT, 0,
+    r = SendMessage(hWnd, DTM_SETFORMAT, 0,
                    (LPARAM)"'hh' hh");
     expect(1, r);
     ZeroMemory(&systime, sizeof(systime));
     systime.wYear = 2000;
     systime.wMonth = systime.wDay = 1;
-    r = SendMessage(hWndDateTime, DTM_SETSYSTEMTIME, 0, (LPARAM)&systime);
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, 0, (LPARAM)&systime);
     expect(1, r);
-    GetWindowText(hWndDateTime, txt, 256);
-    todo_wine ok(strcmp(txt, "hh 12") == 0, "String mismatch (\"%s\" vs \"hh 12\")\n", txt);
-    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    GetWindowText(hWnd, txt, 256);
+    ok(strcmp(txt, "hh 12") == 0, "String mismatch (\"%s\" vs \"hh 12\")\n", txt);
+
+    DestroyWindow(hWnd);
 }
 
 static void test_mccolor_types(HWND hWndDateTime, int mccolor_type, const char* mccolor_name)
@@ -239,43 +222,60 @@ static void test_mccolor_types(HWND hWndDateTime, int mccolor_type, const char*
     ok(r==theColor, "%s: GETMCCOLOR: Expected %d, got %ld\n", mccolor_name, theColor, r);
 }
 
-static void test_dtm_set_and_get_mccolor(HWND hWndDateTime)
+static void test_dtm_set_and_get_mccolor(void)
 {
-    test_mccolor_types(hWndDateTime, MCSC_BACKGROUND, "MCSC_BACKGROUND");
-    test_mccolor_types(hWndDateTime, MCSC_MONTHBK, "MCSC_MONTHBK");
-    test_mccolor_types(hWndDateTime, MCSC_TEXT, "MCSC_TEXT");
-    test_mccolor_types(hWndDateTime, MCSC_TITLEBK, "MCSC_TITLEBK");
-    test_mccolor_types(hWndDateTime, MCSC_TITLETEXT, "MCSC_TITLETEXT");
-    test_mccolor_types(hWndDateTime, MCSC_TRAILINGTEXT, "MCSC_TRAILINGTEXT");
+    HWND hWnd;
+
+    hWnd = create_datetime_control(DTS_SHOWNONE);
 
-    ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_set_and_get_mccolor_seq, "test_dtm_set_and_get_mccolor", FALSE);
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    test_mccolor_types(hWnd, MCSC_BACKGROUND, "MCSC_BACKGROUND");
+    test_mccolor_types(hWnd, MCSC_MONTHBK, "MCSC_MONTHBK");
+    test_mccolor_types(hWnd, MCSC_TEXT, "MCSC_TEXT");
+    test_mccolor_types(hWnd, MCSC_TITLEBK, "MCSC_TITLEBK");
+    test_mccolor_types(hWnd, MCSC_TITLETEXT, "MCSC_TITLETEXT");
+    test_mccolor_types(hWnd, MCSC_TRAILINGTEXT, "MCSC_TRAILINGTEXT");
+
+    ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_set_and_get_mccolor_seq, "test_dtm_set_and_get_mccolor", FALSE);
+
+    DestroyWindow(hWnd);
 }
 
-static void test_dtm_set_and_get_mcfont(HWND hWndDateTime)
+static void test_dtm_set_and_get_mcfont(void)
 {
     HFONT hFontOrig, hFontNew;
+    HWND hWnd;
+
+    hWnd = create_datetime_control(DTS_SHOWNONE);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
     hFontOrig = GetStockObject(DEFAULT_GUI_FONT);
-    SendMessage(hWndDateTime, DTM_SETMCFONT, (WPARAM)hFontOrig, TRUE);
-    hFontNew = (HFONT)SendMessage(hWndDateTime, DTM_GETMCFONT, 0, 0);
+    SendMessage(hWnd, DTM_SETMCFONT, (WPARAM)hFontOrig, TRUE);
+    hFontNew = (HFONT)SendMessage(hWnd, DTM_GETMCFONT, 0, 0);
     ok(hFontOrig == hFontNew, "Expected hFontOrig==hFontNew, hFontOrig=%p, hFontNew=%p\n", hFontOrig, hFontNew);
 
     ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_set_and_get_mcfont_seq, "test_dtm_set_and_get_mcfont", FALSE);
-    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    DestroyWindow(hWnd);
 }
 
-static void test_dtm_get_monthcal(HWND hWndDateTime)
+static void test_dtm_get_monthcal(void)
 {
     LRESULT r;
+    HWND hWnd;
+
+    hWnd = create_datetime_control(DTS_SHOWNONE);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
     todo_wine {
-        r = SendMessage(hWndDateTime, DTM_GETMONTHCAL, 0, 0);
+        r = SendMessage(hWnd, DTM_GETMONTHCAL, 0, 0);
         ok(r == 0, "Expected NULL(no child month calendar control), got %ld\n", r);
     }
 
     ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_get_monthcal_seq, "test_dtm_get_monthcal", FALSE);
-    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    DestroyWindow(hWnd);
 }
 
 static void fill_systime_struct(SYSTEMTIME *st, int year, int month, int dayofweek, int day, int hour, int minute, int second, int milliseconds)
@@ -318,24 +318,29 @@ static LPARAM compare_systime(SYSTEMTIME *st1, SYSTEMTIME *st2)
 #define expect_systime_date(ST1, ST2) ok(compare_systime_date((ST1), (ST2))==1, "ST1.date != ST2.date\n")
 #define expect_systime_time(ST1, ST2) ok(compare_systime_time((ST1), (ST2))==1, "ST1.time != ST2.time\n")
 
-static void test_dtm_set_and_get_range(HWND hWndDateTime)
+static void test_dtm_set_and_get_range(void)
 {
     LRESULT r;
     SYSTEMTIME st[2];
     SYSTEMTIME getSt[2];
+    HWND hWnd;
+
+    hWnd = create_datetime_control(DTS_SHOWNONE);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
     /* initialize st[0] to lowest possible value */
     fill_systime_struct(&st[0], 1601, 1, 0, 1, 0, 0, 0, 0);
     /* initialize st[1] to all invalid numbers */
     fill_systime_struct(&st[1], 0, 0, 7, 0, 24, 60, 60, 1000);
 
-    r = SendMessage(hWndDateTime, DTM_SETRANGE, GDTR_MIN, (LPARAM)st);
+    r = SendMessage(hWnd, DTM_SETRANGE, GDTR_MIN, (LPARAM)st);
     expect(1, r);
-    r = SendMessage(hWndDateTime, DTM_GETRANGE, 0, (LPARAM)getSt);
+    r = SendMessage(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
     ok(r == GDTR_MIN, "Expected %x, not %x(GDTR_MAX) or %x(GDTR_MIN | GDTR_MAX), got %lx\n", GDTR_MIN, GDTR_MAX, GDTR_MIN | GDTR_MAX, r);
     expect_systime(&st[0], &getSt[0]);
 
-    r = SendMessage(hWndDateTime, DTM_SETRANGE, GDTR_MAX, (LPARAM)st);
+    r = SendMessage(hWnd, DTM_SETRANGE, GDTR_MAX, (LPARAM)st);
     expect_unsuccess(0, r);
 
     /* set st[0] to all invalid numbers */
@@ -343,25 +348,25 @@ static void test_dtm_set_and_get_range(HWND hWndDateTime)
     /* set st[1] to highest possible value */
     fill_systime_struct(&st[1], 30827, 12, 6, 31, 23, 59, 59, 999);
 
-    r = SendMessage(hWndDateTime, DTM_SETRANGE, GDTR_MAX, (LPARAM)st);
+    r = SendMessage(hWnd, DTM_SETRANGE, GDTR_MAX, (LPARAM)st);
     expect(1, r);
-    r = SendMessage(hWndDateTime, DTM_GETRANGE, 0, (LPARAM)getSt);
+    r = SendMessage(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
     todo_wine {
         ok(r == GDTR_MAX, "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MIN | GDTR_MAX), got %lx\n", GDTR_MAX, GDTR_MIN, GDTR_MIN | GDTR_MAX, r);
     }
     expect_systime(&st[1], &getSt[1]);
 
-    r = SendMessage(hWndDateTime, DTM_SETRANGE, GDTR_MIN, (LPARAM)st);
+    r = SendMessage(hWnd, DTM_SETRANGE, GDTR_MIN, (LPARAM)st);
     expect_unsuccess(0, r);
-    r = SendMessage(hWndDateTime, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
+    r = SendMessage(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
     expect_unsuccess(0, r);
 
     /* set st[0] to highest possible value */
     fill_systime_struct(&st[0], 30827, 12, 6, 31, 23, 59, 59, 999);
 
-    r = SendMessage(hWndDateTime, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
+    r = SendMessage(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
     expect(1, r);
-    r = SendMessage(hWndDateTime, DTM_GETRANGE, 0, (LPARAM)getSt);
+    r = SendMessage(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
     ok(r == (GDTR_MIN | GDTR_MAX), "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MAX), got %lx\n", (GDTR_MIN | GDTR_MAX), GDTR_MIN, GDTR_MAX, r);
     expect_systime(&st[0], &getSt[0]);
     expect_systime(&st[1], &getSt[1]);
@@ -371,9 +376,9 @@ static void test_dtm_set_and_get_range(HWND hWndDateTime)
     /* set st[1] to highest possible value */
     fill_systime_struct(&st[1], 30827, 12, 6, 31, 23, 59, 59, 999);
 
-    r = SendMessage(hWndDateTime, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
+    r = SendMessage(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
     expect(1, r);
-    r = SendMessage(hWndDateTime, DTM_GETRANGE, 0, (LPARAM)getSt);
+    r = SendMessage(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
     ok(r == (GDTR_MIN | GDTR_MAX), "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MAX), got %lx\n", (GDTR_MIN | GDTR_MAX), GDTR_MIN, GDTR_MAX, r);
     expect_systime(&st[0], &getSt[0]);
     expect_systime(&st[1], &getSt[1]);
@@ -383,32 +388,37 @@ static void test_dtm_set_and_get_range(HWND hWndDateTime)
     /* set st[1] to value lower than maximum */
     fill_systime_struct(&st[1], 2007, 3, 2, 31, 23, 59, 59, 999);
 
-    r = SendMessage(hWndDateTime, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
+    r = SendMessage(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
     expect(1, r);
-    r = SendMessage(hWndDateTime, DTM_GETRANGE, 0, (LPARAM)getSt);
+    r = SendMessage(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
     ok(r == (GDTR_MIN | GDTR_MAX), "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MAX), got %lx\n", (GDTR_MIN | GDTR_MAX), GDTR_MIN, GDTR_MAX, r);
     expect_systime(&st[0], &getSt[0]);
     expect_systime(&st[1], &getSt[1]);
 
     ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_set_and_get_range_seq, "test_dtm_set_and_get_range", FALSE);
-    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    DestroyWindow(hWnd);
 }
 
 /* when max<min for DTM_SETRANGE, Windows seems to swap the min and max values,
 although that's undocumented.  However, it doesn't seem to be implemented
 correctly, causing some strange side effects */
-static void test_dtm_set_range_swap_min_max(HWND hWndDateTime)
+static void test_dtm_set_range_swap_min_max(void)
 {
     LRESULT r;
     SYSTEMTIME st[2];
     SYSTEMTIME getSt[2];
     SYSTEMTIME origSt;
+    HWND hWnd;
+
+    hWnd = create_datetime_control(DTS_SHOWNONE);
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
     fill_systime_struct(&st[0], 2007, 2, 4, 15, 2, 2, 2, 2);
 
-    r = SendMessage(hWndDateTime, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st[0]);
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st[0]);
     expect(1, r);
-    r = SendMessage(hWndDateTime, DTM_GETSYSTEMTIME, 0, (LPARAM)&origSt);
+    r = SendMessage(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&origSt);
     ok(r == GDT_VALID, "Expected %d, not %d(GDT_NONE) or %d(GDT_ERROR), got %ld\n", GDT_VALID, GDT_NONE, GDT_ERROR, r);
     expect_systime(&st[0], &origSt);
 
@@ -418,28 +428,33 @@ static void test_dtm_set_range_swap_min_max(HWND hWndDateTime)
 
     /* since min>max, min and max values should be swapped by DTM_SETRANGE
     automatically */
-    r = SendMessage(hWndDateTime, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
+    r = SendMessage(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
     expect(1, r);
-    r = SendMessage(hWndDateTime, DTM_GETRANGE, 0, (LPARAM)getSt);
+    r = SendMessage(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
     ok(r == (GDTR_MIN | GDTR_MAX), "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MAX), got %lx\n", (GDTR_MIN | GDTR_MAX), GDTR_MIN, GDTR_MAX, r);
     todo_wine {
-        expect_systime(&st[0], &getSt[0]);
-    }
-    todo_wine {
-        expect_systime(&st[1], &getSt[1]);
+        ok(compare_systime(&st[0], &getSt[0]) == 1 ||
+           broken(compare_systime(&st[0], &getSt[1]) == 1), /* comctl32 version  <= 5.80 */
+           "ST1 != ST2\n");
+
+        ok(compare_systime(&st[1], &getSt[1]) == 1 ||
+           broken(compare_systime(&st[1], &getSt[0]) == 1), /* comctl32 version  <= 5.80 */
+           "ST1 != ST2\n");
     }
 
     fill_systime_struct(&st[0], 1980, 1, 3, 23, 14, 34, 37, 465);
 
-    r = SendMessage(hWndDateTime, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st[0]);
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st[0]);
     expect(1, r);
-    r = SendMessage(hWndDateTime, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt[0]);
+    r = SendMessage(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt[0]);
     ok(r == GDT_VALID, "Expected %d, not %d(GDT_NONE) or %d(GDT_ERROR), got %ld\n", GDT_VALID, GDT_NONE, GDT_ERROR, r);
     /* the time part seems to not change after swapping the min and max values
     and doing DTM_SETSYSTEMTIME */
     expect_systime_date(&st[0], &getSt[0]);
     todo_wine {
-        expect_systime_time(&origSt, &getSt[0]);
+        ok(compare_systime_time(&origSt, &getSt[0]) == 1 ||
+           broken(compare_systime_time(&st[0], &getSt[0]) == 1), /* comctl32 version  <= 5.80 */
+           "ST1.time != ST2.time\n");
     }
 
     /* set st[0] to value higher than minimum */
@@ -447,18 +462,21 @@ static void test_dtm_set_range_swap_min_max(HWND hWndDateTime)
     /* set st[1] to value lower than maximum */
     fill_systime_struct(&st[1], 2007, 3, 2, 31, 23, 59, 59, 999);
 
-    r = SendMessage(hWndDateTime, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
+    r = SendMessage(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
     expect(1, r);
     /* for some reason after we swapped the min and max values before,
     whenever we do a DTM_SETRANGE, the DTM_GETRANGE will return the values
     swapped*/
-    r = SendMessage(hWndDateTime, DTM_GETRANGE, 0, (LPARAM)getSt);
+    r = SendMessage(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
     ok(r == (GDTR_MIN | GDTR_MAX), "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MAX), got %lx\n", (GDTR_MIN | GDTR_MAX), GDTR_MIN, GDTR_MAX, r);
     todo_wine {
-        expect_systime(&st[0], &getSt[1]);
-    }
-    todo_wine {
-        expect_systime(&st[1], &getSt[0]);
+        ok(compare_systime(&st[0], &getSt[1]) == 1 ||
+           broken(compare_systime(&st[0], &getSt[0]) == 1), /* comctl32 version  <= 5.80 */
+           "ST1 != ST2\n");
+
+        ok(compare_systime(&st[1], &getSt[0]) == 1 ||
+           broken(compare_systime(&st[1], &getSt[1]) == 1), /* comctl32 version <= 5.80 */
+           "ST1 != ST2\n");
     }
 
     /* set st[0] to value higher than st[1] */
@@ -467,9 +485,9 @@ static void test_dtm_set_range_swap_min_max(HWND hWndDateTime)
 
     /* set min>max again, so that the return values of DTM_GETRANGE are no
     longer swapped the next time we do a DTM SETRANGE and DTM_GETRANGE*/
-    r = SendMessage(hWndDateTime, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
+    r = SendMessage(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
     expect(1, r);
-    r = SendMessage(hWndDateTime, DTM_GETRANGE, 0, (LPARAM)getSt);
+    r = SendMessage(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
     ok(r == (GDTR_MIN | GDTR_MAX), "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MAX), got %lx\n", (GDTR_MIN | GDTR_MAX), GDTR_MIN, GDTR_MAX, r);
     expect_systime(&st[0], &getSt[1]);
     expect_systime(&st[1], &getSt[0]);
@@ -479,25 +497,25 @@ static void test_dtm_set_range_swap_min_max(HWND hWndDateTime)
     /* set st[1] to highest possible value */
     fill_systime_struct(&st[1], 30827, 12, 6, 31, 23, 59, 59, 999);
 
-    r = SendMessage(hWndDateTime, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
+    r = SendMessage(hWnd, DTM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st);
     expect(1, r);
-    r = SendMessage(hWndDateTime, DTM_GETRANGE, 0, (LPARAM)getSt);
+    r = SendMessage(hWnd, DTM_GETRANGE, 0, (LPARAM)getSt);
     ok(r == (GDTR_MIN | GDTR_MAX), "Expected %x, not %x(GDTR_MIN) or %x(GDTR_MAX), got %lx\n", (GDTR_MIN | GDTR_MAX), GDTR_MIN, GDTR_MAX, r);
     expect_systime(&st[0], &getSt[0]);
     expect_systime(&st[1], &getSt[1]);
 
     ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_set_range_swap_min_max_seq, "test_dtm_set_range_swap_min_max", FALSE);
-    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    DestroyWindow(hWnd);
 }
 
-static void test_dtm_set_and_get_system_time(HWND hWndDateTime)
+static void test_dtm_set_and_get_system_time(void)
 {
     LRESULT r;
-    SYSTEMTIME st;
-    SYSTEMTIME getSt;
-    HWND hWndDateTime_test_gdt_none;
+    SYSTEMTIME st, getSt, ref;
+    HWND hWnd, hWndDateTime_test_gdt_none;
 
-    hWndDateTime_test_gdt_none = create_datetime_control(0, 0);
+    hWndDateTime_test_gdt_none = create_datetime_control(0);
 
     ok(hWndDateTime_test_gdt_none!=NULL, "Expected non NULL, got %p\n", hWndDateTime_test_gdt_none);
     if(hWndDateTime_test_gdt_none) {
@@ -513,64 +531,175 @@ static void test_dtm_set_and_get_system_time(HWND hWndDateTime)
 
     DestroyWindow(hWndDateTime_test_gdt_none);
 
-    r = SendMessage(hWndDateTime, DTM_SETSYSTEMTIME, GDT_NONE, (LPARAM)&st);
+    hWnd = create_datetime_control(DTS_SHOWNONE);
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_NONE, (LPARAM)&st);
     expect(1, r);
-    r = SendMessage(hWndDateTime, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
+    r = SendMessage(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
     ok(r == GDT_NONE, "Expected %d, not %d(GDT_VALID) or %d(GDT_ERROR), got %ld\n", GDT_NONE, GDT_VALID, GDT_ERROR, r);
 
     /* set st to lowest possible value */
     fill_systime_struct(&st, 1601, 1, 0, 1, 0, 0, 0, 0);
 
-    r = SendMessage(hWndDateTime, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
     expect(1, r);
 
     /* set st to highest possible value */
     fill_systime_struct(&st, 30827, 12, 6, 31, 23, 59, 59, 999);
 
-    r = SendMessage(hWndDateTime, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
     expect(1, r);
 
     /* set st to value between min and max */
     fill_systime_struct(&st, 1980, 1, 3, 23, 14, 34, 37, 465);
 
-    r = SendMessage(hWndDateTime, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
     expect(1, r);
-    r = SendMessage(hWndDateTime, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
+    r = SendMessage(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
     ok(r == GDT_VALID, "Expected %d, not %d(GDT_NONE) or %d(GDT_ERROR), got %ld\n", GDT_VALID, GDT_NONE, GDT_ERROR, r);
     expect_systime(&st, &getSt);
 
     /* set st to invalid value */
     fill_systime_struct(&st, 0, 0, 7, 0, 24, 60, 60, 1000);
 
-    r = SendMessage(hWndDateTime, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
     expect_unsuccess(0, r);
 
     ok_sequence(sequences, DATETIME_SEQ_INDEX, test_dtm_set_and_get_system_time_seq, "test_dtm_set_and_get_system_time", FALSE);
-    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    /* set to some valid value */
+    GetSystemTime(&ref);
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&ref);
+    expect(1, r);
+    r = SendMessage(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
+    expect(GDT_VALID, r);
+    expect_systime(&ref, &getSt);
+
+    /* year invalid */
+    st = ref;
+    st.wYear = 0;
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
+    todo_wine expect(1, r);
+    r = SendMessage(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
+    expect(GDT_VALID, r);
+    expect_systime(&ref, &getSt);
+    /* month invalid */
+    st = ref;
+    st.wMonth = 13;
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
+    expect(0, r);
+    r = SendMessage(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
+    expect(GDT_VALID, r);
+    expect_systime(&ref, &getSt);
+    /* day invalid */
+    st = ref;
+    st.wDay = 32;
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
+    expect(0, r);
+    r = SendMessage(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
+    expect(GDT_VALID, r);
+    expect_systime(&ref, &getSt);
+    /* day of week isn't validated */
+    st = ref;
+    st.wDayOfWeek = 10;
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
+    expect(1, r);
+    r = SendMessage(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
+    expect(GDT_VALID, r);
+    expect_systime(&ref, &getSt);
+    /* hour invalid */
+    st = ref;
+    st.wHour = 25;
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
+    expect(0, r);
+    r = SendMessage(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
+    expect(GDT_VALID, r);
+    expect_systime(&ref, &getSt);
+    /* minute invalid */
+    st = ref;
+    st.wMinute = 60;
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
+    expect(0, r);
+    r = SendMessage(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
+    expect(GDT_VALID, r);
+    expect_systime(&ref, &getSt);
+    /* sec invalid */
+    st = ref;
+    st.wSecond = 60;
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
+    expect(0, r);
+    r = SendMessage(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
+    expect(GDT_VALID, r);
+    expect_systime(&ref, &getSt);
+    /* msec invalid */
+    st = ref;
+    st.wMilliseconds = 1000;
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
+    expect(0, r);
+    r = SendMessage(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
+    expect(GDT_VALID, r);
+    expect_systime(&ref, &getSt);
+
+    /* day of week should be calculated automatically,
+       actual day of week for this date is 4 */
+    fill_systime_struct(&st, 2009, 10, 1, 1, 0, 0, 10, 200);
+    r = SendMessage(hWnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM)&st);
+    expect(1, r);
+    r = SendMessage(hWnd, DTM_GETSYSTEMTIME, 0, (LPARAM)&getSt);
+    expect(GDT_VALID, r);
+    /* 01.10.2009 is Thursday */
+    expect(4, (LRESULT)getSt.wDayOfWeek);
+    st.wDayOfWeek = 4;
+    expect_systime(&st, &getSt);
+
+    DestroyWindow(hWnd);
 }
 
-static void test_datetime_control(void)
+static void test_wm_set_get_text(void)
 {
-    HWND hWndDateTime;
-
-    hWndDateTime = create_datetime_control(DTS_SHOWNONE, 0);
-
-    ok(hWndDateTime != NULL, "Expected non NULL, got %p\n", hWndDateTime);
-    if(hWndDateTime!=NULL) {
-        test_dtm_set_format(hWndDateTime);
-        test_dtm_set_and_get_mccolor(hWndDateTime);
-        test_dtm_set_and_get_mcfont(hWndDateTime);
-        test_dtm_get_monthcal(hWndDateTime);
-        test_dtm_set_and_get_range(hWndDateTime);
-        test_dtm_set_range_swap_min_max(hWndDateTime);
-        test_dtm_set_and_get_system_time(hWndDateTime);
-    }
-    else {
-        skip("hWndDateTime is NULL\n");
-    }
+    static const CHAR a_str[] = "a";
+    char buff[16], time[16];
+    HWND hWnd;
+    LRESULT ret;
+
+    hWnd = create_datetime_control(0);
+
+    ret = SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)a_str);
+    ok(CB_ERR == ret ||
+       broken(0 == ret) || /* comctl32 <= 4.72 */
+       broken(1 == ret), /* comctl32 <= 4.70 */
+       "Expected CB_ERR, got %ld\n", ret);
+
+    buff[0] = 0;
+    ret = SendMessage(hWnd, WM_GETTEXT, sizeof(buff), (LPARAM)buff);
+    ok(strcmp(buff, a_str) != 0, "Expected text not to change, got %s\n", buff);
 
-    DestroyWindow(hWndDateTime);
-    ok_sequence(sequences, DATETIME_SEQ_INDEX, destroy_window_seq, "test_dtm_set_and_get_system_time", TRUE);
+    GetDateFormat(LOCALE_USER_DEFAULT, 0, NULL, NULL, time, sizeof(time));
+    ok(!strcmp(buff, time), "Expected %s, got %s\n", time, buff);
+
+    DestroyWindow(hWnd);
+}
+
+static void test_dts_shownone(void)
+{
+    HWND hwnd;
+    DWORD style;
+
+    /* it isn't allowed to change DTS_SHOWNONE after creation */
+    hwnd = create_datetime_control(0);
+    style = GetWindowLong(hwnd, GWL_STYLE);
+    SetWindowLong(hwnd, GWL_STYLE, style | DTS_SHOWNONE);
+    style = GetWindowLong(hwnd, GWL_STYLE);
+    ok(!(style & DTS_SHOWNONE), "Expected DTS_SHOWNONE not to be set\n");
+    DestroyWindow(hwnd);
+
+    hwnd = create_datetime_control(DTS_SHOWNONE);
+    style = GetWindowLong(hwnd, GWL_STYLE);
+    SetWindowLong(hwnd, GWL_STYLE, style & ~DTS_SHOWNONE);
+    style = GetWindowLong(hwnd, GWL_STYLE);
+    ok(style & DTS_SHOWNONE, "Expected DTS_SHOWNONE to be set\n");
+    DestroyWindow(hwnd);
 }
 
 START_TEST(datetime)
@@ -592,5 +721,13 @@ START_TEST(datetime)
 
     init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
 
-    test_datetime_control();
+    test_dtm_set_format();
+    test_dtm_set_and_get_mccolor();
+    test_dtm_set_and_get_mcfont();
+    test_dtm_get_monthcal();
+    test_dtm_set_and_get_range();
+    test_dtm_set_range_swap_min_max();
+    test_dtm_set_and_get_system_time();
+    test_wm_set_get_text();
+    test_dts_shownone();
 }
index 221bcba..419c1b1 100644 (file)
 
 #include "wine/test.h"
 
-#define DPAM_NOSORT 0x1
-#define DPAM_INSERT 0x4
-#define DPAM_DELETE 0x8
+#define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
 
-typedef struct _ITEMDATA
+typedef struct _STREAMDATA
 {
-    INT   iPos;
-    PVOID pvData;
-} ITEMDATA, *LPITEMDATA;
-
-typedef HRESULT (CALLBACK *PFNDPASTM)(LPITEMDATA,IStream*,LPARAM);
+    DWORD dwSize;
+    DWORD dwData2;
+    DWORD dwItems;
+} STREAMDATA, *PSTREAMDATA;
 
 static HDPA    (WINAPI *pDPA_Clone)(const HDPA,const HDPA);
 static HDPA    (WINAPI *pDPA_Create)(INT);
@@ -55,9 +52,9 @@ static INT     (WINAPI *pDPA_GetPtr)(const HDPA,INT);
 static INT     (WINAPI *pDPA_GetPtrIndex)(const HDPA,PVOID);
 static BOOL    (WINAPI *pDPA_Grow)(HDPA,INT);
 static INT     (WINAPI *pDPA_InsertPtr)(const HDPA,INT,PVOID);
-static HRESULT (WINAPI *pDPA_LoadStream)(HDPA*,PFNDPASTM,IStream*,LPARAM);
+static HRESULT (WINAPI *pDPA_LoadStream)(HDPA*,PFNDPASTREAM,IStream*,LPVOID);
 static BOOL    (WINAPI *pDPA_Merge)(const HDPA,const HDPA,DWORD,PFNDPACOMPARE,PFNDPAMERGE,LPARAM);
-static HRESULT (WINAPI *pDPA_SaveStream)(HDPA,PFNDPASTM,IStream*,LPARAM);
+static HRESULT (WINAPI *pDPA_SaveStream)(HDPA,PFNDPASTREAM,IStream*,LPVOID);
 static INT     (WINAPI *pDPA_Search)(HDPA,PVOID,INT,PFNDPACOMPARE,LPARAM,UINT);
 static BOOL    (WINAPI *pDPA_SetPtr)(const HDPA,INT,PVOID);
 static BOOL    (WINAPI *pDPA_Sort)(const HDPA,PFNDPACOMPARE,LPARAM);
@@ -109,14 +106,22 @@ static INT CALLBACK CB_CmpGT(PVOID p1, PVOID p2, LPARAM lp)
     return p1 > p2 ? -1 : p1 < p2 ? 1 : 0;
 }
 
+/* merge callback messages counter
+   DPAMM_MERGE     1
+   DPAMM_DELETE    2
+   DPAMM_INSERT    3  */
+static INT nMessages[4];
+
 static PVOID CALLBACK CB_MergeInsertSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp)
 {
+    nMessages[op]++;
     ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
     return p1;
 }        
 
 static PVOID CALLBACK CB_MergeDeleteOddSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp)
 {
+    nMessages[op]++;
     ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
     return ((PCHAR)p2)+1;
 }
@@ -134,30 +139,30 @@ static INT CALLBACK CB_EnumFirstThree(PVOID pItem, PVOID lp)
     return pItem != (PVOID)3;
 }
 
-static HRESULT CALLBACK CB_Save(LPITEMDATA pInfo, IStream *pStm, LPARAM lp)
+static HRESULT CALLBACK CB_Save(DPASTREAMINFO *pInfo, IStream *pStm, LPVOID lp)
 {
     HRESULT hRes;
-    
-    ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
+
+    ok(lp == (LPVOID)0xdeadbeef, "lp=%p\n", lp);
     hRes = IStream_Write(pStm, &pInfo->iPos, sizeof(INT), NULL);
-    ok(hRes == S_OK, "hRes=0x%x\n", hRes);
-    hRes = IStream_Write(pStm, &pInfo->pvData, sizeof(PVOID), NULL);
-    ok(hRes == S_OK, "hRes=0x%x\n", hRes);
+    expect(S_OK, hRes);
+    hRes = IStream_Write(pStm, &pInfo->pvItem, sizeof(PVOID), NULL);
+    expect(S_OK, hRes);
     return S_OK;
 }
 
-static HRESULT CALLBACK CB_Load(LPITEMDATA pInfo, IStream *pStm, LPARAM lp)
+static HRESULT CALLBACK CB_Load(DPASTREAMINFO *pInfo, IStream *pStm, LPVOID lp)
 {
     HRESULT hRes;
     INT iOldPos;
     
     iOldPos = pInfo->iPos;
-    ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
+    ok(lp == (LPVOID)0xdeadbeef, "lp=%p\n", lp);
     hRes = IStream_Read(pStm, &pInfo->iPos, sizeof(INT), NULL);
-    ok(hRes == S_OK, "hRes=0x%x\n", hRes);
+    expect(S_OK, hRes);
     ok(pInfo->iPos == iOldPos, "iPos=%d iOldPos=%d\n", pInfo->iPos, iOldPos);
-    hRes = IStream_Read(pStm, &pInfo->pvData, sizeof(PVOID), NULL);
-    ok(hRes == S_OK, "hRes=0x%x\n", hRes);
+    hRes = IStream_Read(pStm, &pInfo->pvItem, sizeof(PVOID), NULL);
+    expect(S_OK, hRes);
     return S_OK;
 }
 
@@ -200,7 +205,6 @@ static void test_dpa(void)
     INT ret, i;
     PVOID p;
     DWORD dw, dw2, dw3;
-    HRESULT hRes;
     BOOL rc;
     
     GetSystemInfo(&si);
@@ -209,9 +213,9 @@ static void test_dpa(void)
     dpa3 = pDPA_CreateEx(0, hHeap);
     ok(dpa3 != NULL, "\n");
     ret = pDPA_Grow(dpa3, si.dwPageSize + 1);
-    todo_wine ok(!ret && GetLastError() == ERROR_NOT_ENOUGH_MEMORY, 
+    ok(!ret && GetLastError() == ERROR_NOT_ENOUGH_MEMORY,
        "ret=%d error=%d\n", ret, GetLastError());
-        
+
     dpa = pDPA_Create(0);
     ok(dpa != NULL, "\n");
 
@@ -288,9 +292,9 @@ static void test_dpa(void)
         ok(j == DPA_ERR, "j=%d\n", j);
         /* ... but for a binary search it's ignored */
         j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, i+1, CB_CmpLT, 0xdeadbeef, DPAS_SORTED);
-        todo_wine ok(j+1 == i, "j=%d i=%d\n", j, i);
+        ok(j+1 == i, "j=%d i=%d\n", j, i);
     }
-    
+
     /* Try to get the index of a nonexistent item */
     i = pDPA_GetPtrIndex(dpa, (PVOID)7);
     ok(i == DPA_ERR, "i=%d\n", i);
@@ -335,49 +339,6 @@ static void test_dpa(void)
         ok(j != i, "i=%d\n", i);
     }
 
-    if(pDPA_Merge)
-    {
-        /* Delete all even entries from dpa */
-        p = pDPA_DeletePtr(dpa, 1);
-        p = pDPA_DeletePtr(dpa, 2);
-        p = pDPA_DeletePtr(dpa, 3);
-        rc=CheckDPA(dpa, 0x135, &dw);
-        ok(rc, "dw=0x%x\n", dw);
-    
-        /* Delete all odd entries from dpa2 */
-        pDPA_Merge(dpa2, dpa, DPAM_DELETE, 
-                   CB_CmpLT, CB_MergeDeleteOddSrc, 0xdeadbeef);
-        todo_wine
-        {
-            rc=CheckDPA(dpa2, 0x246, &dw2);
-            ok(rc, "dw=0x%x\n", dw2);
-        }
-    
-        /* Merge dpa3 into dpa2 and dpa */
-        pDPA_Merge(dpa, dpa3, DPAM_INSERT|DPAM_NOSORT, 
-                   CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
-        pDPA_Merge(dpa2, dpa3, DPAM_INSERT|DPAM_NOSORT, 
-                   CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
-    
-        rc=CheckDPA(dpa,  0x123456, &dw);
-        ok(rc, "dw=0x%x\n",  dw);
-        rc=CheckDPA(dpa2, 0x123456, &dw2);
-        ok(rc ||
-           broken(!rc), /* win98 */
-           "dw2=0x%x\n", dw2);
-        rc=CheckDPA(dpa3, 0x123456, &dw3);
-        ok(rc, "dw3=0x%x\n", dw3);
-    }
-
-    if(pDPA_EnumCallback)
-    {
-        nEnum = 0;
-        pDPA_EnumCallback(dpa2, CB_EnumFirstThree, dpa2);
-        rc=CheckDPA(dpa2, 0x777456, &dw2);
-        ok(rc, "dw=0x%x\n", dw2);
-        ok(nEnum == 3, "nEnum=%d\n", nEnum);
-    }
-    
     /* Setting item with huge index should work */
     ok(pDPA_SetPtr(dpa2, 0x12345, (PVOID)0xdeadbeef), "\n");
     ret = pDPA_GetPtrIndex(dpa2, (PVOID)0xdeadbeef);
@@ -386,62 +347,397 @@ static void test_dpa(void)
     pDPA_DeleteAllPtrs(dpa2);
     rc=CheckDPA(dpa2, 0, &dw2);
     ok(rc, "dw2=0x%x\n", dw2);
+
+    pDPA_Destroy(dpa);
     pDPA_Destroy(dpa2);
+    pDPA_Destroy(dpa3);
+}
+
+static void test_DPA_Merge(void)
+{
+    HDPA dpa, dpa2, dpa3;
+    INT ret, i;
+    DWORD dw;
+    BOOL rc;
+
+    if(!pDPA_Merge)
+    {
+        win_skip("DPA_Merge() not available\n");
+        return;
+    }
+
+    dpa  = pDPA_Create(0);
+    dpa2 = pDPA_Create(0);
+    dpa3 = pDPA_Create(0);
+
+    ret = pDPA_InsertPtr(dpa, 0, (PVOID)1);
+    ok(ret == 0, "ret=%d\n", ret);
+    ret = pDPA_InsertPtr(dpa, 1, (PVOID)3);
+    ok(ret == 1, "ret=%d\n", ret);
+    ret = pDPA_InsertPtr(dpa, 2, (PVOID)5);
+    ok(ret == 2, "ret=%d\n", ret);
+
+    rc = CheckDPA(dpa, 0x135, &dw);
+    ok(rc, "dw=0x%x\n", dw);
+
+    for (i = 0; i < 6; i++)
+    {
+        ret = pDPA_InsertPtr(dpa2, i, (PVOID)(INT_PTR)(6-i));
+        ok(ret == i, "ret=%d\n", ret);
+        ret = pDPA_InsertPtr(dpa3, i, (PVOID)(INT_PTR)(i+1));
+        ok(ret == i, "ret=%d\n", ret);
+    }
+
+    rc = CheckDPA(dpa2, 0x654321, &dw);
+    ok(rc, "dw=0x%x\n", dw);
+    rc = CheckDPA(dpa3, 0x123456, &dw);
+    ok(rc, "dw=0x%x\n", dw);
+
+    /* Delete all odd entries from dpa2 */
+    memset(nMessages, 0, sizeof(nMessages));
+    pDPA_Merge(dpa2, dpa, DPAM_INTERSECT,
+               CB_CmpLT, CB_MergeDeleteOddSrc, 0xdeadbeef);
+    rc = CheckDPA(dpa2, 0x246, &dw);
+    ok(rc, "dw=0x%x\n", dw);
+
+    expect(3, nMessages[DPAMM_MERGE]);
+    expect(3, nMessages[DPAMM_DELETE]);
+    expect(0, nMessages[DPAMM_INSERT]);
+
+    for (i = 0; i < 6; i++)
+    {
+        ret = pDPA_InsertPtr(dpa2, i, (PVOID)(INT_PTR)(6-i));
+        ok(ret == i, "ret=%d\n", ret);
+    }
+
+    /* DPAM_INTERSECT - returning source while merging */
+    memset(nMessages, 0, sizeof(nMessages));
+    pDPA_Merge(dpa2, dpa, DPAM_INTERSECT,
+               CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
+    rc = CheckDPA(dpa2, 0x135, &dw);
+    ok(rc, "dw=0x%x\n", dw);
+
+    expect(3, nMessages[DPAMM_MERGE]);
+    expect(6, nMessages[DPAMM_DELETE]);
+    expect(0, nMessages[DPAMM_INSERT]);
+
+    /* DPAM_UNION */
+    pDPA_DeleteAllPtrs(dpa);
+    pDPA_InsertPtr(dpa, 0, (PVOID)1);
+    pDPA_InsertPtr(dpa, 1, (PVOID)3);
+    pDPA_InsertPtr(dpa, 2, (PVOID)5);
+    pDPA_DeleteAllPtrs(dpa2);
+    pDPA_InsertPtr(dpa2, 0, (PVOID)2);
+    pDPA_InsertPtr(dpa2, 1, (PVOID)4);
+    pDPA_InsertPtr(dpa2, 2, (PVOID)6);
+
+    memset(nMessages, 0, sizeof(nMessages));
+    pDPA_Merge(dpa2, dpa, DPAM_UNION,
+               CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
+    rc = CheckDPA(dpa2, 0x123456, &dw);
+    ok(rc ||
+       broken(!rc && dw == 0x23456), /* 4.7x */
+       "dw=0x%x\n", dw);
+
+    expect(0, nMessages[DPAMM_MERGE]);
+    expect(0, nMessages[DPAMM_DELETE]);
+    ok(nMessages[DPAMM_INSERT] == 3 ||
+       broken(nMessages[DPAMM_INSERT] == 2), /* 4.7x */
+       "Expected 3, got %d\n", nMessages[DPAMM_INSERT]);
+
+    /* Merge dpa3 into dpa2 and dpa */
+    memset(nMessages, 0, sizeof(nMessages));
+    pDPA_Merge(dpa, dpa3, DPAM_UNION|DPAM_SORTED,
+               CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
+    expect(3, nMessages[DPAMM_MERGE]);
+    expect(0, nMessages[DPAMM_DELETE]);
+    expect(3, nMessages[DPAMM_INSERT]);
+
+
+    pDPA_DeleteAllPtrs(dpa2);
+    pDPA_InsertPtr(dpa2, 0, (PVOID)2);
+    pDPA_InsertPtr(dpa2, 1, (PVOID)4);
+    pDPA_InsertPtr(dpa2, 2, (PVOID)6);
+
+    memset(nMessages, 0, sizeof(nMessages));
+    pDPA_Merge(dpa2, dpa3, DPAM_UNION|DPAM_SORTED,
+               CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
+    expect(3, nMessages[DPAMM_MERGE]);
+    expect(0, nMessages[DPAMM_DELETE]);
+    ok(nMessages[DPAMM_INSERT] == 3 ||
+       broken(nMessages[DPAMM_INSERT] == 2), /* 4.7x */
+       "Expected 3, got %d\n", nMessages[DPAMM_INSERT]);
+
+    rc = CheckDPA(dpa,  0x123456, &dw);
+    ok(rc, "dw=0x%x\n",  dw);
+    rc = CheckDPA(dpa2, 0x123456, &dw);
+    ok(rc ||
+       broken(!rc), /* win98 */
+       "dw=0x%x\n", dw);
+    rc = CheckDPA(dpa3, 0x123456, &dw);
+    ok(rc, "dw=0x%x\n", dw);
+
+    pDPA_Destroy(dpa);
+    pDPA_Destroy(dpa2);
+    pDPA_Destroy(dpa3);
+}
+
+static void test_DPA_EnumCallback(void)
+{
+    HDPA dpa;
+    BOOL rc;
+    DWORD dw;
+    INT i, ret;
 
-    if(pDPA_DestroyCallback)
+    if(!pDPA_EnumCallback)
     {
-        nEnum = 0;
-        pDPA_DestroyCallback(dpa3, CB_EnumFirstThree, dpa3);
-        ok(nEnum == 3, "nEnum=%d\n", nEnum);
+        win_skip("DPA_EnumCallback() not available\n");
+        return;
     }
-    else pDPA_Destroy(dpa3);
+
+    dpa = pDPA_Create(0);
+
+    for (i = 0; i < 6; i++)
+    {
+        ret = pDPA_InsertPtr(dpa, i, (PVOID)(INT_PTR)(i+1));
+        ok(ret == i, "ret=%d\n", ret);
+    }
+
+    rc = CheckDPA(dpa, 0x123456, &dw);
+    ok(rc, "dw=0x%x\n", dw);
+
+    nEnum = 0;
+    /* test callback sets first 3 items to 7 */
+    pDPA_EnumCallback(dpa, CB_EnumFirstThree, dpa);
+    rc = CheckDPA(dpa, 0x777456, &dw);
+    ok(rc, "dw=0x%x\n", dw);
+    ok(nEnum == 3, "nEnum=%d\n", nEnum);
+
+    pDPA_Destroy(dpa);
+}
+
+static void test_DPA_DestroyCallback(void)
+{
+    HDPA dpa;
+    INT i, ret;
+
+    if(!pDPA_DestroyCallback)
+    {
+        win_skip("DPA_DestroyCallback() not available\n");
+        return;
+    }
+
+    dpa = pDPA_Create(0);
+
+    for (i = 0; i < 3; i++)
+    {
+        ret = pDPA_InsertPtr(dpa, i, (PVOID)(INT_PTR)(i+1));
+        ok(ret == i, "ret=%d\n", ret);
+    }
+
+    nEnum = 0;
+    pDPA_DestroyCallback(dpa, CB_EnumFirstThree, dpa);
+    ok(nEnum == 3, "nEnum=%d\n", nEnum);
+}
+
+static void test_DPA_LoadStream(void)
+{
+    static const WCHAR szStg[] = { 'S','t','g',0 };
+    IStorage* pStg = NULL;
+    IStream* pStm = NULL;
+    LARGE_INTEGER li;
+    ULARGE_INTEGER uli;
+    DWORD dwMode;
+    HRESULT hRes;
+    STREAMDATA header;
+    ULONG written, ret;
+    HDPA dpa;
+
+    if(!pDPA_LoadStream)
+    {
+        win_skip("DPA_LoadStream() not available. Skipping stream tests.\n");
+        return;
+    }
+
+    hRes = CoInitialize(NULL);
+    if (hRes != S_OK)
+    {
+        ok(0, "hResult: %d\n", hRes);
+        return;
+    }
+
+    dwMode = STGM_DIRECT|STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE;
+    hRes = StgCreateDocfile(NULL, dwMode|STGM_DELETEONRELEASE, 0, &pStg);
+    expect(S_OK, hRes);
+
+    hRes = IStorage_CreateStream(pStg, szStg, dwMode, 0, 0, &pStm);
+    expect(S_OK, hRes);
+
+    /* write less than header size */
+    li.QuadPart = 0;
+    hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
+    expect(S_OK, hRes);
+
+    memset(&header, 0, sizeof(header));
+    written = 0;
+    uli.QuadPart = sizeof(header)-1;
+    hRes = IStream_SetSize(pStm, uli);
+    expect(S_OK, hRes);
+    hRes = IStream_Write(pStm, &header, sizeof(header)-1, &written);
+    expect(S_OK, hRes);
+    written -= sizeof(header)-1;
+    expect(0, written);
+
+    li.QuadPart = 0;
+    hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
+    expect(S_OK, hRes);
+
+    hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, NULL);
+    expect(E_FAIL, hRes);
+
+    /* check stream position after header read failed */
+    li.QuadPart = 0;
+    uli.QuadPart = 1;
+    hRes = IStream_Seek(pStm, li, STREAM_SEEK_CUR, &uli);
+    expect(S_OK, hRes);
+    ok(uli.QuadPart == 0, "Expected to position reset\n");
+
+    /* write valid header for empty DPA */
+    header.dwSize = sizeof(header);
+    header.dwData2 = 1;
+    header.dwItems = 0;
+    written = 0;
+
+    li.QuadPart = 0;
+    hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
+    expect(S_OK, hRes);
+
+    uli.QuadPart = sizeof(header);
+    hRes = IStream_SetSize(pStm, uli);
+    expect(S_OK, hRes);
+
+    hRes = IStream_Write(pStm, &header, sizeof(header), &written);
+    expect(S_OK, hRes);
+    written -= sizeof(header);
+    expect(0, written);
+
+    li.QuadPart = 0;
+    hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
+    expect(S_OK, hRes);
+
+    dpa = NULL;
+    hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, NULL);
+    expect(S_OK, hRes);
+    DPA_Destroy(dpa);
+
+    /* try with altered dwData2 field */
+    header.dwSize = sizeof(header);
+    header.dwData2 = 2;
+    header.dwItems = 0;
+
+    li.QuadPart = 0;
+    hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
+    expect(S_OK, hRes);
+    hRes = IStream_Write(pStm, &header, sizeof(header), &written);
+    expect(S_OK, hRes);
+    written -= sizeof(header);
+    expect(0, written);
+
+    li.QuadPart = 0;
+    hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
+    expect(S_OK, hRes);
+
+    hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, (void*)0xdeadbeef);
+    expect(E_FAIL, hRes);
+
+    ret = IStream_Release(pStm);
+    ok(!ret, "ret=%d\n", ret);
+
+    ret = IStorage_Release(pStg);
+    ok(!ret, "ret=%d\n", ret);
+
+    CoUninitialize();
+}
+
+static void test_DPA_SaveStream(void)
+{
+    HDPA dpa;
+    static const WCHAR szStg[] = { 'S','t','g',0 };
+    IStorage* pStg = NULL;
+    IStream* pStm = NULL;
+    DWORD dwMode, dw;
+    HRESULT hRes;
+    ULONG ret;
+    INT i;
+    BOOL rc;
+    LARGE_INTEGER liZero;
 
     if(!pDPA_SaveStream)
-        goto skip_stream_tests;
+    {
+        win_skip("DPA_SaveStream() not available. Skipping stream tests.\n");
+        return;
+    }
 
     hRes = CoInitialize(NULL);
-    if(hRes == S_OK)
+    if (hRes != S_OK)
     {
-        static const WCHAR szStg[] = { 'S','t','g',0 };
-        IStorage* pStg = NULL;
-        IStream* pStm = NULL;
-        LARGE_INTEGER liZero;
-        DWORD dwMode;
-        liZero.QuadPart = 0;
-
-        dwMode = STGM_DIRECT|STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE;
-        hRes = StgCreateDocfile(NULL, dwMode|STGM_DELETEONRELEASE, 0, &pStg);
-        ok(hRes == S_OK, "hRes=0x%x\n", hRes);
-
-        hRes = IStorage_CreateStream(pStg, szStg, dwMode, 0, 0, &pStm);
-        ok(hRes == S_OK, "hRes=0x%x\n", hRes);
-
-        hRes = pDPA_SaveStream(dpa, CB_Save, pStm, 0xdeadbeef);
-        todo_wine ok(hRes == S_OK, "hRes=0x%x\n", hRes);
-        pDPA_Destroy(dpa);
-        
-        hRes = IStream_Seek(pStm, liZero, STREAM_SEEK_SET, NULL);
-        ok(hRes == S_OK, "hRes=0x%x\n", hRes);
-        hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, 0xdeadbeef);
-        todo_wine
-        {
-            ok(hRes == S_OK, "hRes=0x%x\n", hRes);
-            rc=CheckDPA(dpa, 0x123456, &dw);
-            ok(rc, "dw=0x%x\n", dw);
-        }
+        ok(0, "hResult: %d\n", hRes);
+        return;
+    }
 
-        ret = IStream_Release(pStm);
-        ok(!ret, "ret=%d\n", ret);
-       
-        ret = IStorage_Release(pStg);
-        ok(!ret, "ret=%d\n", ret);
+    dwMode = STGM_DIRECT|STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE;
+    hRes = StgCreateDocfile(NULL, dwMode|STGM_DELETEONRELEASE, 0, &pStg);
+    expect(S_OK, hRes);
 
-        CoUninitialize();
+    hRes = IStorage_CreateStream(pStg, szStg, dwMode, 0, 0, &pStm);
+    expect(S_OK, hRes);
+
+    dpa = pDPA_Create(0);
+
+    /* simple parameter check */
+    hRes = pDPA_SaveStream(dpa, NULL, pStm, NULL);
+    ok(hRes == E_INVALIDARG ||
+       broken(hRes == S_OK) /* XP and below */, "Wrong result, %d\n", hRes);
+if (0) {
+    /* crashes on XP */
+    hRes = pDPA_SaveStream(NULL, CB_Save, pStm, NULL);
+    expect(E_INVALIDARG, hRes);
+
+    hRes = pDPA_SaveStream(dpa, CB_Save, NULL, NULL);
+    expect(E_INVALIDARG, hRes);
+}
+
+    /* saving/loading */
+    for (i = 0; i < 6; i++)
+    {
+        ret = pDPA_InsertPtr(dpa, i, (PVOID)(INT_PTR)(i+1));
+        ok(ret == i, "ret=%d\n", ret);
     }
-    else ok(0, "hResult: %d\n", hRes);
 
-skip_stream_tests:
+    liZero.QuadPart = 0;
+    hRes = IStream_Seek(pStm, liZero, STREAM_SEEK_SET, NULL);
+    expect(S_OK, hRes);
+
+    hRes = pDPA_SaveStream(dpa, CB_Save, pStm, (void*)0xdeadbeef);
+    expect(S_OK, hRes);
+    pDPA_Destroy(dpa);
+
+    liZero.QuadPart = 0;
+    hRes = IStream_Seek(pStm, liZero, STREAM_SEEK_SET, NULL);
+    expect(S_OK, hRes);
+    hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, (void*)0xdeadbeef);
+    expect(S_OK, hRes);
+    rc = CheckDPA(dpa, 0x123456, &dw);
+    ok(rc, "dw=0x%x\n", dw);
     pDPA_Destroy(dpa);
+
+    ret = IStream_Release(pStm);
+    ok(!ret, "ret=%d\n", ret);
+
+    ret = IStorage_Release(pStg);
+    ok(!ret, "ret=%d\n", ret);
+
+    CoUninitialize();
 }
 
 START_TEST(dpa)
@@ -450,8 +746,16 @@ START_TEST(dpa)
 
     hcomctl32 = GetModuleHandleA("comctl32.dll");
 
-    if(InitFunctionPtrs(hcomctl32))
-        test_dpa();
-    else
+    if(!InitFunctionPtrs(hcomctl32))
+    {
         win_skip("Needed functions are not available\n");
+        return;
+    }
+
+    test_dpa();
+    test_DPA_Merge();
+    test_DPA_EnumCallback();
+    test_DPA_DestroyCallback();
+    test_DPA_LoadStream();
+    test_DPA_SaveStream();
 }
index 7661253..d9b4a73 100644 (file)
@@ -24,6 +24,7 @@
 #include <assert.h>
 
 #include "wine/test.h"
+#include "v6util.h"
 #include "msg.h"
 
 typedef struct tagEXPECTEDNOTIFY
@@ -121,7 +122,6 @@ static const struct message deleteItem_getItemCount_seq[] = {
 };
 
 static const struct message orderArray_seq[] = {
-    { HDM_GETITEMCOUNT, sent },
     { HDM_SETORDERARRAY, sent|wparam, 2 },
     { HDM_GETORDERARRAY, sent|wparam, 2 },
     { 0 }
@@ -244,7 +244,7 @@ static LONG addItem(HWND hdex, int idx, LPSTR text)
     hdItem.cxy        = 100;
     hdItem.pszText    = text;
     hdItem.cchTextMax = 0;
-    return (LONG)SendMessage(hdex, HDM_INSERTITEMA, (WPARAM)idx, (LPARAM)&hdItem);
+    return SendMessage(hdex, HDM_INSERTITEMA, idx, (LPARAM)&hdItem);
 }
 
 static LONG setItem(HWND hdex, int idx, LPSTR text, BOOL fCheckNotifies)
@@ -259,7 +259,7 @@ static LONG setItem(HWND hdex, int idx, LPSTR text, BOOL fCheckNotifies)
         expect_notify(HDN_ITEMCHANGINGA, FALSE, &hdexItem);
         expect_notify(HDN_ITEMCHANGEDA, FALSE, &hdexItem);
     }
-    ret = (LONG)SendMessage(hdex, HDM_SETITEMA, (WPARAM)idx, (LPARAM)&hdexItem);
+    ret = SendMessage(hdex, HDM_SETITEMA, idx, (LPARAM)&hdexItem);
     if (fCheckNotifies)
         ok(notifies_received(), "setItem(): not all expected notifies were received\n");
     return ret;
@@ -279,19 +279,19 @@ static LONG setItemUnicodeNotify(HWND hdex, int idx, LPSTR text, LPWSTR wText)
     
     expect_notify(HDN_ITEMCHANGINGW, TRUE, (HDITEMA*)&hdexNotify);
     expect_notify(HDN_ITEMCHANGEDW, TRUE, (HDITEMA*)&hdexNotify);
-    ret = (LONG)SendMessage(hdex, HDM_SETITEMA, (WPARAM)idx, (LPARAM)&hdexItem);
+    ret = SendMessage(hdex, HDM_SETITEMA, idx, (LPARAM)&hdexItem);
     ok(notifies_received(), "setItemUnicodeNotify(): not all expected notifies were received\n");
     return ret;
 }
 
 static LONG delItem(HWND hdex, int idx)
 {
-    return (LONG)SendMessage(hdex, HDM_DELETEITEM, (WPARAM)idx, 0);
+    return SendMessage(hdex, HDM_DELETEITEM, idx, 0);
 }
 
 static LONG getItemCount(HWND hdex)
 {
-    return (LONG)SendMessage(hdex, HDM_GETITEMCOUNT, 0, 0);
+    return SendMessage(hdex, HDM_GETITEMCOUNT, 0, 0);
 }
 
 static LONG getItem(HWND hdex, int idx, LPSTR textBuffer)
@@ -300,7 +300,7 @@ static LONG getItem(HWND hdex, int idx, LPSTR textBuffer)
     hdItem.mask         = HDI_TEXT;
     hdItem.pszText      = textBuffer;
     hdItem.cchTextMax   = MAX_CHARS;
-    return (LONG)SendMessage(hdex, HDM_GETITEMA, (WPARAM)idx, (LPARAM)&hdItem);
+    return SendMessage(hdex, HDM_GETITEMA, idx, (LPARAM)&hdItem);
 }
 
 static void addReadDelItem(HWND hdex, HDITEMA *phdiCreate, int maskRead, HDITEMA *phdiRead)
@@ -396,14 +396,9 @@ static WCHAR pszUniTestW[] = {'T','S','T',0};
     ok(res == i, "Got Item Count as %d\n", res);\
 }
 
-struct subclass_info
-{
-    WNDPROC oldproc;
-};
-
 static LRESULT WINAPI header_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
-    struct subclass_info *info = (struct subclass_info *)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
+    WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
     static LONG defwndproc_counter = 0;
     LRESULT ret;
     struct message msg;
@@ -417,7 +412,7 @@ static LRESULT WINAPI header_subclass_proc(HWND hwnd, UINT message, WPARAM wPara
     add_message(sequences, HEADER_SEQ_INDEX, &msg);
 
     defwndproc_counter++;
-    ret = CallWindowProcA(info->oldproc, hwnd, message, wParam, lParam);
+    ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
     defwndproc_counter--;
 
     return ret;
@@ -487,7 +482,7 @@ static HWND create_custom_parent_window(void)
 
 static HWND create_custom_header_control(HWND hParent, BOOL preloadHeaderItems)
 {
-    struct subclass_info *info;
+    WNDPROC oldproc;
     HWND childHandle;
     HDLAYOUT hlayout;
     RECT rectwin;
@@ -505,9 +500,6 @@ static HWND create_custom_header_control(HWND hParent, BOOL preloadHeaderItems)
 
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
-    info = HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info));
-    if (!info)
-         return NULL;
 
     childHandle = CreateWindowEx(0, WC_HEADER, NULL,
                            WS_CHILD|WS_BORDER|WS_VISIBLE|HDS_BUTTONS|HDS_HORZ,
@@ -534,9 +526,9 @@ static HWND create_custom_header_control(HWND hParent, BOOL preloadHeaderItems)
     SetWindowPos(childHandle, winpos.hwndInsertAfter, winpos.x, winpos.y,
                  winpos.cx, winpos.cy, 0);
 
-    info->oldproc = (WNDPROC)SetWindowLongPtrA(childHandle, GWLP_WNDPROC,
-                                               (LONG_PTR)header_subclass_proc);
-    SetWindowLongPtrA(childHandle, GWLP_USERDATA, (LONG_PTR)info);
+    oldproc = (WNDPROC)SetWindowLongPtrA(childHandle, GWLP_WNDPROC,
+                                         (LONG_PTR)header_subclass_proc);
+    SetWindowLongPtrA(childHandle, GWLP_USERDATA, (LONG_PTR)oldproc);
     return childHandle;
 }
 
@@ -756,10 +748,10 @@ static void test_header_control (void)
         TEST_GET_ITEM(i, 4);
         TEST_GET_ITEMCOUNT(6);
     }
-    
-    SendMessageA(hWndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, 0);
+
+    SendMessageA(hWndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
     setItemUnicodeNotify(hWndHeader, 3, pszUniTestA, pszUniTestW);
-    SendMessageA(hWndHeader, WM_NOTIFYFORMAT, (WPARAM)hHeaderParentWnd, (LPARAM)NF_REQUERY);
+    SendMessageA(hWndHeader, WM_NOTIFYFORMAT, (WPARAM)hHeaderParentWnd, NF_REQUERY);
     setItem(hWndHeader, 3, str_items[4], TRUE);
     
     dont_expect_notify(HDN_GETDISPINFOA);
@@ -820,10 +812,8 @@ static void test_hdm_getitemrect(HWND hParent)
     expect(80, rect.left);
     expect(0, rect.top);
     expect(160, rect.right);
-    todo_wine
-    {
-      expect(g_customheight, rect.bottom);
-    }
+    expect(g_customheight, rect.bottom);
+
     retVal = SendMessage(hChild, HDM_GETITEMRECT, 0, (LPARAM) &rect);
 
     ok(retVal == TRUE, "Getting item rect should TRUE, got %d\n", retVal);
@@ -832,10 +822,8 @@ static void test_hdm_getitemrect(HWND hParent)
     expect(0, rect.top);
 
     expect(80, rect.right);
-    todo_wine
-    {
-      expect(g_customheight, rect.bottom);
-    }
+    expect(g_customheight, rect.bottom);
+
     retVal = SendMessage(hChild, HDM_GETITEMRECT, 10, (LPARAM) &rect);
     ok(retVal == 0, "Getting rect of nonexistent item should return 0, got %d\n", retVal);
 
@@ -907,30 +895,25 @@ static void test_hdm_hittest(HWND hParent)
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
     retVal = SendMessage(hChild, HDM_HITTEST, 0, (LPARAM) &hdHitTestInfo);
-    todo_wine
-    {
-      expect(0, retVal);
-      expect(0, hdHitTestInfo.iItem);
-    }
+    expect(0, retVal);
+    expect(0, hdHitTestInfo.iItem);
+    expect(HHT_ONDIVIDER, hdHitTestInfo.flags);
 
     pt.x = secondItemRightBoundary - 1;
     pt.y = bottomBoundary - 1;
     hdHitTestInfo.pt = pt;
     retVal = SendMessage(hChild, HDM_HITTEST, 1, (LPARAM) &hdHitTestInfo);
-    todo_wine
-    {
-      expect(1, retVal);
-    }
+    expect(1, retVal);
     expect(1, hdHitTestInfo.iItem);
+    expect(HHT_ONDIVIDER, hdHitTestInfo.flags);
 
     pt.x = secondItemRightBoundary;
     pt.y = bottomBoundary + 1;
     hdHitTestInfo.pt = pt;
-    todo_wine
-    {
-     retVal = SendMessage(hChild, HDM_HITTEST, 0, (LPARAM) &hdHitTestInfo);
-     expect(-1, retVal);
-    }
+    retVal = SendMessage(hChild, HDM_HITTEST, 0, (LPARAM) &hdHitTestInfo);
+    expect(-1, retVal);
+    expect(-1, hdHitTestInfo.iItem);
+    expect(HHT_BELOW, hdHitTestInfo.flags);
 
     ok_sequence(sequences, HEADER_SEQ_INDEX, hittest_seq, "hittest sequence testing", FALSE);
 
@@ -951,11 +934,9 @@ static void test_hdm_sethotdivider(HWND hParent)
                                     "adder header control to parent", FALSE);
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
-    todo_wine
-    {
-        retVal = SendMessage(hChild, HDM_SETHOTDIVIDER, TRUE, 0X00050005);
-        expect(0, retVal);
-    }
+    retVal = SendMessage(hChild, HDM_SETHOTDIVIDER, TRUE, MAKELPARAM(5, 5));
+    expect(0, retVal);
+
     retVal = SendMessage(hChild, HDM_SETHOTDIVIDER, FALSE, 100);
     expect(100, retVal);
     retVal = SendMessage(hChild, HDM_SETHOTDIVIDER, FALSE, 1);
@@ -973,7 +954,7 @@ static void test_hdm_sethotdivider(HWND hParent)
 static void test_hdm_imageMessages(HWND hParent)
 {
     HIMAGELIST hImageList = ImageList_Create (4, 4, 0, 1, 0);
-    HIMAGELIST hImageListRetVal;
+    HIMAGELIST hIml;
     HWND hChild;
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
@@ -983,14 +964,15 @@ static void test_hdm_imageMessages(HWND hParent)
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
-    hImageListRetVal = (HIMAGELIST) SendMessage(hChild, HDM_SETIMAGELIST, 0, (LPARAM) hImageList);
-    ok(hImageListRetVal == NULL, "Expected NULL, got %p\n", hImageListRetVal);
+    hIml = (HIMAGELIST) SendMessage(hChild, HDM_SETIMAGELIST, 0, (LPARAM) hImageList);
+    ok(hIml == NULL, "Expected NULL, got %p\n", hIml);
 
-    hImageListRetVal = (HIMAGELIST) SendMessage(hChild, HDM_GETIMAGELIST, 0, 0);
-    ok(hImageListRetVal != NULL, "Expected non-NULL handle, got %p\n", hImageListRetVal);
+    hIml = (HIMAGELIST) SendMessage(hChild, HDM_GETIMAGELIST, 0, 0);
+    ok(hIml != NULL, "Expected non-NULL handle, got %p\n", hIml);
 
-    hImageListRetVal = (HIMAGELIST) SendMessage(hChild, HDM_CREATEDRAGIMAGE, 0, 0);
-    ok(hImageListRetVal != NULL, "Expected non-NULL handle, got %p\n", hImageListRetVal);
+    hIml = (HIMAGELIST) SendMessage(hChild, HDM_CREATEDRAGIMAGE, 0, 0);
+    ok(hIml != NULL, "Expected non-NULL handle, got %p\n", hIml);
+    ImageList_Destroy(hIml);
 
     ok_sequence(sequences, HEADER_SEQ_INDEX, imageMessages_seq, "imageMessages sequence testing", FALSE);
 
@@ -1025,9 +1007,16 @@ static void test_hdm_filterMessages(HWND hParent)
     todo_wine
     {
         retVal = SendMessage(hChild, HDM_CLEARFILTER, 0, 1);
-        expect(1, retVal);
+        if (retVal == 0)
+            win_skip("HDM_CLEARFILTER needs 5.80\n");
+        else
+            expect(1, retVal);
+
         retVal = SendMessage(hChild, HDM_EDITFILTER, 1, 0);
-        expect(1, retVal);
+        if (retVal == 0)
+            win_skip("HDM_EDITFILTER needs 5.80\n");
+        else
+            expect(1, retVal);
     }
     if (winetest_interactive)
          ok_sequence(sequences, HEADER_SEQ_INDEX, filterMessages_seq_interactive,
@@ -1076,7 +1065,10 @@ static void test_hdm_bitmapmarginMessages(HWND hParent)
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
     retVal = SendMessage(hChild, HDM_GETBITMAPMARGIN, 0, 0);
-    expect(6, retVal);
+    if (retVal == 0)
+        win_skip("HDM_GETBITMAPMARGIN needs 5.80\n");
+    else
+        expect(6, retVal);
 
     ok_sequence(sequences, HEADER_SEQ_INDEX, bitmapmarginMessages_seq,
                       "bitmapmarginMessages sequence testing", FALSE);
@@ -1085,7 +1077,6 @@ static void test_hdm_bitmapmarginMessages(HWND hParent)
 
 static void test_hdm_index_messages(HWND hParent)
 {
-
     HWND hChild;
     int retVal;
     int loopcnt;
@@ -1098,6 +1089,7 @@ static void test_hdm_index_messages(HWND hParent)
     static char thirdHeaderItem[] = "Type";
     static char fourthHeaderItem[] = "Date Modified";
     static char *items[] = {firstHeaderItem, secondHeaderItem, thirdHeaderItem, fourthHeaderItem};
+    RECT rect;
     HDITEM hdItem;
     hdItem.mask = HDI_TEXT | HDI_WIDTH | HDI_FORMAT;
     hdItem.fmt = HDF_LEFT;
@@ -1125,17 +1117,17 @@ static void test_hdm_index_messages(HWND hParent)
 
     retVal = SendMessage(hChild, HDM_DELETEITEM, 3, (LPARAM) &hdItem);
     ok(retVal == TRUE, "Deleting item 3 should return TRUE, got %d\n", retVal);
-    retVal = SendMessage(hChild, HDM_GETITEMCOUNT, 0, (LPARAM) &hdItem);
+    retVal = SendMessage(hChild, HDM_GETITEMCOUNT, 0, 0);
     ok(retVal == 3, "Getting item count should return 3, got %d\n", retVal);
 
     retVal = SendMessage(hChild, HDM_DELETEITEM, 3, (LPARAM) &hdItem);
     ok(retVal == FALSE, "Deleting already-deleted item should return FALSE, got %d\n", retVal);
-    retVal = SendMessage(hChild, HDM_GETITEMCOUNT, 0, (LPARAM) &hdItem);
+    retVal = SendMessage(hChild, HDM_GETITEMCOUNT, 0, 0);
     ok(retVal == 3, "Getting item count should return 3, got %d\n", retVal);
 
     retVal = SendMessage(hChild, HDM_DELETEITEM, 2, (LPARAM) &hdItem);
     ok(retVal == TRUE, "Deleting item 2 should return TRUE, got %d\n", retVal);
-    retVal = SendMessage(hChild, HDM_GETITEMCOUNT, 0, (LPARAM) &hdItem);
+    retVal = SendMessage(hChild, HDM_GETITEMCOUNT, 0, 0);
     ok(retVal == 2, "Getting item count should return 2, got %d\n", retVal);
 
     ok_sequence(sequences, HEADER_SEQ_INDEX, deleteItem_getItemCount_seq,
@@ -1156,9 +1148,19 @@ static void test_hdm_index_messages(HWND hParent)
     expect(0, strcmpResult);
     expect(80, hdItem.cxy);
 
+    iSize = SendMessage(hChild, HDM_GETITEMCOUNT, 0, 0);
+
+    /* item should be updated just after accepting new array */
+    ShowWindow(hChild, SW_HIDE);
+    retVal = SendMessage(hChild, HDM_SETORDERARRAY, iSize, (LPARAM) lpiarray);
+    expect(TRUE, retVal);
+    rect.left = 0;
+    retVal = SendMessage(hChild, HDM_GETITEMRECT, 0, (LPARAM) &rect);
+    expect(TRUE, retVal);
+    ok(rect.left != 0, "Expected updated rectangle\n");
+
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
-    iSize = SendMessage(hChild, HDM_GETITEMCOUNT, 0, (LPARAM) &hdItem);
     retVal = SendMessage(hChild, HDM_SETORDERARRAY, iSize, (LPARAM) lpiarray);
     ok(retVal == TRUE, "Setting header items order should return TRUE, got %d\n", retVal);
 
@@ -1187,12 +1189,156 @@ static void test_hdm_index_messages(HWND hParent)
     DestroyWindow(hChild);
 }
 
+static void test_hdf_fixedwidth(HWND hParent)
+{
+    HWND hChild;
+    HDITEM hdItem;
+    DWORD ret;
+    RECT rect;
+    HDHITTESTINFO ht;
+
+    hChild = create_custom_header_control(hParent, FALSE);
+
+    hdItem.mask = HDI_WIDTH | HDI_FORMAT;
+    hdItem.fmt = HDF_FIXEDWIDTH;
+    hdItem.cxy = 80;
+
+    ret = SendMessage(hChild, HDM_INSERTITEM, 0, (LPARAM)&hdItem);
+    expect(0, ret);
+
+    /* try to change width */
+    rect.right = rect.bottom = 0;
+    SendMessage(hChild, HDM_GETITEMRECT, 0, (LPARAM)&rect);
+    ok(rect.right  != 0, "Expected not zero width\n");
+    ok(rect.bottom != 0, "Expected not zero height\n");
+
+    SendMessage(hChild, WM_LBUTTONDOWN, 0, MAKELPARAM(rect.right, rect.bottom / 2));
+    SendMessage(hChild, WM_MOUSEMOVE, 0, MAKELPARAM(rect.right + 20, rect.bottom / 2));
+    SendMessage(hChild, WM_LBUTTONUP, 0, MAKELPARAM(rect.right + 20, rect.bottom / 2));
+
+    SendMessage(hChild, HDM_GETITEMRECT, 0, (LPARAM)&rect);
+
+    if (hdItem.cxy != rect.right)
+    {
+        win_skip("HDF_FIXEDWIDTH format not supported\n");
+        DestroyWindow(hChild);
+        return;
+    }
+
+    /* try to adjust with message */
+    hdItem.mask = HDI_WIDTH;
+    hdItem.cxy = 90;
+
+    ret = SendMessage(hChild, HDM_SETITEM, 0, (LPARAM)&hdItem);
+    expect(TRUE, ret);
+
+    rect.right = 0;
+    SendMessage(hChild, HDM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(90, rect.right);
+
+    /* hittesting doesn't report ondivider flag for HDF_FIXEDWIDTH */
+    ht.pt.x = rect.right - 1;
+    ht.pt.y = rect.bottom / 2;
+    SendMessage(hChild, HDM_HITTEST, 0, (LPARAM)&ht);
+    expect(HHT_ONHEADER, ht.flags);
+
+    /* try to adjust with message */
+    hdItem.mask = HDI_FORMAT;
+    hdItem.fmt  = 0;
+
+    ret = SendMessage(hChild, HDM_SETITEM, 0, (LPARAM)&hdItem);
+    expect(TRUE, ret);
+
+    ht.pt.x = 90;
+    ht.pt.y = rect.bottom / 2;
+    SendMessage(hChild, HDM_HITTEST, 0, (LPARAM)&ht);
+    expect(HHT_ONDIVIDER, ht.flags);
+
+    DestroyWindow(hChild);
+}
+
+static void test_hds_nosizing(HWND hParent)
+{
+    HWND hChild;
+    HDITEM hdItem;
+    DWORD ret;
+    RECT rect;
+    HDHITTESTINFO ht;
+
+    hChild = create_custom_header_control(hParent, FALSE);
+
+    memset(&hdItem, 0, sizeof(hdItem));
+    hdItem.mask = HDI_WIDTH;
+    hdItem.cxy = 80;
+
+    ret = SendMessage(hChild, HDM_INSERTITEM, 0, (LPARAM)&hdItem);
+    expect(0, ret);
+
+    /* HDS_NOSIZING only blocks hittesting */
+    ret = GetWindowLong(hChild, GWL_STYLE);
+    SetWindowLong(hChild, GWL_STYLE, ret | HDS_NOSIZING);
+
+    /* try to change width with mouse gestures */
+    rect.right = rect.bottom = 0;
+    SendMessage(hChild, HDM_GETITEMRECT, 0, (LPARAM)&rect);
+    ok(rect.right  != 0, "Expected not zero width\n");
+    ok(rect.bottom != 0, "Expected not zero height\n");
+
+    SendMessage(hChild, WM_LBUTTONDOWN, 0, MAKELPARAM(rect.right, rect.bottom / 2));
+    SendMessage(hChild, WM_MOUSEMOVE, 0, MAKELPARAM(rect.right + 20, rect.bottom / 2));
+    SendMessage(hChild, WM_LBUTTONUP, 0, MAKELPARAM(rect.right + 20, rect.bottom / 2));
+
+    SendMessage(hChild, HDM_GETITEMRECT, 0, (LPARAM)&rect);
+
+    if (hdItem.cxy != rect.right)
+    {
+        win_skip("HDS_NOSIZING style not supported\n");
+        DestroyWindow(hChild);
+        return;
+    }
+
+    /* this style doesn't set HDF_FIXEDWIDTH for items */
+    hdItem.mask = HDI_FORMAT;
+    ret = SendMessage(hChild, HDM_GETITEM, 0, (LPARAM)&hdItem);
+    expect(TRUE, ret);
+    ok(!(hdItem.fmt & HDF_FIXEDWIDTH), "Unexpected HDF_FIXEDWIDTH\n");
+
+    /* try to adjust with message */
+    hdItem.mask = HDI_WIDTH;
+    hdItem.cxy = 90;
+
+    ret = SendMessage(hChild, HDM_SETITEM, 0, (LPARAM)&hdItem);
+    expect(TRUE, ret);
+
+    rect.right = 0;
+    SendMessage(hChild, HDM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(90, rect.right);
+
+    /* hittesting doesn't report ondivider flags for HDS_NOSIZING */
+    ht.pt.x = rect.right - 1;
+    ht.pt.y = rect.bottom / 2;
+    SendMessage(hChild, HDM_HITTEST, 0, (LPARAM)&ht);
+    expect(HHT_ONHEADER, ht.flags);
+
+    /* try to adjust with message */
+    ret = GetWindowLong(hChild, GWL_STYLE);
+    SetWindowLong(hChild, GWL_STYLE, ret & ~HDS_NOSIZING);
+
+    ht.pt.x = 90;
+    ht.pt.y = rect.bottom / 2;
+    SendMessage(hChild, HDM_HITTEST, 0, (LPARAM)&ht);
+    expect(HHT_ONDIVIDER, ht.flags);
+
+    DestroyWindow(hChild);
+}
+
 #define TEST_NMCUSTOMDRAW(draw_stage, item_spec, lparam, _left, _top, _right, _bottom) \
     ok(nm->dwDrawStage == draw_stage, "Invalid dwDrawStage %d vs %d\n", draw_stage, nm->dwDrawStage); \
     if (item_spec != -1) \
         ok(nm->dwItemSpec == item_spec, "Invalid dwItemSpec %d vs %ld\n", item_spec, nm->dwItemSpec); \
     ok(nm->lItemlParam == lparam, "Invalid lItemlParam %d vs %ld\n", lparam, nm->lItemlParam); \
-    ok(nm->rc.top == _top && nm->rc.bottom == _bottom && nm->rc.left == _left && nm->rc.right == _right, \
+    ok((nm->rc.top == _top && nm->rc.bottom == _bottom && nm->rc.left == _left && nm->rc.right == _right) || \
+        broken(draw_stage != CDDS_ITEMPREPAINT), /* comctl32 < 5.80 */ \
         "Invalid rect (%d,%d) (%d,%d) vs (%d,%d) (%d,%d)\n", _left, _top, _right, _bottom, \
         nm->rc.left, nm->rc.top, nm->rc.right, nm->rc.bottom);
 
@@ -1545,15 +1691,139 @@ static int init(void)
     return 1;
 }
 
+/* maximum 8 items allowed */
+static void check_orderarray(HWND hwnd, DWORD start, DWORD set, DWORD expected,
+                             int todo, int line)
+{
+    int count, i;
+    INT order[8];
+    DWORD ret, array = 0;
+
+    count = SendMessage(hwnd, HDM_GETITEMCOUNT, 0, 0);
+
+    /* initial order */
+    for(i = 1; i<=count; i++)
+        order[i-1] = start>>(4*(count-i)) & 0xf;
+
+    ret = SendMessage(hwnd, HDM_SETORDERARRAY, count, (LPARAM)order);
+    ok_(__FILE__, line)(ret, "Expected HDM_SETORDERARAY to succeed, got %d\n", ret);
+
+    /* new order */
+    for(i = 1; i<=count; i++)
+        order[i-1] = set>>(4*(count-i)) & 0xf;
+    ret = SendMessage(hwnd, HDM_SETORDERARRAY, count, (LPARAM)order);
+    ok_(__FILE__, line)(ret, "Expected HDM_SETORDERARAY to succeed, got %d\n", ret);
+
+    /* check actual order */
+    ret = SendMessage(hwnd, HDM_GETORDERARRAY, count, (LPARAM)order);
+    ok_(__FILE__, line)(ret, "Expected HDM_GETORDERARAY to succeed, got %d\n", ret);
+    for(i = 1; i<=count; i++)
+        array |= order[i-1]<<(4*(count-i));
+
+    if (todo) {
+    todo_wine
+        ok_(__FILE__, line)(array == expected, "Expected %x, got %x\n", expected, array);
+    }
+    else
+        ok_(__FILE__, line)(array == expected, "Expected %x, got %x\n", expected, array);
+}
+
+static void test_hdm_orderarray(void)
+{
+    HWND hwnd;
+    INT order[5];
+    DWORD ret;
+
+    hwnd = create_header_control();
+
+    /* three items */
+    addItem(hwnd, 0, NULL);
+    addItem(hwnd, 1, NULL);
+    addItem(hwnd, 2, NULL);
+
+    ret = SendMessage(hwnd, HDM_GETORDERARRAY, 3, (LPARAM)order);
+    if (!ret)
+    {
+        win_skip("HDM_GETORDERARRAY not implemented.\n");
+        DestroyWindow(hwnd);
+        return;
+    }
+
+    expect(0, order[0]);
+    expect(1, order[1]);
+    expect(2, order[2]);
+
+if (0)
+{
+    /* null pointer, crashes native */
+    ret = SendMessage(hwnd, HDM_SETORDERARRAY, 3, 0);
+    expect(FALSE, ret);
+}
+    /* count out of limits */
+    ret = SendMessage(hwnd, HDM_SETORDERARRAY, 5, (LPARAM)order);
+    expect(FALSE, ret);
+    /* count out of limits */
+    ret = SendMessage(hwnd, HDM_SETORDERARRAY, 2, (LPARAM)order);
+    expect(FALSE, ret);
+
+    /* try with out of range item index */
+    /* (0,1,2)->(1,0,3) => (1,0,2) */
+    check_orderarray(hwnd, 0x120, 0x103, 0x102, FALSE, __LINE__);
+    /* (1,0,2)->(3,0,1) => (0,2,1) */
+    check_orderarray(hwnd, 0x102, 0x301, 0x021, TRUE, __LINE__);
+    /* (0,2,1)->(2,3,1) => (2,0,1) */
+    check_orderarray(hwnd, 0x021, 0x231, 0x201, FALSE, __LINE__);
+
+    /* (0,1,2)->(0,2,2) => (0,1,2) */
+    check_orderarray(hwnd, 0x012, 0x022, 0x012, FALSE, __LINE__);
+
+    addItem(hwnd, 3, NULL);
+
+    /* (0,1,2,3)->(0,1,2,2) => (0,1,3,2) */
+    check_orderarray(hwnd, 0x0123, 0x0122, 0x0132, FALSE, __LINE__);
+    /* (0,1,2,3)->(0,1,3,3) => (0,1,2,3) */
+    check_orderarray(hwnd, 0x0123, 0x0133, 0x0123, FALSE, __LINE__);
+    /* (0,1,2,3)->(0,4,2,3) => (0,1,2,3) */
+    check_orderarray(hwnd, 0x0123, 0x0423, 0x0123, FALSE, __LINE__);
+    /* (0,1,2,3)->(4,0,1,2) => (0,1,3,2) */
+    check_orderarray(hwnd, 0x0123, 0x4012, 0x0132, TRUE, __LINE__);
+    /* (0,1,3,2)->(4,0,1,4) => (0,3,1,2) */
+    check_orderarray(hwnd, 0x0132, 0x4014, 0x0312, TRUE, __LINE__);
+    /* (0,1,2,3)->(4,1,0,2) => (1,0,3,2) */
+    check_orderarray(hwnd, 0x0123, 0x4102, 0x1032, TRUE, __LINE__);
+    /* (0,1,2,3)->(0,1,4,2) => (0,1,2,3) */
+    check_orderarray(hwnd, 0x0123, 0x0142, 0x0132, FALSE, __LINE__);
+    /* (0,1,2,3)->(4,4,4,4) => (0,1,2,3) */
+    check_orderarray(hwnd, 0x0123, 0x4444, 0x0123, FALSE, __LINE__);
+    /* (0,1,2,3)->(4,4,1,2) => (0,1,3,2) */
+    check_orderarray(hwnd, 0x0123, 0x4412, 0x0132, TRUE, __LINE__);
+    /* (0,1,2,3)->(4,4,4,1) => (0,2,3,1) */
+    check_orderarray(hwnd, 0x0123, 0x4441, 0x0231, TRUE, __LINE__);
+    /* (0,1,2,3)->(1,4,4,4) => (1,0,2,3) */
+    check_orderarray(hwnd, 0x0123, 0x1444, 0x1023, FALSE, __LINE__);
+    /* (0,1,2,3)->(4,2,4,1) => (0,2,3,1) */
+    check_orderarray(hwnd, 0x0123, 0x4241, 0x0231, FALSE, __LINE__);
+    /* (0,1,2,3)->(4,2,0,1) => (2,0,3,1) */
+    check_orderarray(hwnd, 0x0123, 0x4201, 0x2031, TRUE, __LINE__);
+    /* (3,2,1,0)->(4,2,0,1) => (3,2,0,1) */
+    check_orderarray(hwnd, 0x3210, 0x4201, 0x3201, FALSE, __LINE__);
+
+    DestroyWindow(hwnd);
+}
+
 START_TEST(header)
 {
     HWND parent_hwnd;
+    ULONG_PTR ctx_cookie;
+    HANDLE hCtx;
+    HWND hwnd;
 
     if (!init())
         return;
 
     test_header_control();
     test_header_order();
+    test_hdm_orderarray();
     test_customdraw();
 
     DestroyWindow(hHeaderParentWnd);
@@ -1573,6 +1843,33 @@ START_TEST(header)
     test_hdm_unicodeformatMessages(parent_hwnd);
     test_hdm_bitmapmarginMessages(parent_hwnd);
 
-    DestroyWindow(parent_hwnd);
+    if (!load_v6_module(&ctx_cookie, &hCtx))
+    {
+        DestroyWindow(parent_hwnd);
+        return;
+    }
+
+    /* this is a XP SP3 failure workaround */
+    hwnd = CreateWindowExA(0, WC_HEADER, NULL,
+                           WS_CHILD|WS_BORDER|WS_VISIBLE|HDS_BUTTONS|HDS_HORZ,
+                           0, 0, 100, 100,
+                           parent_hwnd, NULL, GetModuleHandleA(NULL), NULL);
 
+    if (!IsWindow(hwnd))
+    {
+        win_skip("FIXME: failed to create Header window.\n");
+        unload_v6_module(ctx_cookie, hCtx);
+        DestroyWindow(parent_hwnd);
+        return;
+    }
+    else
+        DestroyWindow(hwnd);
+
+    /* comctl32 version 6 tests start here */
+    test_hdf_fixedwidth(parent_hwnd);
+    test_hds_nosizing(parent_hwnd);
+
+    unload_v6_module(ctx_cookie, hCtx);
+
+    DestroyWindow(parent_hwnd);
 }
index 3fe249f..cf749c8 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright 2004 Michael Stefaniuc
  * Copyright 2002 Mike McCormack for CodeWeavers
  * Copyright 2007 Dmitry Timoshkov
+ * Copyright 2009 Owen Rudge for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 #include "winuser.h"
 #include "objbase.h"
 #include "commctrl.h" /* must be included after objbase.h to get ImageList_Write */
+#include "initguid.h"
+#include "commoncontrols.h"
+#include "shellapi.h"
 
 #include "wine/test.h"
+#include "v6util.h"
 
 #undef VISIBLE
 
@@ -65,10 +70,14 @@ typedef struct _ILHEAD
 } ILHEAD;
 #include "poppack.h"
 
+static HIMAGELIST (WINAPI *pImageList_Create)(int, int, UINT, int, int);
+static int (WINAPI *pImageList_Add)(HIMAGELIST, HBITMAP, HBITMAP);
 static BOOL (WINAPI *pImageList_DrawIndirect)(IMAGELISTDRAWPARAMS*);
 static BOOL (WINAPI *pImageList_SetImageCount)(HIMAGELIST,UINT);
+static HRESULT (WINAPI *pImageList_CoCreateInstance)(REFCLSID,const IUnknown *,
+    REFIID,void **);
+static HRESULT (WINAPI *pHIMAGELIST_QueryInterface)(HIMAGELIST,REFIID,void **);
 
-static HDC desktopDC;
 static HINSTANCE hinst;
 
 /* These macros build cursor/bitmap data in 4x4 pixel blocks */
@@ -327,7 +336,7 @@ static BOOL DoTest1(void)
     HICON hicon3 ;
 
     /* create an imagelist to play with */
-    himl = ImageList_Create(84,84,0x10,0,3);
+    himl = ImageList_Create(84, 84, ILC_COLOR16, 0, 3);
     ok(himl!=0,"failed to create imagelist\n");
 
     /* load the icons to add to the image list */
@@ -393,7 +402,7 @@ static BOOL DoTest2(void)
     HICON hicon3 ;
 
     /* create an imagelist to play with */
-    himl = ImageList_Create(84,84,0x10,0,3);
+    himl = ImageList_Create(84, 84, ILC_COLOR16, 0, 3);
     ok(himl!=0,"failed to create imagelist\n");
 
     /* load the icons to add to the image list */
@@ -442,7 +451,7 @@ static BOOL DoTest3(void)
     ok(hdc!=NULL, "couldn't get DC\n");
 
     /* create an imagelist to play with */
-    himl = ImageList_Create(48,48,0x10,0,3);
+    himl = ImageList_Create(48, 48, ILC_COLOR16, 0, 3);
     ok(himl!=0,"failed to create imagelist\n");
 
     /* load the icons to add to the image list */
@@ -824,7 +833,7 @@ static void check_bitmap_data(const char *bm_data, ULONG bm_data_size,
 
 static void check_ilhead_data(const char *ilh_data, INT cx, INT cy, INT cur, INT max)
 {
-    ILHEAD *ilh = (ILHEAD *)ilh_data;
+    const ILHEAD *ilh = (const ILHEAD *)ilh_data;
 
     ok(ilh->usMagic == IMAGELIST_MAGIC, "wrong usMagic %4x (expected %02x)\n", ilh->usMagic, IMAGELIST_MAGIC);
     ok(ilh->usVersion == 0x101, "wrong usVersion %x (expected 0x101)\n", ilh->usVersion);
@@ -981,13 +990,639 @@ static void test_imagelist_storage(void)
     iml_clear_stream_data();
 }
 
+static void test_shell_imagelist(void)
+{
+    BOOL (WINAPI *pSHGetImageList)(INT, REFIID, void**);
+    IImageList *iml = NULL;
+    HMODULE hShell32;
+    HRESULT hr;
+    int out = 0;
+    RECT rect;
+    int cx, cy;
+
+    /* Try to load function from shell32 */
+    hShell32 = LoadLibrary("shell32.dll");
+    pSHGetImageList = (void*)GetProcAddress(hShell32, (LPCSTR) 727);
+
+    if (!pSHGetImageList)
+    {
+        win_skip("SHGetImageList not available, skipping test\n");
+        return;
+    }
+
+    /* Get system image list */
+    hr = (pSHGetImageList)(SHIL_SYSSMALL, &IID_IImageList, (void**)&iml);
+
+    ok(SUCCEEDED(hr), "SHGetImageList failed, hr=%x\n", hr);
+
+    if (hr != S_OK)
+        return;
+
+    IImageList_GetImageCount(iml, &out);
+    ok(out > 0, "IImageList_GetImageCount returned out <= 0\n");
+
+    /* Fetch the small icon size */
+    cx = GetSystemMetrics(SM_CXSMICON);
+    cy = GetSystemMetrics(SM_CYSMICON);
+
+    /* Check icon size matches */
+    IImageList_GetImageRect(iml, 0, &rect);
+    ok(((rect.right == cx) && (rect.bottom == cy)),
+                 "IImageList_GetImageRect returned r:%d,b:%d\n",
+                 rect.right, rect.bottom);
+
+    IImageList_Release(iml);
+    FreeLibrary(hShell32);
+}
+
+static HBITMAP create_test_bitmap(HDC hdc, int bpp, UINT32 pixel1, UINT32 pixel2)
+{
+    HBITMAP hBitmap;
+    UINT32 *buffer = NULL;
+    BITMAPINFO bitmapInfo = {{sizeof(BITMAPINFOHEADER), 2, 1, 1, bpp, BI_RGB,
+                                0, 0, 0, 0, 0}};
+
+    hBitmap = CreateDIBSection(hdc, &bitmapInfo, DIB_RGB_COLORS, (void**)&buffer, NULL, 0);
+    ok(hBitmap != NULL && buffer != NULL, "CreateDIBSection failed.\n");
+
+    if(!hBitmap || !buffer)
+    {
+        DeleteObject(hBitmap);
+        return NULL;
+    }
+
+    buffer[0] = pixel1;
+    buffer[1] = pixel2;
+
+    return hBitmap;
+}
+
+static BOOL colour_match(UINT32 x, UINT32 y)
+{
+    const INT32 tolerance = 8;
+
+    const INT32 dr = abs((INT32)(x & 0x000000FF) - (INT32)(y & 0x000000FF));
+    const INT32 dg = abs((INT32)((x & 0x0000FF00) >> 8) - (INT32)((y & 0x0000FF00) >> 8));
+    const INT32 db = abs((INT32)((x & 0x00FF0000) >> 16) - (INT32)((y & 0x00FF0000) >> 16));
+
+    return (dr <= tolerance && dg <= tolerance && db <= tolerance);
+}
+
+static void check_ImageList_DrawIndirect(IMAGELISTDRAWPARAMS *ildp, UINT32 *bits,
+                                         UINT32 expected, int line)
+{
+    bits[0] = 0x00FFFFFF;
+    pImageList_DrawIndirect(ildp);
+    ok(colour_match(bits[0], expected),
+       "ImageList_DrawIndirect: Pixel %08X, Expected a close match to %08X from line %d\n",
+       bits[0] & 0x00FFFFFF, expected, line);
+}
+
+
+static void check_ImageList_DrawIndirect_fStyle(HDC hdc, HIMAGELIST himl, UINT32 *bits, int i,
+                                                UINT fStyle, UINT32 expected, int line)
+{
+    IMAGELISTDRAWPARAMS ildp = {sizeof(IMAGELISTDRAWPARAMS), himl, i, hdc,
+        0, 0, 0, 0, 0, 0, CLR_NONE, CLR_NONE, fStyle, 0, ILS_NORMAL, 0, 0x00000000};
+    check_ImageList_DrawIndirect(&ildp, bits, expected, line);
+}
+
+static void check_ImageList_DrawIndirect_ILD_ROP(HDC hdc, HIMAGELIST himl, UINT32 *bits, int i,
+                                                DWORD dwRop, UINT32 expected, int line)
+{
+    IMAGELISTDRAWPARAMS ildp = {sizeof(IMAGELISTDRAWPARAMS), himl, i, hdc,
+        0, 0, 0, 0, 0, 0, CLR_NONE, CLR_NONE, ILD_IMAGE | ILD_ROP, dwRop, ILS_NORMAL, 0, 0x00000000};
+    check_ImageList_DrawIndirect(&ildp, bits, expected, line);
+}
+
+static void check_ImageList_DrawIndirect_fState(HDC hdc, HIMAGELIST himl, UINT32 *bits, int i, UINT fStyle,
+                                                UINT fState, DWORD Frame, UINT32 expected, int line)
+{
+    IMAGELISTDRAWPARAMS ildp = {sizeof(IMAGELISTDRAWPARAMS), himl, i, hdc,
+        0, 0, 0, 0, 0, 0, CLR_NONE, CLR_NONE, fStyle, 0, fState, Frame, 0x00000000};
+    check_ImageList_DrawIndirect(&ildp, bits, expected, line);
+}
+
+static void check_ImageList_DrawIndirect_broken(HDC hdc, HIMAGELIST himl, UINT32 *bits, int i,
+                                                UINT fStyle, UINT fState, DWORD Frame, UINT32 expected,
+                                                UINT32 broken_expected, int line)
+{
+    IMAGELISTDRAWPARAMS ildp = {sizeof(IMAGELISTDRAWPARAMS), himl, i, hdc,
+        0, 0, 0, 0, 0, 0, CLR_NONE, CLR_NONE, fStyle, 0, fState, Frame, 0x00000000};
+    bits[0] = 0x00FFFFFF;
+    pImageList_DrawIndirect(&ildp);
+    ok(colour_match(bits[0], expected) ||
+       broken(colour_match(bits[0], broken_expected)),
+       "ImageList_DrawIndirect: Pixel %08X, Expected a close match to %08X from line %d\n",
+       bits[0] & 0x00FFFFFF, expected, line);
+}
+
+static void test_ImageList_DrawIndirect(void)
+{
+    HIMAGELIST himl = NULL;
+    int ret;
+    HDC hdcDst = NULL;
+    HBITMAP hbmOld = NULL, hbmDst = NULL;
+    HBITMAP hbmMask = NULL, hbmInverseMask = NULL;
+    HBITMAP hbmImage = NULL, hbmAlphaImage = NULL, hbmTransparentImage = NULL;
+    int iImage = -1, iAlphaImage = -1, iTransparentImage = -1;
+    UINT32 *bits = 0;
+    UINT32 maskBits = 0x00000000, inverseMaskBits = 0xFFFFFFFF;
+
+    BITMAPINFO bitmapInfo = {{sizeof(BITMAPINFOHEADER), 2, 1, 1, 32, BI_RGB,
+                                0, 0, 0, 0, 0}};
+
+    hdcDst = CreateCompatibleDC(0);
+    ok(hdcDst != 0, "CreateCompatibleDC(0) failed to return a valid DC\n");
+    if (!hdcDst)
+        return;
+
+    hbmMask = CreateBitmap(2, 1, 1, 1, &maskBits);
+    ok(hbmMask != 0, "CreateBitmap failed\n");
+    if(!hbmMask) goto cleanup;
+
+    hbmInverseMask = CreateBitmap(2, 1, 1, 1, &inverseMaskBits);
+    ok(hbmInverseMask != 0, "CreateBitmap failed\n");
+    if(!hbmInverseMask) goto cleanup;
+
+    himl = pImageList_Create(2, 1, ILC_COLOR32, 0, 1);
+    ok(himl != 0, "ImageList_Create failed\n");
+    if(!himl) goto cleanup;
+
+    /* Add a no-alpha image */
+    hbmImage = create_test_bitmap(hdcDst, 32, 0x00ABCDEF, 0x00ABCDEF);
+    if(!hbmImage) goto cleanup;
+
+    iImage = pImageList_Add(himl, hbmImage, hbmMask);
+    ok(iImage != -1, "ImageList_Add failed\n");
+    if(iImage == -1) goto cleanup;
+
+    /* Add an alpha image */
+    hbmAlphaImage = create_test_bitmap(hdcDst, 32, 0x89ABCDEF, 0x89ABCDEF);
+    if(!hbmAlphaImage) goto cleanup;
+
+    iAlphaImage = pImageList_Add(himl, hbmAlphaImage, hbmMask);
+    ok(iAlphaImage != -1, "ImageList_Add failed\n");
+    if(iAlphaImage == -1) goto cleanup;
+
+    /* Add a transparent alpha image */
+    hbmTransparentImage = create_test_bitmap(hdcDst, 32, 0x00ABCDEF, 0x89ABCDEF);
+    if(!hbmTransparentImage) goto cleanup;
+
+    iTransparentImage = pImageList_Add(himl, hbmTransparentImage, hbmMask);
+    ok(iTransparentImage != -1, "ImageList_Add failed\n");
+    if(iTransparentImage == -1) goto cleanup;
+
+    /* 32-bit Tests */
+    bitmapInfo.bmiHeader.biBitCount = 32;
+    hbmDst = CreateDIBSection(hdcDst, &bitmapInfo, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
+    ok (hbmDst && bits, "CreateDIBSection failed to return a valid bitmap and buffer\n");
+    if (!hbmDst || !bits)
+        goto cleanup;
+    hbmOld = SelectObject(hdcDst, hbmDst);
+
+    check_ImageList_DrawIndirect_fStyle(hdcDst, himl, bits, iImage, ILD_NORMAL, 0x00ABCDEF, __LINE__);
+    check_ImageList_DrawIndirect_fStyle(hdcDst, himl, bits, iImage, ILD_TRANSPARENT, 0x00ABCDEF, __LINE__);
+    todo_wine check_ImageList_DrawIndirect_broken(hdcDst, himl, bits, iAlphaImage, ILD_BLEND25, ILS_NORMAL, 0, 0x00E8F1FA, 0x00D4D9DD, __LINE__);
+    todo_wine check_ImageList_DrawIndirect_broken(hdcDst, himl, bits, iAlphaImage, ILD_BLEND50, ILS_NORMAL, 0, 0x00E8F1FA, 0x00B4BDC4, __LINE__);
+    check_ImageList_DrawIndirect_fStyle(hdcDst, himl, bits, iImage, ILD_MASK, 0x00ABCDEF, __LINE__);
+    check_ImageList_DrawIndirect_fStyle(hdcDst, himl, bits, iImage, ILD_IMAGE, 0x00ABCDEF, __LINE__);
+    check_ImageList_DrawIndirect_fStyle(hdcDst, himl, bits, iImage, ILD_PRESERVEALPHA, 0x00ABCDEF, __LINE__);
+
+    check_ImageList_DrawIndirect_fStyle(hdcDst, himl, bits, iAlphaImage, ILD_NORMAL, 0x00D3E5F7, __LINE__);
+    check_ImageList_DrawIndirect_fStyle(hdcDst, himl, bits, iAlphaImage, ILD_TRANSPARENT, 0x00D3E5F7, __LINE__);
+    todo_wine
+    {
+        check_ImageList_DrawIndirect_broken(hdcDst, himl, bits, iAlphaImage, ILD_BLEND25, ILS_NORMAL, 0, 0x00E8F1FA, 0x009DA8B1, __LINE__);
+        check_ImageList_DrawIndirect_broken(hdcDst, himl, bits, iAlphaImage, ILD_BLEND50, ILS_NORMAL, 0, 0x00E8F1FA, 0x008C99A3, __LINE__);
+
+    }
+    check_ImageList_DrawIndirect_fStyle(hdcDst, himl, bits, iAlphaImage, ILD_MASK, 0x00D3E5F7, __LINE__);
+    check_ImageList_DrawIndirect_fStyle(hdcDst, himl, bits, iAlphaImage, ILD_IMAGE, 0x00D3E5F7, __LINE__);
+    todo_wine check_ImageList_DrawIndirect_fStyle(hdcDst, himl, bits, iAlphaImage, ILD_PRESERVEALPHA, 0x005D6F81, __LINE__);
+
+    check_ImageList_DrawIndirect_fStyle(hdcDst, himl, bits, iTransparentImage, ILD_NORMAL, 0x00FFFFFF, __LINE__);
+
+    check_ImageList_DrawIndirect_ILD_ROP(hdcDst, himl, bits, iImage, SRCCOPY, 0x00ABCDEF, __LINE__);
+    check_ImageList_DrawIndirect_ILD_ROP(hdcDst, himl, bits, iImage, SRCINVERT, 0x00543210, __LINE__);
+
+    /* ILD_ROP is ignored when the image has an alpha channel */
+    check_ImageList_DrawIndirect_ILD_ROP(hdcDst, himl, bits, iAlphaImage, SRCCOPY, 0x00D3E5F7, __LINE__);
+    check_ImageList_DrawIndirect_ILD_ROP(hdcDst, himl, bits, iAlphaImage, SRCINVERT, 0x00D3E5F7, __LINE__);
+
+    todo_wine check_ImageList_DrawIndirect_fState(hdcDst, himl, bits, iImage, ILD_NORMAL, ILS_SATURATE, 0, 0x00CCCCCC, __LINE__);
+    todo_wine check_ImageList_DrawIndirect_broken(hdcDst, himl, bits, iAlphaImage, ILD_NORMAL, ILS_SATURATE, 0, 0x00AFAFAF, 0x00F0F0F0, __LINE__);
+
+    check_ImageList_DrawIndirect_fState(hdcDst, himl, bits, iImage, ILD_NORMAL, ILS_GLOW, 0, 0x00ABCDEF, __LINE__);
+    check_ImageList_DrawIndirect_fState(hdcDst, himl, bits, iImage, ILD_NORMAL, ILS_SHADOW, 0, 0x00ABCDEF, __LINE__);
+
+    check_ImageList_DrawIndirect_fState(hdcDst, himl, bits, iImage, ILD_NORMAL, ILS_ALPHA, 127, 0x00D5E6F7, __LINE__);
+    check_ImageList_DrawIndirect_broken(hdcDst, himl, bits, iAlphaImage, ILD_NORMAL, ILS_ALPHA, 127, 0x00E9F2FB, 0x00AEB7C0, __LINE__);
+    todo_wine check_ImageList_DrawIndirect_broken(hdcDst, himl, bits, iAlphaImage, ILD_NORMAL, ILS_NORMAL, 127, 0x00E9F2FB, 0x00D3E5F7, __LINE__);
+
+cleanup:
+
+    if(hbmOld)
+        SelectObject(hdcDst, hbmOld);
+    if(hbmDst)
+        DeleteObject(hbmDst);
+
+    if(hdcDst)
+        DeleteDC(hdcDst);
+
+    if(hbmMask)
+        DeleteObject(hbmMask);
+    if(hbmInverseMask)
+        DeleteObject(hbmInverseMask);
+
+    if(hbmImage)
+        DeleteObject(hbmImage);
+    if(hbmAlphaImage)
+        DeleteObject(hbmAlphaImage);
+    if(hbmTransparentImage)
+        DeleteObject(hbmTransparentImage);
+
+    if(himl)
+    {
+        ret = ImageList_Destroy(himl);
+        ok(ret, "ImageList_Destroy failed\n");
+    }
+}
+
+static void test_iimagelist(void)
+{
+    IImageList *imgl;
+    HIMAGELIST himl;
+    HRESULT hr;
+    ULONG ret;
+
+    if (!pHIMAGELIST_QueryInterface)
+    {
+        win_skip("XP imagelist functions not available\n");
+        return;
+    }
+
+    /* test reference counting on destruction */
+    imgl = (IImageList*)createImageList(32, 32);
+    ret = IUnknown_AddRef(imgl);
+    ok(ret == 2, "Expected 2, got %d\n", ret);
+    ret = ImageList_Destroy((HIMAGELIST)imgl);
+    ok(ret == TRUE, "Expected TRUE, got %d\n", ret);
+    ret = ImageList_Destroy((HIMAGELIST)imgl);
+    ok(ret == TRUE, "Expected TRUE, got %d\n", ret);
+    ret = ImageList_Destroy((HIMAGELIST)imgl);
+    ok(ret == FALSE, "Expected FALSE, got %d\n", ret);
+
+    imgl = (IImageList*)createImageList(32, 32);
+    ret = IUnknown_AddRef(imgl);
+    ok(ret == 2, "Expected 2, got %d\n", ret);
+    ret = ImageList_Destroy((HIMAGELIST)imgl);
+    ok(ret == TRUE, "Expected TRUE, got %d\n", ret);
+    ret = IImageList_Release(imgl);
+    ok(ret == 0, "Expected 0, got %d\n", ret);
+    ret = ImageList_Destroy((HIMAGELIST)imgl);
+    ok(ret == FALSE, "Expected FALSE, got %d\n", ret);
+
+    if (!pImageList_CoCreateInstance)
+    {
+        win_skip("Vista imagelist functions not available\n");
+        return;
+    }
+
+    hr = pImageList_CoCreateInstance(&CLSID_ImageList, NULL, &IID_IImageList, (void **) &imgl);
+    ok(SUCCEEDED(hr), "ImageList_CoCreateInstance failed, hr=%x\n", hr);
+
+    if (hr == S_OK)
+        IImageList_Release(imgl);
+
+    himl = createImageList(32, 32);
+
+    if (!himl)
+        return;
+
+    hr = (pHIMAGELIST_QueryInterface)(himl, &IID_IImageList, (void **) &imgl);
+    ok(SUCCEEDED(hr), "HIMAGELIST_QueryInterface failed, hr=%x\n", hr);
+
+    if (hr == S_OK)
+        IImageList_Release(imgl);
+
+    ImageList_Destroy(himl);
+}
+
+static void testHotspot_v6(void)
+{
+    struct hotspot {
+        int dx;
+        int dy;
+    };
+
+#define SIZEX1 47
+#define SIZEY1 31
+#define SIZEX2 11
+#define SIZEY2 17
+#define HOTSPOTS_MAX 4       /* Number of entries in hotspots */
+    static const struct hotspot hotspots[HOTSPOTS_MAX] = {
+        { 10, 7 },
+        { SIZEX1, SIZEY1 },
+        { -9, -8 },
+        { -7, 35 }
+    };
+    int i, j;
+    HIMAGELIST himl1 = createImageList(SIZEX1, SIZEY1);
+    HIMAGELIST himl2 = createImageList(SIZEX2, SIZEY2);
+    IImageList *imgl1, *imgl2;
+    HRESULT hr;
+
+    /* cast to IImageList */
+    imgl1 = (IImageList *) himl1;
+    imgl2 = (IImageList *) himl2;
+
+    for (i = 0; i < HOTSPOTS_MAX; i++) {
+        for (j = 0; j < HOTSPOTS_MAX; j++) {
+            int dx1 = hotspots[i].dx;
+            int dy1 = hotspots[i].dy;
+            int dx2 = hotspots[j].dx;
+            int dy2 = hotspots[j].dy;
+            int correctx, correcty, newx, newy;
+            char loc[256];
+            IImageList *imglNew;
+            POINT ppt;
+
+            hr = IImageList_BeginDrag(imgl1, 0, dx1, dy1);
+            ok(SUCCEEDED(hr), "BeginDrag failed for { %d, %d }\n", dx1, dy1);
+            sprintf(loc, "BeginDrag (%d,%d)\n", i, j);
+
+            /* check merging the dragged image with a second image */
+            hr = IImageList_SetDragCursorImage(imgl2, (IUnknown *) imgl2, 0, dx2, dy2);
+            ok(SUCCEEDED(hr), "SetDragCursorImage failed for {%d, %d}{%d, %d}\n",
+                    dx1, dy1, dx2, dy2);
+            sprintf(loc, "SetDragCursorImage (%d,%d)\n", i, j);
+
+            /* check new hotspot, it should be the same like the old one */
+            hr = IImageList_GetDragImage(imgl2, NULL, &ppt, &IID_IImageList, (PVOID *) &imglNew);
+            ok(SUCCEEDED(hr), "GetDragImage failed\n");
+            ok(ppt.x == dx1 && ppt.y == dy1,
+                    "Expected drag hotspot [%d,%d] got [%d,%d]\n",
+                    dx1, dy1, ppt.x, ppt.y);
+            /* check size of new dragged image */
+            IImageList_GetIconSize(imglNew, &newx, &newy);
+            correctx = max(SIZEX1, max(SIZEX2 + dx2, SIZEX1 - dx2));
+            correcty = max(SIZEY1, max(SIZEY2 + dy2, SIZEY1 - dy2));
+            ok(newx == correctx && newy == correcty,
+                    "Expected drag image size [%d,%d] got [%d,%d]\n",
+                    correctx, correcty, newx, newy);
+            sprintf(loc, "GetDragImage (%d,%d)\n", i, j);
+            IImageList_EndDrag(imgl2);
+        }
+    }
+#undef SIZEX1
+#undef SIZEY1
+#undef SIZEX2
+#undef SIZEY2
+#undef HOTSPOTS_MAX
+    IImageList_Release(imgl2);
+    IImageList_Release(imgl1);
+}
+
+static void DoTest1_v6(void)
+{
+    IImageList *imgl;
+    HIMAGELIST himl;
+    HRESULT hr;
+
+    HICON hicon1;
+    HICON hicon2;
+    HICON hicon3;
+
+    int ret = 0;
+
+    /* create an imagelist to play with */
+    himl = ImageList_Create(84, 84, ILC_COLOR16, 0, 3);
+    ok(himl != 0,"failed to create imagelist\n");
+
+    imgl = (IImageList *) himl;
+
+    /* load the icons to add to the image list */
+    hicon1 = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits);
+    ok(hicon1 != 0, "no hicon1\n");
+    hicon2 = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits);
+    ok(hicon2 != 0, "no hicon2\n");
+    hicon3 = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits);
+    ok(hicon3 != 0, "no hicon3\n");
+
+    /* remove when nothing exists */
+    hr = IImageList_Remove(imgl, 0);
+    ok(!(SUCCEEDED(hr)), "removed nonexistent icon\n");
+
+    /* removing everything from an empty imagelist should succeed */
+    hr = IImageList_Remove(imgl, -1);
+    ok(SUCCEEDED(hr), "removed nonexistent icon\n");
+
+    /* add three */
+    ok(SUCCEEDED(IImageList_ReplaceIcon(imgl, -1, hicon1, &ret)) && (ret == 0),"failed to add icon1\n");
+    ok(SUCCEEDED(IImageList_ReplaceIcon(imgl, -1, hicon2, &ret)) && (ret == 1),"failed to add icon2\n");
+    ok(SUCCEEDED(IImageList_ReplaceIcon(imgl, -1, hicon3, &ret)) && (ret == 2),"failed to add icon3\n");
+
+    /* remove an index out of range */
+    ok(FAILED(IImageList_Remove(imgl, 4711)),"removed nonexistent icon\n");
+
+    /* remove three */
+    ok(SUCCEEDED(IImageList_Remove(imgl,0)),"can't remove 0\n");
+    ok(SUCCEEDED(IImageList_Remove(imgl,0)),"can't remove 0\n");
+    ok(SUCCEEDED(IImageList_Remove(imgl,0)),"can't remove 0\n");
+
+    /* remove one extra */
+    ok(FAILED(IImageList_Remove(imgl, 0)),"removed nonexistent icon\n");
+
+    /* check SetImageCount/GetImageCount */
+    ok(SUCCEEDED(IImageList_SetImageCount(imgl, 3)), "couldn't increase image count\n");
+    ok(SUCCEEDED(IImageList_GetImageCount(imgl, &ret)) && (ret == 3), "invalid image count after increase\n");
+    ok(SUCCEEDED(IImageList_SetImageCount(imgl, 1)), "couldn't decrease image count\n");
+    ok(SUCCEEDED(IImageList_GetImageCount(imgl, &ret)) && (ret == 1), "invalid image count after decrease to 1\n");
+    ok(SUCCEEDED(IImageList_SetImageCount(imgl, 0)), "couldn't decrease image count\n");
+    ok(SUCCEEDED(IImageList_GetImageCount(imgl, &ret)) && (ret == 0), "invalid image count after decrease to 0\n");
+
+    /* destroy it */
+    ok(SUCCEEDED(IImageList_Release(imgl)),"release imagelist failed\n");
+
+    ok(DestroyIcon(hicon1),"icon 1 wasn't deleted\n");
+    ok(DestroyIcon(hicon2),"icon 2 wasn't deleted\n");
+    ok(DestroyIcon(hicon3),"icon 3 wasn't deleted\n");
+}
+
+static void DoTest3_v6(void)
+{
+    IImageList *imgl;
+    HIMAGELIST himl;
+
+    HBITMAP hbm1;
+    HBITMAP hbm2;
+    HBITMAP hbm3;
+
+    IMAGELISTDRAWPARAMS imldp;
+    HWND hwndfortest;
+    HDC hdc;
+    int ret;
+
+    hwndfortest = create_a_window();
+    hdc = GetDC(hwndfortest);
+    ok(hdc!=NULL, "couldn't get DC\n");
+
+    /* create an imagelist to play with */
+    himl = ImageList_Create(48, 48, ILC_COLOR16, 0, 3);
+    ok(himl!=0,"failed to create imagelist\n");
+
+    imgl = (IImageList *) himl;
+
+    /* load the icons to add to the image list */
+    hbm1 = CreateBitmap(48, 48, 1, 1, bitmap_bits);
+    ok(hbm1 != 0, "no bitmap 1\n");
+    hbm2 = CreateBitmap(48, 48, 1, 1, bitmap_bits);
+    ok(hbm2 != 0, "no bitmap 2\n");
+    hbm3 = CreateBitmap(48, 48, 1, 1, bitmap_bits);
+    ok(hbm3 != 0, "no bitmap 3\n");
+
+    /* add three */
+    ok(SUCCEEDED(IImageList_Add(imgl, hbm1, 0, &ret)) && (ret == 0), "failed to add bitmap 1\n");
+    ok(SUCCEEDED(IImageList_Add(imgl, hbm2, 0, &ret)) && (ret == 1), "failed to add bitmap 2\n");
+
+    ok(SUCCEEDED(IImageList_SetImageCount(imgl, 3)), "Setimage count failed\n");
+    ok(SUCCEEDED(IImageList_Replace(imgl, 2, hbm3, 0)), "failed to replace bitmap 3\n");
+
+    memset(&imldp, 0, sizeof (imldp));
+    ok(FAILED(IImageList_Draw(imgl, &imldp)), "zero data succeeded!\n");
+
+    imldp.cbSize = sizeof (imldp);
+    imldp.hdcDst = hdc;
+    imldp.himl = himl;
+
+    if (FAILED(IImageList_Draw(imgl, &imldp)))
+    {
+       /* Earlier versions of native comctl32 use a smaller structure */
+       imldp.cbSize -= 3 * sizeof(DWORD);
+       ok(SUCCEEDED(IImageList_Draw(imgl, &imldp)), "should succeed\n");
+    }
+
+    REDRAW(hwndfortest);
+    WAIT;
+
+    imldp.fStyle = SRCCOPY;
+    imldp.rgbBk = CLR_DEFAULT;
+    imldp.rgbFg = CLR_DEFAULT;
+    imldp.y = 100;
+    imldp.x = 100;
+    ok(SUCCEEDED(IImageList_Draw(imgl, &imldp)), "should succeed\n");
+    imldp.i ++;
+    ok(SUCCEEDED(IImageList_Draw(imgl, &imldp)), "should succeed\n");
+    imldp.i ++;
+    ok(SUCCEEDED(IImageList_Draw(imgl, &imldp)), "should succeed\n");
+    imldp.i ++;
+    ok(FAILED(IImageList_Draw(imgl, &imldp)), "should fail\n");
+
+    /* remove three */
+    ok(SUCCEEDED(IImageList_Remove(imgl, 0)), "removing 1st bitmap\n");
+    ok(SUCCEEDED(IImageList_Remove(imgl, 0)), "removing 2nd bitmap\n");
+    ok(SUCCEEDED(IImageList_Remove(imgl, 0)), "removing 3rd bitmap\n");
+
+    /* destroy it */
+    ok(SUCCEEDED(IImageList_Release(imgl)), "release imagelist failed\n");
+
+    /* bitmaps should not be deleted by the imagelist */
+    ok(DeleteObject(hbm1),"bitmap 1 can't be deleted\n");
+    ok(DeleteObject(hbm2),"bitmap 2 can't be deleted\n");
+    ok(DeleteObject(hbm3),"bitmap 3 can't be deleted\n");
+
+    ReleaseDC(hwndfortest, hdc);
+    DestroyWindow(hwndfortest);
+}
+
+static void testMerge_v6(void)
+{
+    HIMAGELIST himl1, himl2;
+    IImageList *imgl1, *imgl2, *merge;
+    HICON hicon1;
+    HWND hwnd = create_a_window();
+    HRESULT hr;
+    int ret;
+
+    himl1 = ImageList_Create(32,32,0,0,3);
+    ok(himl1 != NULL,"failed to create himl1\n");
+
+    himl2 = ImageList_Create(32,32,0,0,3);
+    ok(himl2 != NULL,"failed to create himl2\n");
+
+    hicon1 = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits);
+    ok(hicon1 != NULL, "failed to create hicon1\n");
+
+    if (!himl1 || !himl2 || !hicon1)
+        return;
+
+    /* cast to IImageList */
+    imgl1 = (IImageList *) himl1;
+    imgl2 = (IImageList *) himl2;
+
+    ok(SUCCEEDED(IImageList_ReplaceIcon(imgl2, -1, hicon1, &ret)) && (ret == 0),"add icon1 to himl2 failed\n");
+
+    /* If himl1 has no images, merge still succeeds */
+    hr = IImageList_Merge(imgl1, -1, (IUnknown *) imgl2, 0, 0, 0, &IID_IImageList, (void **) &merge);
+    ok(SUCCEEDED(hr), "merge himl1,-1 failed\n");
+    if (SUCCEEDED(hr)) IImageList_Release(merge);
+
+    hr = IImageList_Merge(imgl1, 0, (IUnknown *) imgl2, 0, 0, 0, &IID_IImageList, (void **) &merge);
+    ok(SUCCEEDED(hr), "merge himl1,0 failed\n");
+    if (SUCCEEDED(hr)) IImageList_Release(merge);
+
+    /* Same happens if himl2 is empty */
+    IImageList_Release(imgl2);
+    himl2 = ImageList_Create(32,32,0,0,3);
+    ok(himl2 != NULL,"failed to recreate himl2\n");
+
+    imgl2 = (IImageList *) himl2;
+
+    hr = IImageList_Merge(imgl1, -1, (IUnknown *) imgl2, -1, 0, 0, &IID_IImageList, (void **) &merge);
+    ok(SUCCEEDED(hr), "merge himl2,-1 failed\n");
+    if (SUCCEEDED(hr)) IImageList_Release(merge);
+
+    hr = IImageList_Merge(imgl1, -1, (IUnknown *) imgl2, 0, 0, 0, &IID_IImageList, (void **) &merge);
+    ok(SUCCEEDED(hr), "merge himl2,0 failed\n");
+    if (SUCCEEDED(hr)) IImageList_Release(merge);
+
+    /* Now try merging an image with itself */
+    ok(SUCCEEDED(IImageList_ReplaceIcon(imgl2, -1, hicon1, &ret)) && (ret == 0),"re-add icon1 to himl2 failed\n");
+
+    hr = IImageList_Merge(imgl2, 0, (IUnknown *) imgl2, 0, 0, 0, &IID_IImageList, (void **) &merge);
+    ok(SUCCEEDED(hr), "merge himl2 with itself failed\n");
+    if (SUCCEEDED(hr)) IImageList_Release(merge);
+
+    /* Try merging 2 different image lists */
+    ok(SUCCEEDED(IImageList_ReplaceIcon(imgl1, -1, hicon1, &ret)) && (ret == 0),"add icon1 to himl1 failed\n");
+
+    hr = IImageList_Merge(imgl1, 0, (IUnknown *) imgl2, 0, 0, 0, &IID_IImageList, (void **) &merge);
+    ok(SUCCEEDED(hr), "merge himl1 with himl2 failed\n");
+    if (SUCCEEDED(hr)) IImageList_Release(merge);
+
+    hr = IImageList_Merge(imgl1, 0, (IUnknown *) imgl2, 0, 8, 16, &IID_IImageList, (void **) &merge);
+    ok(SUCCEEDED(hr), "merge himl1 with himl2 8,16 failed\n");
+    if (SUCCEEDED(hr)) IImageList_Release(merge);
+
+    IImageList_Release(imgl1);
+    IImageList_Release(imgl2);
+
+    DestroyIcon(hicon1);
+    DestroyWindow(hwnd);
+}
+
 START_TEST(imagelist)
 {
+    ULONG_PTR ctx_cookie;
+    HANDLE hCtx;
+
     HMODULE hComCtl32 = GetModuleHandle("comctl32.dll");
+    pImageList_Create = NULL;   /* These are not needed for non-v6.0 tests*/
+    pImageList_Add = NULL;
     pImageList_DrawIndirect = (void*)GetProcAddress(hComCtl32, "ImageList_DrawIndirect");
     pImageList_SetImageCount = (void*)GetProcAddress(hComCtl32, "ImageList_SetImageCount");
 
-    desktopDC=GetDC(NULL);
     hinst = GetModuleHandleA(NULL);
 
     InitCommonControls();
@@ -998,4 +1633,36 @@ START_TEST(imagelist)
     DoTest3();
     testMerge();
     test_imagelist_storage();
+
+    FreeLibrary(hComCtl32);
+
+    /* Now perform v6 tests */
+
+    if (!load_v6_module(&ctx_cookie, &hCtx))
+        return;
+
+    /* Reload comctl32 */
+    hComCtl32 = LoadLibraryA("comctl32.dll");
+    pImageList_Create = (void*)GetProcAddress(hComCtl32, "ImageList_Create");
+    pImageList_Add = (void*)GetProcAddress(hComCtl32, "ImageList_Add");
+    pImageList_DrawIndirect = (void*)GetProcAddress(hComCtl32, "ImageList_DrawIndirect");
+    pImageList_SetImageCount = (void*)GetProcAddress(hComCtl32, "ImageList_SetImageCount");
+    pImageList_CoCreateInstance = (void*)GetProcAddress(hComCtl32, "ImageList_CoCreateInstance");
+    pHIMAGELIST_QueryInterface = (void*)GetProcAddress(hComCtl32, "HIMAGELIST_QueryInterface");
+
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
+    /* Do v6.0 tests */
+    test_ImageList_DrawIndirect();
+    test_shell_imagelist();
+    test_iimagelist();
+
+    testHotspot_v6();
+    DoTest1_v6();
+    DoTest3_v6();
+    testMerge_v6();
+
+    CoUninitialize();
+
+    unload_v6_module(ctx_cookie, hCtx);
 }
index d04223c..85f92b1 100644 (file)
@@ -33,8 +33,6 @@ static HWND create_ipaddress_control (void)
     handle = CreateWindowEx(0, WC_IPADDRESS, NULL,
                            WS_BORDER|WS_VISIBLE, 0, 0, 0, 0,
                            NULL, NULL, NULL, NULL);
-    assert(handle);
-
     return handle;
 }
 
@@ -45,6 +43,11 @@ static void test_get_set_text(void)
     INT r;
 
     hwnd = create_ipaddress_control();
+    if (!hwnd)
+    {
+        win_skip("IPAddress control not implemented\n");
+        return;
+    }
 
     /* check text just after creation */
     r = GetWindowText(hwnd, ip, sizeof(ip)/sizeof(CHAR));
index e94bdaa..6ef82bb 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright 2006 Mike McCormack for CodeWeavers
  * Copyright 2007 George Gov
+ * Copyright 2009 Nikolay Sivov
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 #include <commctrl.h>
 
 #include "wine/test.h"
+#include "v6util.h"
 #include "msg.h"
 
 #define PARENT_SEQ_INDEX       0
 #define PARENT_FULL_SEQ_INDEX  1
 #define LISTVIEW_SEQ_INDEX     2
-#define NUM_MSG_SEQUENCES      3
+#define EDITBOX_SEQ_INDEX      3
+#define COMBINED_SEQ_INDEX     4
+#define NUM_MSG_SEQUENCES      5
 
 #define LISTVIEW_ID 0
 #define HEADER_ID   1
 #define expect2(expected1, expected2, got1, got2) ok(expected1 == got1 && expected2 == got2, \
        "expected (%d,%d), got (%d,%d)\n", expected1, expected2, got1, got2)
 
-HWND hwndparent;
+static const WCHAR testparentclassW[] =
+    {'L','i','s','t','v','i','e','w',' ','t','e','s','t',' ','p','a','r','e','n','t','W', 0};
+
+static HWND hwndparent, hwndparentW;
+/* prevents edit box creation, LVN_BEGINLABELEDIT return value */
+static BOOL blockEdit;
+/* return nonzero on NM_HOVER */
+static BOOL g_block_hover;
+/* dumps LVN_ITEMCHANGED message data */
+static BOOL g_dump_itemchanged;
+/* format reported to control:
+   -1 falls to defproc, anything else returned */
+static INT  notifyFormat;
+/* indicates we're running < 5.80 version */
+static BOOL g_is_below_5;
+/* item data passed to LVN_GETDISPINFOA */
+static LVITEMA g_itema;
+
+static HWND subclass_editbox(HWND hwndListview);
 
 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
 
-static const struct message create_parent_wnd_seq[] = {
-    { WM_GETMINMAXINFO,     sent },
-    { WM_NCCREATE,          sent },
-    { WM_NCCALCSIZE,        sent|wparam, 0 },
-    { WM_CREATE,            sent },
-    { WM_SHOWWINDOW,        sent|wparam, 1 },
-    { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
-    { WM_QUERYNEWPALETTE,   sent|optional },
-    { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
-    { WM_WINDOWPOSCHANGED,  sent|optional },
-    { WM_NCCALCSIZE,        sent|wparam|optional, 1 },
-    { WM_ACTIVATEAPP,       sent|wparam, 1 },
-    { WM_NCACTIVATE,        sent|wparam, 1 },
-    { WM_ACTIVATE,          sent|wparam, 1 },
-    { WM_IME_SETCONTEXT,    sent|wparam|defwinproc|optional, 1 },
-    { WM_IME_NOTIFY,        sent|defwinproc|optional },
-    { WM_SETFOCUS,          sent|wparam|defwinproc, 0 },
-    /* Win9x adds SWP_NOZORDER below */
-    { WM_WINDOWPOSCHANGED,  sent, /*|wparam, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE|SWP_NOCLIENTSIZE|SWP_NOCLIENTMOVE*/ },
-    { WM_NCCALCSIZE,        sent|wparam|optional, 1 },
-    { WM_SIZE,              sent },
-    { WM_MOVE,              sent },
+static const struct message create_ownerdrawfixed_parent_seq[] = {
+    { WM_NOTIFYFORMAT, sent },
+    { WM_QUERYUISTATE, sent|optional }, /* Win2K and higher */
+    { WM_MEASUREITEM, sent },
+    { WM_PARENTNOTIFY, sent },
     { 0 }
 };
 
@@ -71,10 +76,10 @@ static const struct message redraw_listview_seq[] = {
     { WM_PAINT,      sent|id,            0, 0, LISTVIEW_ID },
     { WM_PAINT,      sent|id,            0, 0, HEADER_ID },
     { WM_NCPAINT,    sent|id|defwinproc, 0, 0, HEADER_ID },
-    { WM_ERASEBKGND, sent|id|defwinproc, 0, 0, HEADER_ID },
+    { WM_ERASEBKGND, sent|id|defwinproc|optional, 0, 0, HEADER_ID },
     { WM_NOTIFY,     sent|id|defwinproc, 0, 0, LISTVIEW_ID },
     { WM_NCPAINT,    sent|id|defwinproc, 0, 0, LISTVIEW_ID },
-    { WM_ERASEBKGND, sent|id|defwinproc, 0, 0, LISTVIEW_ID },
+    { WM_ERASEBKGND, sent|id|defwinproc|optional, 0, 0, LISTVIEW_ID },
     { 0 }
 };
 
@@ -123,6 +128,8 @@ static const struct message listview_item_count_seq[] = {
     { LVM_INSERTITEM,     sent },
     { LVM_GETITEMCOUNT,   sent },
     { LVM_DELETEITEM,     sent|wparam, 2 },
+    { WM_NCPAINT,         sent|optional },
+    { WM_ERASEBKGND,      sent|optional },
     { LVM_GETITEMCOUNT,   sent },
     { LVM_DELETEALLITEMS, sent },
     { LVM_GETITEMCOUNT,   sent },
@@ -139,6 +146,8 @@ static const struct message listview_itempos_seq[] = {
     { LVM_INSERTITEM,      sent },
     { LVM_INSERTITEM,      sent },
     { LVM_SETITEMPOSITION, sent|wparam|lparam, 1, MAKELPARAM(10,5) },
+    { WM_NCPAINT,          sent|optional },
+    { WM_ERASEBKGND,       sent|optional },
     { LVM_GETITEMPOSITION, sent|wparam,        1 },
     { LVM_SETITEMPOSITION, sent|wparam|lparam, 2, MAKELPARAM(0,0) },
     { LVM_GETITEMPOSITION, sent|wparam,        2 },
@@ -168,9 +177,128 @@ static const struct message forward_erasebkgnd_parent_seq[] = {
     { 0 }
 };
 
-struct subclass_info
-{
-    WNDPROC oldproc;
+static const struct message ownderdata_select_focus_parent_seq[] = {
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED },
+    { WM_NOTIFY, sent|id, 0, 0, LVN_GETDISPINFOA },
+    { WM_NOTIFY, sent|id|optional, 0, 0, LVN_GETDISPINFOA }, /* version 4.7x */
+    { 0 }
+};
+
+static const struct message ownerdata_setstate_all_parent_seq[] = {
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED },
+    { 0 }
+};
+
+static const struct message ownerdata_defocus_all_parent_seq[] = {
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED },
+    { WM_NOTIFY, sent|id, 0, 0, LVN_GETDISPINFOA },
+    { WM_NOTIFY, sent|id|optional, 0, 0, LVN_GETDISPINFOA },
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED },
+    { 0 }
+};
+
+static const struct message ownerdata_deselect_all_parent_seq[] = {
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ODCACHEHINT },
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED },
+    { 0 }
+};
+
+static const struct message select_all_parent_seq[] = {
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGING },
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED },
+
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGING },
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED },
+
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGING },
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED },
+
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGING },
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED },
+
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGING },
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED },
+    { 0 }
+};
+
+static const struct message textcallback_set_again_parent_seq[] = {
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGING },
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED  },
+    { 0 }
+};
+
+static const struct message single_getdispinfo_parent_seq[] = {
+    { WM_NOTIFY, sent|id, 0, 0, LVN_GETDISPINFOA },
+    { 0 }
+};
+
+static const struct message getitemposition_seq1[] = {
+    { LVM_GETITEMPOSITION, sent|id, 0, 0, LISTVIEW_ID },
+    { 0 }
+};
+
+static const struct message getitemposition_seq2[] = {
+    { LVM_GETITEMPOSITION, sent|id, 0, 0, LISTVIEW_ID },
+    { HDM_GETITEMRECT, sent|id, 0, 0, HEADER_ID },
+    { 0 }
+};
+
+static const struct message editbox_create_pos[] = {
+    /* sequence sent after LVN_BEGINLABELEDIT */
+    /* next two are 4.7x specific */
+    { WM_WINDOWPOSCHANGING, sent },
+    { WM_WINDOWPOSCHANGED, sent|optional },
+
+    { WM_WINDOWPOSCHANGING, sent|optional },
+    { WM_NCCALCSIZE, sent },
+    { WM_WINDOWPOSCHANGED, sent },
+    { WM_MOVE, sent|defwinproc },
+    { WM_SIZE, sent|defwinproc },
+    /* the rest is todo, skipped in 4.7x */
+    { WM_WINDOWPOSCHANGING, sent|optional },
+    { WM_WINDOWPOSCHANGED, sent|optional },
+    { 0 }
+};
+
+static const struct message scroll_parent_seq[] = {
+    { WM_NOTIFY, sent|id, 0, 0, LVN_BEGINSCROLL },
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ENDSCROLL },
+    { 0 }
+};
+
+static const struct message setredraw_seq[] = {
+    { WM_SETREDRAW, sent|id|wparam, FALSE, 0, LISTVIEW_ID },
+    { 0 }
+};
+
+static const struct message lvs_ex_transparentbkgnd_seq[] = {
+    { WM_PRINTCLIENT, sent|lparam, 0, PRF_ERASEBKGND },
+    { 0 }
+};
+
+static const struct message edit_end_nochange[] = {
+    { WM_NOTIFY, sent|id, 0, 0, LVN_ENDLABELEDITA },
+    { WM_NOTIFY, sent|id, 0, 0, NM_CUSTOMDRAW },     /* todo */
+    { WM_NOTIFY, sent|id, 0, 0, NM_SETFOCUS },
+    { 0 }
+};
+
+static const struct message hover_parent[] = {
+    { WM_GETDLGCODE, sent }, /* todo_wine */
+    { WM_NOTIFY, sent|id, 0, 0, NM_HOVER },
+    { 0 }
+};
+
+static const struct message listview_destroy[] = {
+    { 0x0090, sent|optional }, /* Vista */
+    { WM_PARENTNOTIFY, sent },
+    { WM_SHOWWINDOW, sent },
+    { WM_WINDOWPOSCHANGING, sent },
+    { WM_WINDOWPOSCHANGED, sent|optional },
+    { WM_DESTROY, sent },
+    { WM_NOTIFY, sent|id, 0, 0, LVN_DELETEALLITEMS },
+    { WM_NCDESTROY, sent },
+    { 0 }
 };
 
 static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
@@ -184,6 +312,7 @@ 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;
 
     /* log system messages, except for painting */
     if (message < WM_USER &&
@@ -198,9 +327,67 @@ static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LP
         trace("parent: %p, %04x, %08lx, %08lx\n", hwnd, message, wParam, lParam);
 
         add_message(sequences, PARENT_SEQ_INDEX, &msg);
+        add_message(sequences, COMBINED_SEQ_INDEX, &msg);
     }
     add_message(sequences, PARENT_FULL_SEQ_INDEX, &msg);
 
+    switch (message)
+    {
+      case WM_NOTIFY:
+      {
+          switch (((NMHDR*)lParam)->code)
+          {
+          case LVN_BEGINLABELEDIT:
+              /* subclass edit box */
+              if (!blockEdit)
+                  subclass_editbox(((NMHDR*)lParam)->hwndFrom);
+
+              return blockEdit;
+
+          case LVN_ENDLABELEDIT:
+              {
+              /* always accept new item text */
+              NMLVDISPINFO *di = (NMLVDISPINFO*)lParam;
+              trace("LVN_ENDLABELEDIT: text=%s\n", di->item.pszText);
+              return TRUE;
+              }
+          case LVN_BEGINSCROLL:
+          case LVN_ENDSCROLL:
+              {
+              NMLVSCROLL *pScroll = (NMLVSCROLL*)lParam;
+
+              trace("LVN_%sSCROLL: (%d,%d)\n", pScroll->hdr.code == LVN_BEGINSCROLL ?
+                                               "BEGIN" : "END", pScroll->dx, pScroll->dy);
+              }
+              break;
+          case LVN_ITEMCHANGED:
+              if (g_dump_itemchanged)
+              {
+                  NMLISTVIEW *nmlv = (NMLISTVIEW*)lParam;
+                  trace("LVN_ITEMCHANGED: item=%d,new=%x,old=%x,changed=%x\n",
+                         nmlv->iItem, nmlv->uNewState, nmlv->uOldState, nmlv->uChanged);
+              }
+              break;
+          case LVN_GETDISPINFOA:
+              {
+                  NMLVDISPINFOA *dispinfo = (NMLVDISPINFOA*)lParam;
+                  g_itema = dispinfo->item;
+              }
+              break;
+          case NM_HOVER:
+              if (g_block_hover) return 1;
+              break;
+          }
+          break;
+      }
+      case WM_NOTIFYFORMAT:
+      {
+          /* force to return format */
+          if (lParam == NF_QUERY && notifyFormat != -1) return notifyFormat;
+          break;
+      }
+    }
+
     defwndproc_counter++;
     ret = DefWindowProcA(hwnd, message, wParam, lParam);
     defwndproc_counter--;
@@ -208,39 +395,72 @@ static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LP
     return ret;
 }
 
-static BOOL register_parent_wnd_class(void)
+static BOOL register_parent_wnd_class(BOOL Unicode)
 {
-    WNDCLASSA cls;
-
-    cls.style = 0;
-    cls.lpfnWndProc = parent_wnd_proc;
-    cls.cbClsExtra = 0;
-    cls.cbWndExtra = 0;
-    cls.hInstance = GetModuleHandleA(NULL);
-    cls.hIcon = 0;
-    cls.hCursor = LoadCursorA(0, IDC_ARROW);
-    cls.hbrBackground = GetStockObject(WHITE_BRUSH);
-    cls.lpszMenuName = NULL;
-    cls.lpszClassName = "Listview test parent class";
-    return RegisterClassA(&cls);
-}
-
-static HWND create_parent_window(void)
+    WNDCLASSA clsA;
+    WNDCLASSW clsW;
+
+    if (Unicode)
+    {
+        clsW.style = 0;
+        clsW.lpfnWndProc = parent_wnd_proc;
+        clsW.cbClsExtra = 0;
+        clsW.cbWndExtra = 0;
+        clsW.hInstance = GetModuleHandleW(NULL);
+        clsW.hIcon = 0;
+        clsW.hCursor = LoadCursorA(0, IDC_ARROW);
+        clsW.hbrBackground = GetStockObject(WHITE_BRUSH);
+        clsW.lpszMenuName = NULL;
+        clsW.lpszClassName = testparentclassW;
+    }
+    else
+    {
+        clsA.style = 0;
+        clsA.lpfnWndProc = parent_wnd_proc;
+        clsA.cbClsExtra = 0;
+        clsA.cbWndExtra = 0;
+        clsA.hInstance = GetModuleHandleA(NULL);
+        clsA.hIcon = 0;
+        clsA.hCursor = LoadCursorA(0, IDC_ARROW);
+        clsA.hbrBackground = GetStockObject(WHITE_BRUSH);
+        clsA.lpszMenuName = NULL;
+        clsA.lpszClassName = "Listview test parent class";
+    }
+
+    return Unicode ? RegisterClassW(&clsW) : RegisterClassA(&clsA);
+}
+
+static HWND create_parent_window(BOOL Unicode)
 {
-    if (!register_parent_wnd_class())
+    static const WCHAR nameW[] = {'t','e','s','t','p','a','r','e','n','t','n','a','m','e','W',0};
+    HWND hwnd;
+
+    if (!register_parent_wnd_class(Unicode))
         return NULL;
 
-    return CreateWindowEx(0, "Listview test parent class",
-                          "Listview test parent window",
-                          WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
-                          WS_MAXIMIZEBOX | WS_VISIBLE,
-                          0, 0, 100, 100,
-                          GetDesktopWindow(), NULL, GetModuleHandleA(NULL), NULL);
+    blockEdit = FALSE;
+    notifyFormat = -1;
+
+    if (Unicode)
+        hwnd = CreateWindowExW(0, testparentclassW, nameW,
+                               WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
+                               WS_MAXIMIZEBOX | WS_VISIBLE,
+                               0, 0, 100, 100,
+                               GetDesktopWindow(), NULL, GetModuleHandleW(NULL), NULL);
+    else
+        hwnd = CreateWindowExA(0, "Listview test parent class",
+                               "Listview test parent window",
+                               WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
+                               WS_MAXIMIZEBOX | WS_VISIBLE,
+                               0, 0, 100, 100,
+                               GetDesktopWindow(), NULL, GetModuleHandleA(NULL), NULL);
+    SetWindowPos( hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE );
+    return hwnd;
 }
 
 static LRESULT WINAPI listview_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
-    struct subclass_info *info = (struct subclass_info *)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
+    WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
     static LONG defwndproc_counter = 0;
     LRESULT ret;
     struct message msg;
@@ -262,76 +482,63 @@ static LRESULT WINAPI listview_subclass_proc(HWND hwnd, UINT message, WPARAM wPa
     msg.lParam = lParam;
     msg.id = LISTVIEW_ID;
     add_message(sequences, LISTVIEW_SEQ_INDEX, &msg);
+    add_message(sequences, COMBINED_SEQ_INDEX, &msg);
 
     defwndproc_counter++;
-    ret = CallWindowProcA(info->oldproc, hwnd, message, wParam, lParam);
+    ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
     defwndproc_counter--;
     return ret;
 }
 
 static HWND create_listview_control(DWORD style)
 {
-    struct subclass_info *info;
+    WNDPROC oldproc;
     HWND hwnd;
     RECT rect;
 
-    info = HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info));
-    if (!info)
-        return NULL;
-
     GetClientRect(hwndparent, &rect);
     hwnd = CreateWindowExA(0, WC_LISTVIEW, "foo",
-                           WS_CHILD | WS_BORDER | WS_VISIBLE | LVS_REPORT | style,
+                           WS_CHILD | WS_BORDER | WS_VISIBLE | style,
                            0, 0, rect.right, rect.bottom,
                            hwndparent, NULL, GetModuleHandleA(NULL), NULL);
     ok(hwnd != NULL, "gle=%d\n", GetLastError());
 
-    if (!hwnd)
-    {
-        HeapFree(GetProcessHeap(), 0, info);
-        return NULL;
-    }
+    if (!hwnd) return NULL;
 
-    info->oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
-                                            (LONG_PTR)listview_subclass_proc);
-    SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)info);
+    oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
+                                        (LONG_PTR)listview_subclass_proc);
+    SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc);
 
     return hwnd;
 }
 
-static HWND create_custom_listview_control(DWORD style)
+/* unicode listview window with specified parent */
+static HWND create_listview_controlW(DWORD style, HWND parent)
 {
-    struct subclass_info *info;
+    WNDPROC oldproc;
     HWND hwnd;
     RECT rect;
+    static const WCHAR nameW[] = {'f','o','o',0};
 
-    info = HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info));
-    if (!info)
-        return NULL;
-
-    GetClientRect(hwndparent, &rect);
-    hwnd = CreateWindowExA(0, WC_LISTVIEW, "foo",
+    GetClientRect(parent, &rect);
+    hwnd = CreateWindowExW(0, WC_LISTVIEWW, nameW,
                            WS_CHILD | WS_BORDER | WS_VISIBLE | style,
                            0, 0, rect.right, rect.bottom,
-                           hwndparent, NULL, GetModuleHandleA(NULL), NULL);
+                           parent, NULL, GetModuleHandleW(NULL), NULL);
     ok(hwnd != NULL, "gle=%d\n", GetLastError());
 
-    if (!hwnd)
-    {
-        HeapFree(GetProcessHeap(), 0, info);
-        return NULL;
-    }
+    if (!hwnd) return NULL;
 
-    info->oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
-                                            (LONG_PTR)listview_subclass_proc);
-    SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)info);
+    oldproc = (WNDPROC)SetWindowLongPtrW(hwnd, GWLP_WNDPROC,
+                                        (LONG_PTR)listview_subclass_proc);
+    SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc);
 
     return hwnd;
 }
 
 static LRESULT WINAPI header_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
-    struct subclass_info *info = (struct subclass_info *)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
+    WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
     static LONG defwndproc_counter = 0;
     LRESULT ret;
     struct message msg;
@@ -347,28 +554,156 @@ static LRESULT WINAPI header_subclass_proc(HWND hwnd, UINT message, WPARAM wPara
     add_message(sequences, LISTVIEW_SEQ_INDEX, &msg);
 
     defwndproc_counter++;
-    ret = CallWindowProcA(info->oldproc, hwnd, message, wParam, lParam);
+    ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
     defwndproc_counter--;
     return ret;
 }
 
 static HWND subclass_header(HWND hwndListview)
 {
-    struct subclass_info *info;
+    WNDPROC oldproc;
     HWND hwnd;
 
-    info = HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info));
-    if (!info)
-        return NULL;
-
     hwnd = ListView_GetHeader(hwndListview);
-    info->oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
-                                            (LONG_PTR)header_subclass_proc);
-    SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)info);
+    oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
+                                         (LONG_PTR)header_subclass_proc);
+    SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc);
+
+    return hwnd;
+}
+
+static LRESULT WINAPI editbox_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
+    static LONG defwndproc_counter = 0;
+    LRESULT ret;
+    struct message msg;
+
+    msg.message = message;
+    msg.flags = sent|wparam|lparam;
+    if (defwndproc_counter) msg.flags |= defwinproc;
+    msg.wParam = wParam;
+    msg.lParam = lParam;
+
+    /* all we need is sizing */
+    if (message == WM_WINDOWPOSCHANGING ||
+        message == WM_NCCALCSIZE ||
+        message == WM_WINDOWPOSCHANGED ||
+        message == WM_MOVE ||
+        message == WM_SIZE)
+    {
+        add_message(sequences, EDITBOX_SEQ_INDEX, &msg);
+    }
+
+    defwndproc_counter++;
+    ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
+    defwndproc_counter--;
+    return ret;
+}
+
+static HWND subclass_editbox(HWND hwndListview)
+{
+    WNDPROC oldproc;
+    HWND hwnd;
+
+    hwnd = (HWND)SendMessage(hwndListview, LVM_GETEDITCONTROL, 0, 0);
+    oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
+                                         (LONG_PTR)editbox_subclass_proc);
+    SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc);
 
     return hwnd;
 }
 
+/* Performs a single LVM_HITTEST test */
+static void test_lvm_hittest_(HWND hwnd, INT x, INT y, INT item, UINT flags, UINT broken_flags,
+                              BOOL todo_item, BOOL todo_flags, int line)
+{
+    LVHITTESTINFO lpht;
+    DWORD ret;
+
+    lpht.pt.x = x;
+    lpht.pt.y = y;
+    lpht.iSubItem = 10;
+
+    trace("hittesting pt=(%d,%d)\n", lpht.pt.x, lpht.pt.y);
+    ret = SendMessage(hwnd, LVM_HITTEST, 0, (LPARAM)&lpht);
+
+    if (todo_item)
+    {
+        todo_wine
+        {
+            ok_(__FILE__, line)(ret == item, "Expected %d retval, got %d\n", item, ret);
+            ok_(__FILE__, line)(lpht.iItem == item, "Expected %d item, got %d\n", item, lpht.iItem);
+            ok_(__FILE__, line)(lpht.iSubItem == 10, "Expected subitem not overwrited\n");
+        }
+    }
+    else
+    {
+        ok_(__FILE__, line)(ret == item, "Expected %d retval, got %d\n", item, ret);
+        ok_(__FILE__, line)(lpht.iItem == item, "Expected %d item, got %d\n", item, lpht.iItem);
+        ok_(__FILE__, line)(lpht.iSubItem == 10, "Expected subitem not overwrited\n");
+    }
+
+    if (todo_flags)
+    {
+        todo_wine
+            ok_(__FILE__, line)(lpht.flags == flags, "Expected flags 0x%x, got 0x%x\n", flags, lpht.flags);
+    }
+    else if (broken_flags)
+        ok_(__FILE__, line)(lpht.flags == flags || broken(lpht.flags == broken_flags),
+                            "Expected flags %x, got %x\n", flags, lpht.flags);
+    else
+        ok_(__FILE__, line)(lpht.flags == flags, "Expected flags 0x%x, got 0x%x\n", flags, lpht.flags);
+}
+
+#define test_lvm_hittest(a,b,c,d,e,f,g,h) test_lvm_hittest_(a,b,c,d,e,f,g,h,__LINE__)
+
+/* Performs a single LVM_SUBITEMHITTEST test */
+static void test_lvm_subitemhittest_(HWND hwnd, INT x, INT y, INT item, INT subitem, UINT flags,
+                                     BOOL todo_item, BOOL todo_subitem, BOOL todo_flags, int line)
+{
+    LVHITTESTINFO lpht;
+    DWORD ret;
+
+    lpht.pt.x = x;
+    lpht.pt.y = y;
+
+    trace("subhittesting pt=(%d,%d)\n", lpht.pt.x, lpht.pt.y);
+    ret = SendMessage(hwnd, LVM_SUBITEMHITTEST, 0, (LPARAM)&lpht);
+
+    if (todo_item)
+    {
+        todo_wine
+        {
+            ok_(__FILE__, line)(ret == item, "Expected %d retval, got %d\n", item, ret);
+            ok_(__FILE__, line)(lpht.iItem == item, "Expected %d item, got %d\n", item, lpht.iItem);
+        }
+    }
+    else
+    {
+        ok_(__FILE__, line)(ret == item, "Expected %d retval, got %d\n", item, ret);
+        ok_(__FILE__, line)(lpht.iItem == item, "Expected %d item, got %d\n", item, lpht.iItem);
+    }
+
+    if (todo_subitem)
+    {
+        todo_wine
+            ok_(__FILE__, line)(lpht.iSubItem == subitem, "Expected subitem %d, got %d\n", subitem, lpht.iSubItem);
+    }
+    else
+        ok_(__FILE__, line)(lpht.iSubItem == subitem, "Expected subitem %d, got %d\n", subitem, lpht.iSubItem);
+
+    if (todo_flags)
+    {
+        todo_wine
+            ok_(__FILE__, line)(lpht.flags == flags, "Expected flags 0x%x, got 0x%x\n", flags, lpht.flags);
+    }
+    else
+        ok_(__FILE__, line)(lpht.flags == flags, "Expected flags 0x%x, got 0x%x\n", flags, lpht.flags);
+}
+
+#define test_lvm_subitemhittest(a,b,c,d,e,f,g,h,i) test_lvm_subitemhittest_(a,b,c,d,e,f,g,h,i,__LINE__)
+
 static void test_images(void)
 {
     HWND hwnd;
@@ -392,7 +727,9 @@ static void test_images(void)
                 10, 10, 100, 200, hwndparent, NULL, NULL, NULL);
     ok(hwnd != NULL, "failed to create listview window\n");
 
-    r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, 0x940);
+    r = SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0,
+                    LVS_EX_UNDERLINEHOT | LVS_EX_FLATSB | LVS_EX_ONECLICKACTIVATE);
+
     ok(r == 0, "should return zero\n");
 
     r = SendMessage(hwnd, LVM_SETIMAGELIST, 0, (LPARAM)himl);
@@ -491,7 +828,12 @@ static void test_checkboxes(void)
     item.mask = LVIF_STATE;
     item.stateMask = 0xffff;
     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
-    ok(item.state == 0x1ccc, "state %x\n", item.state);
+    if (item.state != 0x1ccc)
+    {
+        win_skip("LVS_EX_CHECKBOXES style is unavailable. Skipping.\n");
+        DestroyWindow(hwnd);
+        return;
+    }
 
     /* Now add an item without specifying a state and check that its state goes to 0x1000 */
     item.iItem = 2;
@@ -827,59 +1169,99 @@ static void test_items(void)
     r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM) &item);
     ok(r != 0, "ret %d\n", r);
 
+    /* set text to callback value already having it */
+    r = SendMessage(hwnd, LVM_DELETEALLITEMS, 0, 0);
+    expect(TRUE, r);
+    memset (&item, 0, sizeof (item));
+    item.mask  = LVIF_TEXT;
+    item.pszText = LPSTR_TEXTCALLBACK;
+    item.iItem = 0;
+    r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM) &item);
+    ok(r == 0, "ret %d\n", r);
+    memset (&item, 0, sizeof (item));
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    item.pszText = LPSTR_TEXTCALLBACK;
+    r = SendMessage(hwnd, LVM_SETITEMTEXT, 0 , (LPARAM) &item);
+    expect(TRUE, r);
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, textcallback_set_again_parent_seq,
+                "check callback text comparison rule", FALSE);
+
     DestroyWindow(hwnd);
 }
 
 static void test_columns(void)
 {
-    HWND hwnd, hwndheader;
-    LVCOLUMN column;
-    DWORD rc;
+    HWND hwnd;
+    LVCOLUMNA column;
+    LVITEMA item;
     INT order[2];
+    CHAR buff[5];
+    DWORD rc;
 
-    hwnd = CreateWindowEx(0, "SysListView32", "foo", LVS_REPORT,
+    hwnd = CreateWindowExA(0, "SysListView32", "foo", LVS_REPORT,
                 10, 10, 100, 200, hwndparent, NULL, NULL, NULL);
     ok(hwnd != NULL, "failed to create listview window\n");
 
     /* Add a column with no mask */
     memset(&column, 0xcc, sizeof(column));
     column.mask = 0;
-    rc = ListView_InsertColumn(hwnd, 0, &column);
-    ok(rc==0, "Inserting column with no mask failed with %d\n", rc);
+    rc = SendMessageA(hwnd, LVM_INSERTCOLUMNA, 0, (LPARAM)&column);
+    ok(rc == 0, "Inserting column with no mask failed with %d\n", rc);
 
     /* Check its width */
-    rc = ListView_GetColumnWidth(hwnd, 0);
-    ok(rc==10 ||
-       broken(rc==0), /* win9x */
+    rc = SendMessageA(hwnd, LVM_GETCOLUMNWIDTH, 0, 0);
+    ok(rc == 10 || broken(rc == 0) /* win9x */,
        "Inserting column with no mask failed to set width to 10 with %d\n", rc);
 
     DestroyWindow(hwnd);
 
     /* LVM_GETCOLUMNORDERARRAY */
-    hwnd = create_listview_control(0);
-    hwndheader = subclass_header(hwnd);
+    hwnd = create_listview_control(LVS_REPORT);
+    subclass_header(hwnd);
 
     memset(&column, 0, sizeof(column));
     column.mask = LVCF_WIDTH;
     column.cx = 100;
-    rc = ListView_InsertColumn(hwnd, 0, &column);
+    rc = SendMessageA(hwnd, LVM_INSERTCOLUMNA, 0, (LPARAM)&column);
     ok(rc == 0, "Inserting column failed with %d\n", rc);
 
     column.cx = 200;
-    rc = ListView_InsertColumn(hwnd, 1, &column);
+    rc = SendMessageA(hwnd, LVM_INSERTCOLUMNA, 1, (LPARAM)&column);
     ok(rc == 1, "Inserting column failed with %d\n", rc);
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
-    rc = SendMessage(hwnd, LVM_GETCOLUMNORDERARRAY, 2, (LPARAM)&order);
-    ok(rc != 0, "Expected LVM_GETCOLUMNORDERARRAY to succeed\n");
+    rc = SendMessageA(hwnd, LVM_GETCOLUMNORDERARRAY, 2, (LPARAM)&order);
+    ok(rc == 1, "Expected LVM_GETCOLUMNORDERARRAY to succeed\n");
     ok(order[0] == 0, "Expected order 0, got %d\n", order[0]);
     ok(order[1] == 1, "Expected order 1, got %d\n", order[1]);
 
     ok_sequence(sequences, LISTVIEW_SEQ_INDEX, listview_getorderarray_seq, "get order array", FALSE);
 
+    /* after column added subitem is considered as present */
+    insert_item(hwnd, 0);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    item.pszText = buff;
+    item.cchTextMax = sizeof(buff);
+    item.iItem = 0;
+    item.iSubItem = 1;
+    item.mask = LVIF_TEXT;
+    memset(&g_itema, 0, sizeof(g_itema));
+    rc = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
+    ok(rc == 1, "got %d\n", rc);
+    ok(g_itema.iSubItem == 1, "got %d\n", g_itema.iSubItem);
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, single_getdispinfo_parent_seq,
+        "get subitem text after column added", FALSE);
+
     DestroyWindow(hwnd);
 }
+
 /* test setting imagelist between WM_NCCREATE and WM_CREATE */
 static WNDPROC listviewWndProc;
 static HIMAGELIST test_create_imagelist;
@@ -907,6 +1289,8 @@ static void test_create(void)
     LVCOLUMNA col;
     RECT rect;
     WNDCLASSEX cls;
+    DWORD style;
+
     cls.cbSize = sizeof(WNDCLASSEX);
     ok(GetClassInfoEx(GetModuleHandle(NULL), "SysListView32", &cls), "GetClassInfoEx failed\n");
     listviewWndProc = cls.lpfnWndProc;
@@ -918,6 +1302,15 @@ static void test_create(void)
     hList = CreateWindow("MyListView32", "Test", WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, GetModuleHandle(NULL), 0);
     ok((HIMAGELIST)SendMessage(hList, LVM_GETIMAGELIST, 0, 0) == test_create_imagelist, "Image list not obtained\n");
     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
+
+    if (!IsWindow(hHeader))
+    {
+        /* version 4.0 */
+        win_skip("LVM_GETHEADER not implemented. Skipping.\n");
+        DestroyWindow(hList);
+        return;
+    }
+
     ok(IsWindow(hHeader) && IsWindowVisible(hHeader), "Listview not in report mode\n");
     ok(hHeader == GetDlgItem(hList, 0), "Expected header as dialog item\n");
     DestroyWindow(hList);
@@ -937,6 +1330,8 @@ static void test_create(void)
     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
     ok(IsWindow(hHeader), "Header should be created\n");
     ok(hHeader == GetDlgItem(hList, 0), "Expected header as dialog item\n");
+    style = GetWindowLong(hHeader, GWL_STYLE);
+    ok(!(style & HDS_HIDDEN), "Not expected HDS_HIDDEN\n");
     DestroyWindow(hList);
 
     hList = CreateWindow("SysListView32", "Test", WS_VISIBLE|LVS_LIST, 0, 0, 100, 100, NULL, NULL,
@@ -1033,12 +1428,14 @@ static void test_create(void)
     ok(NULL == GetDlgItem(hList, 0), "NULL dialog item expected\n");
     SendMessage(hList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_HEADERDRAGDROP);
     hHeader = (HWND)SendMessage(hList, LVM_GETHEADER, 0, 0);
-    ok(IsWindow(hHeader), "Header should be created\n");
+    ok(IsWindow(hHeader) ||
+       broken(!IsWindow(hHeader)), /* 4.7x common controls */
+       "Header should be created\n");
     ok(hHeader == GetDlgItem(hList, 0), "Expected header as dialog item\n");
     DestroyWindow(hList);
 
     /* not report style accepts LVS_EX_HEADERDRAGDROP too */
-    hList = create_custom_listview_control(0);
+    hList = create_listview_control(LVS_ICON);
     SendMessage(hList, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_HEADERDRAGDROP);
     r = SendMessage(hList, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
     ok(r & LVS_EX_HEADERDRAGDROP, "Expected LVS_EX_HEADERDRAGDROP to be set\n");
@@ -1061,17 +1458,24 @@ static void test_create(void)
     ok(NULL == GetDlgItem(hList, 0), "NULL dialog item expected\n");
 
     DestroyWindow(hList);
+
+    /* WM_MEASUREITEM should be sent when created with LVS_OWNERDRAWFIXED */
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    hList = create_listview_control(LVS_OWNERDRAWFIXED | LVS_REPORT);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, create_ownerdrawfixed_parent_seq,
+                "created with LVS_OWNERDRAWFIXED|LVS_REPORT - parent seq", FALSE);
+    DestroyWindow(hList);
 }
 
 static void test_redraw(void)
 {
-    HWND hwnd, hwndheader;
+    HWND hwnd;
     HDC hdc;
     BOOL res;
     DWORD r;
 
-    hwnd = create_listview_control(0);
-    hwndheader = subclass_header(hwnd);
+    hwnd = create_listview_control(LVS_REPORT);
+    subclass_header(hwnd);
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
@@ -1167,7 +1571,7 @@ static void test_customdraw(void)
     HWND hwnd;
     WNDPROC oldwndproc;
 
-    hwnd = create_listview_control(0);
+    hwnd = create_listview_control(LVS_REPORT);
 
     insert_column(hwnd, 0);
     insert_column(hwnd, 1);
@@ -1193,10 +1597,10 @@ static void test_icon_spacing(void)
     WORD w, h;
     DWORD r;
 
-    hwnd = create_custom_listview_control(LVS_ICON);
+    hwnd = create_listview_control(LVS_ICON);
     ok(hwnd != NULL, "failed to create a listview window\n");
 
-    r = SendMessage(hwnd, WM_NOTIFYFORMAT, (WPARAM)hwndparent, (LPARAM)NF_REQUERY);
+    r = SendMessage(hwnd, WM_NOTIFYFORMAT, (WPARAM)hwndparent, NF_REQUERY);
     expect(NFR_ANSI, r);
 
     /* reset the icon spacing to defaults */
@@ -1217,6 +1621,13 @@ static void test_icon_spacing(void)
        "Expected %d, got %d\n", MAKELONG(w, h), r);
 
     r = SendMessage(hwnd, LVM_SETICONSPACING, 0, MAKELPARAM(25, 35));
+    if (r == 0)
+    {
+        /* version 4.0 */
+        win_skip("LVM_SETICONSPACING unimplemented. Skipping.\n");
+        DestroyWindow(hwnd);
+        return;
+    }
     expect(MAKELONG(20,30), r);
 
     r = SendMessage(hwnd, LVM_SETICONSPACING, 0, MAKELPARAM(-1,-1));
@@ -1239,7 +1650,7 @@ static void test_color(void)
     COLORREF color;
     COLORREF colors[4] = {RGB(0,0,0), RGB(100,50,200), CLR_NONE, RGB(255,255,255)};
 
-    hwnd = create_listview_control(0);
+    hwnd = create_listview_control(LVS_REPORT);
     ok(hwnd != NULL, "failed to create a listview window\n");
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
@@ -1277,6 +1688,11 @@ static void test_item_count(void)
 
     HWND hwnd;
     DWORD r;
+    HDC hdc;
+    HFONT hOldFont;
+    TEXTMETRICA tm;
+    RECT rect;
+    INT height;
 
     LVITEM item0;
     LVITEM item1;
@@ -1285,9 +1701,22 @@ static void test_item_count(void)
     static CHAR item1text[] = "item1";
     static CHAR item2text[] = "item2";
 
-    hwnd = create_listview_control(0);
+    hwnd = create_listview_control(LVS_REPORT);
     ok(hwnd != NULL, "failed to create a listview window\n");
 
+    /* resize in dpiaware manner to fit all 3 items added */
+    hdc = GetDC(0);
+    hOldFont = SelectObject(hdc, GetStockObject(SYSTEM_FONT));
+    GetTextMetricsA(hdc, &tm);
+    /* 2 extra pixels for bounds and header border */
+    height = tm.tmHeight + 2;
+    SelectObject(hdc, hOldFont);
+    ReleaseDC(0, hdc);
+
+    GetWindowRect(hwnd, &rect);
+    /* 3 items + 1 header + 1 to be sure */
+    MoveWindow(hwnd, 0, 0, rect.right - rect.left, 5 * height, FALSE);
+
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
     trace("test item count\n");
@@ -1375,7 +1804,7 @@ static void test_item_position(void)
     static CHAR item1text[] = "item1";
     static CHAR item2text[] = "item2";
 
-    hwnd = create_custom_listview_control(LVS_ICON);
+    hwnd = create_listview_control(LVS_ICON);
     ok(hwnd != NULL, "failed to create a listview window\n");
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
@@ -1440,7 +1869,7 @@ static void test_getorigin(void)
 
     position.x = position.y = 0;
 
-    hwnd = create_custom_listview_control(LVS_ICON);
+    hwnd = create_listview_control(LVS_ICON);
     ok(hwnd != NULL, "failed to create a listview window\n");
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
     trace("test get origin results\n");
@@ -1449,7 +1878,7 @@ static void test_getorigin(void)
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
     DestroyWindow(hwnd);
 
-    hwnd = create_custom_listview_control(LVS_SMALLICON);
+    hwnd = create_listview_control(LVS_SMALLICON);
     ok(hwnd != NULL, "failed to create a listview window\n");
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
     trace("test get origin results\n");
@@ -1458,7 +1887,7 @@ static void test_getorigin(void)
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
     DestroyWindow(hwnd);
 
-    hwnd = create_custom_listview_control(LVS_LIST);
+    hwnd = create_listview_control(LVS_LIST);
     ok(hwnd != NULL, "failed to create a listview window\n");
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
     trace("test get origin results\n");
@@ -1467,7 +1896,7 @@ static void test_getorigin(void)
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
     DestroyWindow(hwnd);
 
-    hwnd = create_custom_listview_control(LVS_REPORT);
+    hwnd = create_listview_control(LVS_REPORT);
     ok(hwnd != NULL, "failed to create a listview window\n");
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
     trace("test get origin results\n");
@@ -1496,6 +1925,7 @@ static void test_multiselect(void)
     BYTE kstate[256];
     select_task task;
     LONG_PTR style;
+    LVITEMA item;
 
     static struct t_select_task task_list[] = {
         { "using VK_DOWN", 0, VK_DOWN, -1, -1 },
@@ -1505,7 +1935,7 @@ static void test_multiselect(void)
     };
 
 
-    hwnd = create_listview_control(0);
+    hwnd = create_listview_control(LVS_REPORT);
 
     for (i=0;i<items;i++) {
            insert_item(hwnd, 0);
@@ -1554,12 +1984,30 @@ static void test_multiselect(void)
     DestroyWindow(hwnd);
 
     /* make multiple selection, then switch to LVS_SINGLESEL */
-    hwnd = create_listview_control(0);
+    hwnd = create_listview_control(LVS_REPORT);
     for (i=0;i<items;i++) {
            insert_item(hwnd, 0);
     }
     item_count = (int)SendMessage(hwnd, LVM_GETITEMCOUNT, 0, 0);
     expect(items,item_count);
+
+    /* try with NULL pointer */
+    r = SendMessageA(hwnd, LVM_SETITEMSTATE, 0, 0);
+    expect(FALSE, r);
+
+    /* select all, check notifications */
+    ListView_SetItemState(hwnd, -1, 0, LVIS_SELECTED);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    item.stateMask = LVIS_SELECTED;
+    item.state     = LVIS_SELECTED;
+    r = SendMessageA(hwnd, LVM_SETITEMSTATE, -1, (LPARAM)&item);
+    expect(TRUE, r);
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, select_all_parent_seq,
+                "select all notification", FALSE);
+
     /* deselect all items */
     ListView_SetItemState(hwnd, -1, 0, LVIS_SELECTED);
     SendMessage(hwnd, LVM_SETSELECTIONMARK, 0, -1);
@@ -1570,7 +2018,6 @@ static void test_multiselect(void)
     r = SendMessage(hwnd, LVM_GETSELECTEDCOUNT, 0, 0);
     expect(3, r);
     r = SendMessage(hwnd, LVM_GETSELECTIONMARK, 0, 0);
-todo_wine
     expect(-1, r);
 
     style = GetWindowLongPtrA(hwnd, GWL_STYLE);
@@ -1602,9 +2049,38 @@ todo_wine
     r = SendMessage(hwnd, LVM_GETSELECTEDCOUNT, 0, 0);
     expect(1, r);
     r = SendMessage(hwnd, LVM_GETSELECTIONMARK, 0, 0);
-todo_wine
     expect(-1, r);
 
+    /* try to select all on LVS_SINGLESEL */
+    memset(&item, 0, sizeof(item));
+    item.stateMask = LVIS_SELECTED;
+    r = SendMessage(hwnd, LVM_SETITEMSTATE, -1, (LPARAM)&item);
+    expect(TRUE, r);
+    SendMessage(hwnd, LVM_SETSELECTIONMARK, 0, -1);
+
+    item.stateMask = LVIS_SELECTED;
+    item.state     = LVIS_SELECTED;
+    r = SendMessage(hwnd, LVM_SETITEMSTATE, -1, (LPARAM)&item);
+    expect(FALSE, r);
+
+    r = ListView_GetSelectedCount(hwnd);
+    expect(0, r);
+    r = ListView_GetSelectionMark(hwnd);
+    expect(-1, r);
+
+    /* try to deselect all on LVS_SINGLESEL */
+    item.stateMask = LVIS_SELECTED;
+    item.state     = LVIS_SELECTED;
+    r = SendMessage(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item);
+    expect(TRUE, r);
+
+    item.stateMask = LVIS_SELECTED;
+    item.state     = 0;
+    r = SendMessage(hwnd, LVM_SETITEMSTATE, -1, (LPARAM)&item);
+    expect(TRUE, r);
+    r = ListView_GetSelectedCount(hwnd);
+    expect(0, r);
+
     DestroyWindow(hwnd);
 }
 
@@ -1613,24 +2089,22 @@ static void test_subitem_rect(void)
     HWND hwnd;
     DWORD r;
     LVCOLUMN col;
-    RECT rect;
+    RECT rect, rect2;
+    INT arr[3];
 
     /* test LVM_GETSUBITEMRECT for header */
-    hwnd = create_listview_control(0);
+    hwnd = create_listview_control(LVS_REPORT);
     ok(hwnd != NULL, "failed to create a listview window\n");
     /* add some columns */
     memset(&col, 0, sizeof(LVCOLUMN));
     col.mask = LVCF_WIDTH;
     col.cx = 100;
-    r = -1;
     r = SendMessage(hwnd, LVM_INSERTCOLUMN, 0, (LPARAM)&col);
     expect(0, r);
     col.cx = 150;
-    r = -1;
     r = SendMessage(hwnd, LVM_INSERTCOLUMN, 1, (LPARAM)&col);
     expect(1, r);
     col.cx = 200;
-    r = -1;
     r = SendMessage(hwnd, LVM_INSERTCOLUMN, 2, (LPARAM)&col);
     expect(2, r);
     /* item = -1 means header, subitem index is 1 based */
@@ -1662,23 +2136,153 @@ todo_wine
 todo_wine
     expect(3, rect.top);
 
-    DestroyWindow(hwnd);
+    /* item LVS_REPORT padding isn't applied to subitems */
+    insert_item(hwnd, 0);
 
-    /* try it for non LVS_REPORT style */
-    hwnd = CreateWindow("SysListView32", "Test", LVS_ICON, 0, 0, 100, 100, NULL, NULL,
-                         GetModuleHandle(NULL), 0);
     rect.left = LVIR_BOUNDS;
     rect.top  = 1;
-    rect.right = rect.bottom = -10;
-    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, -1, (LPARAM)&rect);
-    ok(r == 0, "Expected not-null LRESULT\n");
-    /* rect is unchanged */
-    expect(0, rect.left);
-    expect(-10, rect.right);
-    expect(1, rect.top);
-    expect(-10, rect.bottom);
-    DestroyWindow(hwnd);
-}
+    rect.right = rect.bottom = 0;
+    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, 0, (LPARAM)&rect);
+    ok(r != 0, "Expected not-null LRESULT\n");
+    expect(100, rect.left);
+    expect(250, rect.right);
+
+    rect.left = LVIR_ICON;
+    rect.top  = 1;
+    rect.right = rect.bottom = 0;
+    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, 0, (LPARAM)&rect);
+    ok(r != 0, "Expected not-null LRESULT\n");
+    /* no icon attached - zero width rectangle, with no left padding */
+    expect(100, rect.left);
+    expect(100, rect.right);
+
+    rect.left = LVIR_LABEL;
+    rect.top  = 1;
+    rect.right = rect.bottom = 0;
+    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, 0, (LPARAM)&rect);
+    ok(r != 0, "Expected not-null LRESULT\n");
+    /* same as full LVIR_BOUNDS */
+    expect(100, rect.left);
+    expect(250, rect.right);
+
+    SendMessage(hwnd, LVM_SCROLL, 10, 0);
+
+    rect.left = LVIR_BOUNDS;
+    rect.top  = 1;
+    rect.right = rect.bottom = 0;
+    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, 0, (LPARAM)&rect);
+    ok(r != 0, "Expected not-null LRESULT\n");
+    expect(90, rect.left);
+    expect(240, rect.right);
+
+    SendMessage(hwnd, LVM_SCROLL, -10, 0);
+
+    DestroyWindow(hwnd);
+
+    /* test subitem rects after re-arranging columns */
+    hwnd = create_listview_control(LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+    memset(&col, 0, sizeof(LVCOLUMN));
+    col.mask = LVCF_WIDTH;
+
+    col.cx = 100;
+    r = SendMessage(hwnd, LVM_INSERTCOLUMN, 0, (LPARAM)&col);
+    expect(0, r);
+
+    col.cx = 200;
+    r = SendMessage(hwnd, LVM_INSERTCOLUMN, 1, (LPARAM)&col);
+    expect(1, r);
+
+    col.cx = 300;
+    r = SendMessage(hwnd, LVM_INSERTCOLUMN, 2, (LPARAM)&col);
+    expect(2, r);
+
+    insert_item(hwnd, 0);
+    insert_item(hwnd, 1);
+
+    /* wrong item is refused for main item */
+    rect.left = LVIR_BOUNDS;
+    rect.top  = 0;
+    rect.right = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, 2, (LPARAM)&rect);
+    ok(r == FALSE, "got %d\n", r);
+
+    /* for subitems rectangle is calculated even if there's no item added */
+    rect.left = LVIR_BOUNDS;
+    rect.top  = 1;
+    rect.right = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, 1, (LPARAM)&rect);
+    ok(r == TRUE, "got %d\n", r);
+
+    rect2.left = LVIR_BOUNDS;
+    rect2.top  = 1;
+    rect2.right = rect2.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, 2, (LPARAM)&rect2);
+todo_wine {
+    ok(r == TRUE, "got %d\n", r);
+    expect(rect.right, rect2.right);
+    expect(rect.left, rect2.left);
+    expect(rect.bottom, rect2.top);
+    ok(rect2.bottom > rect2.top, "expected not zero height\n");
+}
+
+    arr[0] = 1; arr[1] = 0; arr[2] = 2;
+    r = SendMessage(hwnd, LVM_SETCOLUMNORDERARRAY, 3, (LPARAM)arr);
+    expect(TRUE, r);
+
+    rect.left = LVIR_BOUNDS;
+    rect.top  = 0;
+    rect.right = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, 0, (LPARAM)&rect);
+    ok(r == TRUE, "got %d\n", r);
+    expect(0, rect.left);
+    expect(600, rect.right);
+
+    rect.left = LVIR_BOUNDS;
+    rect.top  = 1;
+    rect.right = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, 0, (LPARAM)&rect);
+    ok(r == TRUE, "got %d\n", r);
+    expect(0, rect.left);
+    expect(200, rect.right);
+
+    rect2.left = LVIR_BOUNDS;
+    rect2.top  = 1;
+    rect2.right = rect2.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, 1, (LPARAM)&rect2);
+    ok(r == TRUE, "got %d\n", r);
+    expect(0, rect2.left);
+    expect(200, rect2.right);
+    /* items are of the same height */
+    ok(rect2.top > 0, "expected positive item height\n");
+    expect(rect.bottom, rect2.top);
+    expect(rect.bottom * 2 - rect.top, rect2.bottom);
+
+    rect.left = LVIR_BOUNDS;
+    rect.top  = 2;
+    rect.right = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, 0, (LPARAM)&rect);
+    ok(r == TRUE, "got %d\n", r);
+    expect(300, rect.left);
+    expect(600, rect.right);
+
+    DestroyWindow(hwnd);
+
+    /* try it for non LVS_REPORT style */
+    hwnd = CreateWindow("SysListView32", "Test", LVS_ICON, 0, 0, 100, 100, NULL, NULL,
+                         GetModuleHandle(NULL), 0);
+    rect.left = LVIR_BOUNDS;
+    rect.top  = 1;
+    rect.right = rect.bottom = -10;
+    r = SendMessage(hwnd, LVM_GETSUBITEMRECT, -1, (LPARAM)&rect);
+    ok(r == 0, "Expected not-null LRESULT\n");
+    /* rect is unchanged */
+    expect(0, rect.left);
+    expect(-10, rect.right);
+    expect(1, rect.top);
+    expect(-10, rect.bottom);
+    DestroyWindow(hwnd);
+}
 
 /* comparison callback for test_sorting */
 static INT WINAPI test_CallBackCompare(LPARAM first, LPARAM second, LPARAM lParam)
@@ -1696,7 +2300,7 @@ static void test_sorting(void)
     static CHAR names[][5] = {"A", "B", "C", "D", "0"};
     CHAR buff[10];
 
-    hwnd = create_listview_control(0);
+    hwnd = create_listview_control(LVS_REPORT);
     ok(hwnd != NULL, "failed to create a listview window\n");
 
     /* insert some items */
@@ -1746,7 +2350,7 @@ static void test_sorting(void)
     DestroyWindow(hwnd);
 
     /* switch to LVS_SORTASCENDING when some items added */
-    hwnd = create_listview_control(0);
+    hwnd = create_listview_control(LVS_REPORT);
     ok(hwnd != NULL, "failed to create a listview window\n");
 
     item.mask = LVIF_TEXT;
@@ -1873,24 +2477,31 @@ static void test_ownerdata(void)
     LVITEMA item;
 
     /* it isn't possible to set LVS_OWNERDATA after creation */
-    hwnd = create_listview_control(0);
-    ok(hwnd != NULL, "failed to create a listview window\n");
-    style = GetWindowLongPtrA(hwnd, GWL_STYLE);
-    ok(!(style & LVS_OWNERDATA) && style, "LVS_OWNERDATA isn't expected\n");
+    if (g_is_below_5)
+    {
+        win_skip("set LVS_OWNERDATA after creation leads to crash on < 5.80\n");
+    }
+    else
+    {
+        hwnd = create_listview_control(LVS_REPORT);
+        ok(hwnd != NULL, "failed to create a listview window\n");
+        style = GetWindowLongPtrA(hwnd, GWL_STYLE);
+        ok(!(style & LVS_OWNERDATA) && style, "LVS_OWNERDATA isn't expected\n");
 
-    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+        flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
-    ret = SetWindowLongPtrA(hwnd, GWL_STYLE, style | LVS_OWNERDATA);
-    ok(ret == style, "Expected set GWL_STYLE to succeed\n");
-    ok_sequence(sequences, LISTVIEW_SEQ_INDEX, listview_ownerdata_switchto_seq,
+        ret = SetWindowLongPtrA(hwnd, GWL_STYLE, style | LVS_OWNERDATA);
+        ok(ret == style, "Expected set GWL_STYLE to succeed\n");
+        ok_sequence(sequences, LISTVIEW_SEQ_INDEX, listview_ownerdata_switchto_seq,
                 "try to switch to LVS_OWNERDATA seq", FALSE);
 
-    style = GetWindowLongPtrA(hwnd, GWL_STYLE);
-    ok(!(style & LVS_OWNERDATA), "LVS_OWNERDATA isn't expected\n");
-    DestroyWindow(hwnd);
+        style = GetWindowLongPtrA(hwnd, GWL_STYLE);
+        ok(!(style & LVS_OWNERDATA), "LVS_OWNERDATA isn't expected\n");
+        DestroyWindow(hwnd);
+    }
 
     /* try to set LVS_OWNERDATA after creation just having it */
-    hwnd = create_listview_control(LVS_OWNERDATA);
+    hwnd = create_listview_control(LVS_OWNERDATA | LVS_REPORT);
     ok(hwnd != NULL, "failed to create a listview window\n");
     style = GetWindowLongPtrA(hwnd, GWL_STYLE);
     ok(style & LVS_OWNERDATA, "LVS_OWNERDATA is expected\n");
@@ -1904,23 +2515,30 @@ static void test_ownerdata(void)
     DestroyWindow(hwnd);
 
     /* try to remove LVS_OWNERDATA after creation just having it */
-    hwnd = create_listview_control(LVS_OWNERDATA);
-    ok(hwnd != NULL, "failed to create a listview window\n");
-    style = GetWindowLongPtrA(hwnd, GWL_STYLE);
-    ok(style & LVS_OWNERDATA, "LVS_OWNERDATA is expected\n");
+    if (g_is_below_5)
+    {
+        win_skip("remove LVS_OWNERDATA after creation leads to crash on < 5.80\n");
+    }
+    else
+    {
+        hwnd = create_listview_control(LVS_OWNERDATA | LVS_REPORT);
+        ok(hwnd != NULL, "failed to create a listview window\n");
+        style = GetWindowLongPtrA(hwnd, GWL_STYLE);
+        ok(style & LVS_OWNERDATA, "LVS_OWNERDATA is expected\n");
 
-    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+        flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
-    ret = SetWindowLongPtrA(hwnd, GWL_STYLE, style & ~LVS_OWNERDATA);
-    ok(ret == style, "Expected set GWL_STYLE to succeed\n");
-    ok_sequence(sequences, LISTVIEW_SEQ_INDEX, listview_ownerdata_switchto_seq,
+        ret = SetWindowLongPtrA(hwnd, GWL_STYLE, style & ~LVS_OWNERDATA);
+        ok(ret == style, "Expected set GWL_STYLE to succeed\n");
+        ok_sequence(sequences, LISTVIEW_SEQ_INDEX, listview_ownerdata_switchto_seq,
                 "try to switch to LVS_OWNERDATA seq", FALSE);
-    style = GetWindowLongPtrA(hwnd, GWL_STYLE);
-    ok(style & LVS_OWNERDATA, "LVS_OWNERDATA is expected\n");
-    DestroyWindow(hwnd);
+        style = GetWindowLongPtrA(hwnd, GWL_STYLE);
+        ok(style & LVS_OWNERDATA, "LVS_OWNERDATA is expected\n");
+        DestroyWindow(hwnd);
+    }
 
     /* try select an item */
-    hwnd = create_listview_control(LVS_OWNERDATA);
+    hwnd = create_listview_control(LVS_OWNERDATA | LVS_REPORT);
     ok(hwnd != NULL, "failed to create a listview window\n");
     res = SendMessageA(hwnd, LVM_SETITEMCOUNT, 1, 0);
     ok(res != 0, "Expected LVM_SETITEMCOUNT to succeed\n");
@@ -1938,7 +2556,7 @@ static void test_ownerdata(void)
     DestroyWindow(hwnd);
 
     /* LVM_SETITEM is unsupported on LVS_OWNERDATA */
-    hwnd = create_listview_control(LVS_OWNERDATA);
+    hwnd = create_listview_control(LVS_OWNERDATA | LVS_REPORT);
     ok(hwnd != NULL, "failed to create a listview window\n");
     res = SendMessageA(hwnd, LVM_SETITEMCOUNT, 1, 0);
     ok(res != 0, "Expected LVM_SETITEMCOUNT to succeed\n");
@@ -1952,6 +2570,224 @@ static void test_ownerdata(void)
     res = SendMessageA(hwnd, LVM_SETITEM, 0, (LPARAM)&item);
     expect(FALSE, res);
     DestroyWindow(hwnd);
+
+    /* check notifications after focused/selected changed */
+    hwnd = create_listview_control(LVS_OWNERDATA | LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+    res = SendMessageA(hwnd, LVM_SETITEMCOUNT, 20, 0);
+    ok(res != 0, "Expected LVM_SETITEMCOUNT to succeed\n");
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    memset(&item, 0, sizeof(item));
+    item.stateMask = LVIS_SELECTED;
+    item.state     = LVIS_SELECTED;
+    res = SendMessageA(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item);
+    expect(TRUE, res);
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, ownderdata_select_focus_parent_seq,
+                "ownerdata select notification", TRUE);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    memset(&item, 0, sizeof(item));
+    item.stateMask = LVIS_FOCUSED;
+    item.state     = LVIS_FOCUSED;
+    res = SendMessageA(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item);
+    expect(TRUE, res);
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, ownderdata_select_focus_parent_seq,
+                "ownerdata focus notification", TRUE);
+
+    /* select all, check notifications */
+    item.stateMask = LVIS_SELECTED;
+    item.state     = 0;
+    res = SendMessageA(hwnd, LVM_SETITEMSTATE, -1, (LPARAM)&item);
+    expect(TRUE, res);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    item.stateMask = LVIS_SELECTED;
+    item.state     = LVIS_SELECTED;
+
+    g_dump_itemchanged = TRUE;
+    res = SendMessageA(hwnd, LVM_SETITEMSTATE, -1, (LPARAM)&item);
+    expect(TRUE, res);
+    g_dump_itemchanged = FALSE;
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, ownerdata_setstate_all_parent_seq,
+                "ownerdata select all notification", TRUE);
+
+    /* select all again, note that all items are selected already */
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    item.stateMask = LVIS_SELECTED;
+    item.state     = LVIS_SELECTED;
+    g_dump_itemchanged = TRUE;
+    res = SendMessageA(hwnd, LVM_SETITEMSTATE, -1, (LPARAM)&item);
+    expect(TRUE, res);
+    g_dump_itemchanged = FALSE;
+    ok_sequence(sequences, PARENT_SEQ_INDEX, ownerdata_setstate_all_parent_seq,
+                "ownerdata select all notification", TRUE);
+    /* deselect all */
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    item.stateMask = LVIS_SELECTED;
+    item.state     = 0;
+    g_dump_itemchanged = TRUE;
+    res = SendMessageA(hwnd, LVM_SETITEMSTATE, -1, (LPARAM)&item);
+    expect(TRUE, res);
+    g_dump_itemchanged = FALSE;
+    ok_sequence(sequences, PARENT_SEQ_INDEX, ownerdata_deselect_all_parent_seq,
+                "ownerdata deselect all notification", TRUE);
+
+    /* select one, then deselect all */
+    item.stateMask = LVIS_SELECTED;
+    item.state     = LVIS_SELECTED;
+    res = SendMessageA(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item);
+    expect(TRUE, res);
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    item.stateMask = LVIS_SELECTED;
+    item.state     = 0;
+    g_dump_itemchanged = TRUE;
+    res = SendMessageA(hwnd, LVM_SETITEMSTATE, -1, (LPARAM)&item);
+    expect(TRUE, res);
+    g_dump_itemchanged = FALSE;
+    ok_sequence(sequences, PARENT_SEQ_INDEX, ownerdata_deselect_all_parent_seq,
+                "ownerdata select all notification", TRUE);
+
+    /* remove focused, try to focus all */
+    item.stateMask = LVIS_FOCUSED;
+    item.state     = LVIS_FOCUSED;
+    res = SendMessageA(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item);
+    expect(TRUE, res);
+    item.stateMask = LVIS_FOCUSED;
+    item.state     = 0;
+    res = SendMessageA(hwnd, LVM_SETITEMSTATE, -1, (LPARAM)&item);
+    expect(TRUE, res);
+    item.stateMask = LVIS_FOCUSED;
+    res = SendMessageA(hwnd, LVM_GETITEMSTATE, 0, LVIS_FOCUSED);
+    expect(0, res);
+    /* setting all to focused returns failure value */
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    item.stateMask = LVIS_FOCUSED;
+    item.state     = LVIS_FOCUSED;
+    g_dump_itemchanged = TRUE;
+    res = SendMessageA(hwnd, LVM_SETITEMSTATE, -1, (LPARAM)&item);
+    expect(FALSE, res);
+    g_dump_itemchanged = FALSE;
+    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
+                "ownerdata focus all notification", FALSE);
+    /* focus single item, remove all */
+    item.stateMask = LVIS_FOCUSED;
+    item.state     = LVIS_FOCUSED;
+    res = SendMessage(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item);
+    expect(TRUE, res);
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    item.stateMask = LVIS_FOCUSED;
+    item.state     = 0;
+    g_dump_itemchanged = TRUE;
+    res = SendMessageA(hwnd, LVM_SETITEMSTATE, -1, (LPARAM)&item);
+    expect(TRUE, res);
+    g_dump_itemchanged = FALSE;
+    ok_sequence(sequences, PARENT_SEQ_INDEX, ownerdata_defocus_all_parent_seq,
+                "ownerdata remove focus all notification", TRUE);
+    /* set all cut */
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    item.stateMask = LVIS_CUT;
+    item.state     = LVIS_CUT;
+    g_dump_itemchanged = TRUE;
+    res = SendMessageA(hwnd, LVM_SETITEMSTATE, -1, (LPARAM)&item);
+    expect(TRUE, res);
+    g_dump_itemchanged = FALSE;
+    ok_sequence(sequences, PARENT_SEQ_INDEX, ownerdata_setstate_all_parent_seq,
+                "ownerdata cut all notification", TRUE);
+    /* all marked cut, try again */
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    item.stateMask = LVIS_CUT;
+    item.state     = LVIS_CUT;
+    g_dump_itemchanged = TRUE;
+    res = SendMessageA(hwnd, LVM_SETITEMSTATE, -1, (LPARAM)&item);
+    expect(TRUE, res);
+    g_dump_itemchanged = FALSE;
+    ok_sequence(sequences, PARENT_SEQ_INDEX, ownerdata_setstate_all_parent_seq,
+                "ownerdata cut all notification #2", TRUE);
+
+    DestroyWindow(hwnd);
+
+    /* check notifications on LVM_GETITEM */
+    /* zero callback mask */
+    hwnd = create_listview_control(LVS_OWNERDATA | LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+    res = SendMessageA(hwnd, LVM_SETITEMCOUNT, 1, 0);
+    ok(res != 0, "Expected LVM_SETITEMCOUNT to succeed\n");
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    memset(&item, 0, sizeof(item));
+    item.stateMask = LVIS_SELECTED;
+    item.mask      = LVIF_STATE;
+    res = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
+    expect(TRUE, res);
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
+                "ownerdata getitem selected state 1", FALSE);
+
+    /* non zero callback mask but not we asking for */
+    res = SendMessageA(hwnd, LVM_SETCALLBACKMASK, LVIS_OVERLAYMASK, 0);
+    expect(TRUE, res);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    memset(&item, 0, sizeof(item));
+    item.stateMask = LVIS_SELECTED;
+    item.mask      = LVIF_STATE;
+    res = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
+    expect(TRUE, res);
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
+                "ownerdata getitem selected state 2", FALSE);
+
+    /* LVIS_OVERLAYMASK callback mask, asking for index */
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    memset(&item, 0, sizeof(item));
+    item.stateMask = LVIS_OVERLAYMASK;
+    item.mask      = LVIF_STATE;
+    res = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
+    expect(TRUE, res);
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, single_getdispinfo_parent_seq,
+                "ownerdata getitem selected state 2", FALSE);
+
+    DestroyWindow(hwnd);
+
+    /* LVS_SORTASCENDING/LVS_SORTDESCENDING aren't compatible with LVS_OWNERDATA */
+    hwnd = create_listview_control(LVS_OWNERDATA | LVS_SORTASCENDING | LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+    style = GetWindowLongPtrA(hwnd, GWL_STYLE);
+    ok(style & LVS_OWNERDATA, "Expected LVS_OWNERDATA\n");
+    ok(style & LVS_SORTASCENDING, "Expected LVS_SORTASCENDING to be set\n");
+    SetWindowLongPtrA(hwnd, GWL_STYLE, style & ~LVS_SORTASCENDING);
+    style = GetWindowLongPtrA(hwnd, GWL_STYLE);
+    ok(!(style & LVS_SORTASCENDING), "Expected LVS_SORTASCENDING not set\n");
+    DestroyWindow(hwnd);
+    /* apparently it's allowed to switch these style on after creation */
+    hwnd = create_listview_control(LVS_OWNERDATA | LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+    style = GetWindowLongPtrA(hwnd, GWL_STYLE);
+    ok(style & LVS_OWNERDATA, "Expected LVS_OWNERDATA\n");
+    SetWindowLongPtrA(hwnd, GWL_STYLE, style | LVS_SORTASCENDING);
+    style = GetWindowLongPtrA(hwnd, GWL_STYLE);
+    ok(style & LVS_SORTASCENDING, "Expected LVS_SORTASCENDING to be set\n");
+    DestroyWindow(hwnd);
+
+    hwnd = create_listview_control(LVS_OWNERDATA | LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+    style = GetWindowLongPtrA(hwnd, GWL_STYLE);
+    ok(style & LVS_OWNERDATA, "Expected LVS_OWNERDATA\n");
+    SetWindowLongPtrA(hwnd, GWL_STYLE, style | LVS_SORTDESCENDING);
+    style = GetWindowLongPtrA(hwnd, GWL_STYLE);
+    ok(style & LVS_SORTDESCENDING, "Expected LVS_SORTDESCENDING to be set\n");
+    DestroyWindow(hwnd);
 }
 
 static void test_norecompute(void)
@@ -1963,7 +2799,7 @@ static void test_norecompute(void)
     DWORD res;
 
     /* self containing control */
-    hwnd = create_listview_control(0);
+    hwnd = create_listview_control(LVS_REPORT);
     ok(hwnd != NULL, "failed to create a listview window\n");
     memset(&item, 0, sizeof(item));
     item.mask = LVIF_TEXT | LVIF_STATE;
@@ -2003,7 +2839,7 @@ static void test_norecompute(void)
     DestroyWindow(hwnd);
 
     /* LVS_OWNERDATA */
-    hwnd = create_listview_control(LVS_OWNERDATA);
+    hwnd = create_listview_control(LVS_OWNERDATA | LVS_REPORT);
     ok(hwnd != NULL, "failed to create a listview window\n");
 
     item.mask = LVIF_STATE;
@@ -2032,7 +2868,7 @@ static void test_nosortheader(void)
     HWND hwnd, header;
     LONG_PTR style;
 
-    hwnd = create_listview_control(0);
+    hwnd = create_listview_control(LVS_REPORT);
     ok(hwnd != NULL, "failed to create a listview window\n");
 
     header = (HWND)SendMessageA(hwnd, LVM_GETHEADER, 0, 0);
@@ -2050,7 +2886,7 @@ static void test_nosortheader(void)
     DestroyWindow(hwnd);
 
     /* create with LVS_NOSORTHEADER */
-    hwnd = create_listview_control(LVS_NOSORTHEADER);
+    hwnd = create_listview_control(LVS_NOSORTHEADER | LVS_REPORT);
     ok(hwnd != NULL, "failed to create a listview window\n");
 
     header = (HWND)SendMessageA(hwnd, LVM_GETHEADER, 0, 0);
@@ -2068,34 +2904,1650 @@ static void test_nosortheader(void)
     DestroyWindow(hwnd);
 }
 
-START_TEST(listview)
+static void test_setredraw(void)
 {
-    HMODULE hComctl32;
-    BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
+    HWND hwnd;
+    DWORD_PTR style;
+    DWORD ret;
+    HDC hdc;
+    RECT rect;
 
-    hComctl32 = GetModuleHandleA("comctl32.dll");
-    pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
-    if (pInitCommonControlsEx)
-    {
-        INITCOMMONCONTROLSEX iccex;
-        iccex.dwSize = sizeof(iccex);
-        iccex.dwICC  = ICC_LISTVIEW_CLASSES;
-        pInitCommonControlsEx(&iccex);
-    }
-    else
-        InitCommonControls();
+    hwnd = create_listview_control(LVS_OWNERDATA | LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
 
-    init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
+    /* Passing WM_SETREDRAW to DefWinProc removes WS_VISIBLE.
+       ListView seems to handle it internally without DefWinProc */
+
+    /* default value first */
+    ret = SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
+    expect(0, ret);
+    /* disable */
+    style = GetWindowLongPtr(hwnd, GWL_STYLE);
+    ok(style & WS_VISIBLE, "Expected WS_VISIBLE to be set\n");
+    ret = SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
+    expect(0, ret);
+    style = GetWindowLongPtr(hwnd, GWL_STYLE);
+    ok(style & WS_VISIBLE, "Expected WS_VISIBLE to be set\n");
+    ret = SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
+    expect(0, ret);
+
+    /* check update rect after redrawing */
+    ret = SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
+    expect(0, ret);
+    InvalidateRect(hwnd, NULL, FALSE);
+    RedrawWindow(hwnd, NULL, NULL, RDW_UPDATENOW);
+    rect.right = rect.bottom = 1;
+    GetUpdateRect(hwnd, &rect, FALSE);
+    expect(0, rect.right);
+    expect(0, rect.bottom);
+
+    /* WM_ERASEBKGND */
+    hdc = GetWindowDC(hwndparent);
+    ret = SendMessage(hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0);
+    expect(TRUE, ret);
+    ret = SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
+    expect(0, ret);
+    ret = SendMessage(hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0);
+    expect(TRUE, ret);
+    ret = SendMessage(hwnd, WM_SETREDRAW, TRUE, 0);
+    expect(0, ret);
+    ReleaseDC(hwndparent, hdc);
 
+    /* check notification messages to show that repainting is disabled */
+    ret = SendMessage(hwnd, LVM_SETITEMCOUNT, 1, 0);
+    expect(TRUE, ret);
+    ret = SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
+    expect(0, ret);
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
-    hwndparent = create_parent_window();
-    ok_sequence(sequences, PARENT_SEQ_INDEX, create_parent_wnd_seq, "create parent window", TRUE);
+
+    InvalidateRect(hwnd, NULL, TRUE);
+    UpdateWindow(hwnd);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
+                "redraw after WM_SETREDRAW (FALSE)", FALSE);
+
+    ret = SendMessage(hwnd, LVM_SETBKCOLOR, 0, CLR_NONE);
+    expect(TRUE, ret);
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    InvalidateRect(hwnd, NULL, TRUE);
+    UpdateWindow(hwnd);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
+                "redraw after WM_SETREDRAW (FALSE) with CLR_NONE bkgnd", FALSE);
 
-    test_images();
-    test_checkboxes();
-    test_items();
-    test_create();
+    /* message isn't forwarded to header */
+    subclass_header(hwnd);
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    ret = SendMessage(hwnd, WM_SETREDRAW, FALSE, 0);
+    expect(0, ret);
+    ok_sequence(sequences, LISTVIEW_SEQ_INDEX, setredraw_seq,
+                "WM_SETREDRAW: not forwarded to header", FALSE);
+
+    DestroyWindow(hwnd);
+}
+
+static void test_hittest(void)
+{
+    HWND hwnd;
+    DWORD r;
+    RECT bounds;
+    LVITEMA item;
+    static CHAR text[] = "1234567890ABCDEFGHIJKLMNOPQRST";
+    POINT pos;
+    INT x, y;
+    HIMAGELIST himl, himl2;
+    HBITMAP hbmp;
+
+    hwnd = create_listview_control(LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+
+    /* LVS_REPORT with a single subitem (2 columns) */
+    insert_column(hwnd, 0);
+    insert_column(hwnd, 1);
+    insert_item(hwnd, 0);
+
+    item.iSubItem = 0;
+    /* the only purpose of that line is to be as long as a half item rect */
+    item.pszText  = text;
+    r = SendMessage(hwnd, LVM_SETITEMTEXT, 0, (LPARAM)&item);
+    expect(TRUE, r);
+
+    r = SendMessage(hwnd, LVM_SETCOLUMNWIDTH, 0, MAKELPARAM(100, 0));
+    expect(TRUE, r);
+    r = SendMessage(hwnd, LVM_SETCOLUMNWIDTH, 1, MAKELPARAM(100, 0));
+    expect(TRUE, r);
+
+    memset(&bounds, 0, sizeof(bounds));
+    bounds.left = LVIR_BOUNDS;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&bounds);
+    ok(bounds.bottom - bounds.top > 0, "Expected non zero item height\n");
+    ok(bounds.right - bounds.left > 0, "Expected non zero item width\n");
+    r = SendMessage(hwnd, LVM_GETITEMPOSITION, 0, (LPARAM)&pos);
+    expect(TRUE, r);
+
+    /* LVS_EX_FULLROWSELECT not set, no icons attached */
+
+    /* outside columns by x position - valid is [0, 199] */
+    x = -1;
+    y = pos.y + (bounds.bottom - bounds.top) / 2;
+    test_lvm_hittest(hwnd, x, y, -1, LVHT_TOLEFT, 0, FALSE, FALSE);
+    test_lvm_subitemhittest(hwnd, x, y, -1, -1, LVHT_NOWHERE, FALSE, FALSE, FALSE);
+
+    x = pos.x + 50; /* column half width */
+    y = pos.y + (bounds.bottom - bounds.top) / 2;
+    test_lvm_hittest(hwnd, x, y, 0, LVHT_ONITEMLABEL, 0, FALSE, FALSE);
+    test_lvm_subitemhittest(hwnd, x, y, 0, 0, LVHT_ONITEMLABEL, FALSE, FALSE, FALSE);
+    x = pos.x + 150; /* outside column */
+    y = pos.y + (bounds.bottom - bounds.top) / 2;
+    test_lvm_hittest(hwnd, x, y, -1, LVHT_TORIGHT, 0, FALSE, FALSE);
+    test_lvm_subitemhittest(hwnd, x, y, 0, 1, LVHT_ONITEMLABEL, FALSE, FALSE, FALSE);
+    y = (bounds.bottom - bounds.top) / 2;
+    test_lvm_hittest(hwnd, x, y, -1, LVHT_TORIGHT, 0, FALSE, TRUE);
+    test_lvm_subitemhittest(hwnd, x, y, 0, 1, LVHT_ONITEMLABEL, FALSE, FALSE, FALSE);
+    /* outside possible client rectangle (to right) */
+    x = pos.x + 500;
+    y = pos.y + (bounds.bottom - bounds.top) / 2;
+    test_lvm_hittest(hwnd, x, y, -1, LVHT_TORIGHT, 0, FALSE, FALSE);
+    test_lvm_subitemhittest(hwnd, x, y, -1, -1, LVHT_NOWHERE, FALSE, FALSE, FALSE);
+    y = (bounds.bottom - bounds.top) / 2;
+    test_lvm_hittest(hwnd, x, y, -1, LVHT_TORIGHT, 0, FALSE, TRUE);
+    test_lvm_subitemhittest(hwnd, x, y, -1, -1, LVHT_NOWHERE, FALSE, FALSE, FALSE);
+    /* subitem returned with -1 item too */
+    x = pos.x + 150;
+    y = -10;
+    test_lvm_subitemhittest(hwnd, x, y, -1, 1, LVHT_NOWHERE, FALSE, FALSE, FALSE);
+    /* parent client area is 100x100 by default */
+    MoveWindow(hwnd, 0, 0, 300, 100, FALSE);
+    x = pos.x + 150; /* outside column */
+    y = pos.y + (bounds.bottom - bounds.top) / 2;
+    test_lvm_hittest(hwnd, x, y, -1, LVHT_NOWHERE, 0, FALSE, FALSE);
+    test_lvm_subitemhittest(hwnd, x, y, 0, 1, LVHT_ONITEMLABEL, FALSE, FALSE, FALSE);
+    y = (bounds.bottom - bounds.top) / 2;
+    test_lvm_hittest(hwnd, x, y, -1, LVHT_NOWHERE, 0, FALSE, TRUE);
+    test_lvm_subitemhittest(hwnd, x, y, 0, 1, LVHT_ONITEMLABEL, FALSE, FALSE, FALSE);
+    /* the same with LVS_EX_FULLROWSELECT */
+    SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
+    x = pos.x + 150; /* outside column */
+    y = pos.y + (bounds.bottom - bounds.top) / 2;
+    test_lvm_hittest(hwnd, x, y, 0, LVHT_ONITEM, LVHT_ONITEMLABEL, FALSE, FALSE);
+    test_lvm_subitemhittest(hwnd, x, y, 0, 1, LVHT_ONITEMLABEL, FALSE, FALSE, FALSE);
+    y = (bounds.bottom - bounds.top) / 2;
+    test_lvm_subitemhittest(hwnd, x, y, 0, 1, LVHT_ONITEMLABEL, FALSE, FALSE, FALSE);
+    MoveWindow(hwnd, 0, 0, 100, 100, FALSE);
+    x = pos.x + 150; /* outside column */
+    y = pos.y + (bounds.bottom - bounds.top) / 2;
+    test_lvm_hittest(hwnd, x, y, -1, LVHT_TORIGHT, 0, FALSE, FALSE);
+    test_lvm_subitemhittest(hwnd, x, y, 0, 1, LVHT_ONITEMLABEL, FALSE, FALSE, FALSE);
+    y = (bounds.bottom - bounds.top) / 2;
+    test_lvm_hittest(hwnd, x, y, -1, LVHT_TORIGHT, 0, FALSE, TRUE);
+    test_lvm_subitemhittest(hwnd, x, y, 0, 1, LVHT_ONITEMLABEL, FALSE, FALSE, FALSE);
+    /* outside possible client rectangle (to right) */
+    x = pos.x + 500;
+    y = pos.y + (bounds.bottom - bounds.top) / 2;
+    test_lvm_hittest(hwnd, x, y, -1, LVHT_TORIGHT, 0, FALSE, FALSE);
+    test_lvm_subitemhittest(hwnd, x, y, -1, -1, LVHT_NOWHERE, FALSE, FALSE, FALSE);
+    y = (bounds.bottom - bounds.top) / 2;
+    test_lvm_hittest(hwnd, x, y, -1, LVHT_TORIGHT, 0, FALSE, TRUE);
+    test_lvm_subitemhittest(hwnd, x, y, -1, -1, LVHT_NOWHERE, FALSE, FALSE, FALSE);
+    /* try with icons, state icons index is 1 based so at least 2 bitmaps needed */
+    himl = ImageList_Create(16, 16, 0, 4, 4);
+    ok(himl != NULL, "failed to create imagelist\n");
+    hbmp = CreateBitmap(16, 16, 1, 1, NULL);
+    ok(hbmp != NULL, "failed to create bitmap\n");
+    r = ImageList_Add(himl, hbmp, 0);
+    ok(r == 0, "should be zero\n");
+    hbmp = CreateBitmap(16, 16, 1, 1, NULL);
+    ok(hbmp != NULL, "failed to create bitmap\n");
+    r = ImageList_Add(himl, hbmp, 0);
+    ok(r == 1, "should be one\n");
+
+    r = SendMessage(hwnd, LVM_SETIMAGELIST, LVSIL_STATE, (LPARAM)himl);
+    ok(r == 0, "should return zero\n");
+
+    item.mask = LVIF_IMAGE;
+    item.iImage = 0;
+    item.iItem = 0;
+    item.iSubItem = 0;
+    r = SendMessage(hwnd, LVM_SETITEM, 0, (LPARAM)&item);
+    expect(TRUE, r);
+    /* on state icon */
+    x = pos.x + 8;
+    y = pos.y + (bounds.bottom - bounds.top) / 2;
+    test_lvm_hittest(hwnd, x, y, 0, LVHT_ONITEMSTATEICON, 0, FALSE, FALSE);
+    test_lvm_subitemhittest(hwnd, x, y, 0, 0, LVHT_ONITEMSTATEICON, FALSE, FALSE, FALSE);
+    y = (bounds.bottom - bounds.top) / 2;
+    test_lvm_subitemhittest(hwnd, x, y, 0, 0, LVHT_ONITEMSTATEICON, FALSE, FALSE, FALSE);
+
+    /* state icons indices are 1 based, check with valid index */
+    item.mask = LVIF_STATE;
+    item.state = INDEXTOSTATEIMAGEMASK(1);
+    item.stateMask = LVIS_STATEIMAGEMASK;
+    item.iItem = 0;
+    item.iSubItem = 0;
+    r = SendMessage(hwnd, LVM_SETITEM, 0, (LPARAM)&item);
+    expect(TRUE, r);
+    /* on state icon */
+    x = pos.x + 8;
+    y = pos.y + (bounds.bottom - bounds.top) / 2;
+    test_lvm_hittest(hwnd, x, y, 0, LVHT_ONITEMSTATEICON, 0, FALSE, FALSE);
+    test_lvm_subitemhittest(hwnd, x, y, 0, 0, LVHT_ONITEMSTATEICON, FALSE, FALSE, FALSE);
+    y = (bounds.bottom - bounds.top) / 2;
+    test_lvm_subitemhittest(hwnd, x, y, 0, 0, LVHT_ONITEMSTATEICON, FALSE, FALSE, FALSE);
+
+    himl2 = (HIMAGELIST)SendMessage(hwnd, LVM_SETIMAGELIST, LVSIL_STATE, 0);
+    ok(himl2 == himl, "should return handle\n");
+
+    r = SendMessage(hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM)himl);
+    ok(r == 0, "should return zero\n");
+    /* on item icon */
+    x = pos.x + 8;
+    y = pos.y + (bounds.bottom - bounds.top) / 2;
+    test_lvm_hittest(hwnd, x, y, 0, LVHT_ONITEMICON, 0, FALSE, FALSE);
+    test_lvm_subitemhittest(hwnd, x, y, 0, 0, LVHT_ONITEMICON, FALSE, FALSE, FALSE);
+    y = (bounds.bottom - bounds.top) / 2;
+    test_lvm_subitemhittest(hwnd, x, y, 0, 0, LVHT_ONITEMICON, FALSE, FALSE, FALSE);
+
+    DestroyWindow(hwnd);
+}
+
+static void test_getviewrect(void)
+{
+    HWND hwnd;
+    DWORD r;
+    RECT rect;
+    LVITEMA item;
+
+    hwnd = create_listview_control(LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+
+    /* empty */
+    r = SendMessage(hwnd, LVM_GETVIEWRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+
+    insert_column(hwnd, 0);
+    insert_column(hwnd, 1);
+
+    memset(&item, 0, sizeof(item));
+    item.iItem = 0;
+    item.iSubItem = 0;
+    SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&item);
+
+    r = SendMessage(hwnd, LVM_SETCOLUMNWIDTH, 0, MAKELPARAM(100, 0));
+    expect(TRUE, r);
+    r = SendMessage(hwnd, LVM_SETCOLUMNWIDTH, 1, MAKELPARAM(120, 0));
+    expect(TRUE, r);
+
+    rect.left = rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETVIEWRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+    /* left is set to (2e31-1) - XP SP2 */
+    expect(0, rect.right);
+    expect(0, rect.top);
+    expect(0, rect.bottom);
+
+    /* switch to LVS_ICON */
+    SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~LVS_REPORT);
+
+    rect.left = rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETVIEWRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+    expect(0, rect.left);
+    expect(0, rect.top);
+    /* precise value differs for 2k, XP and Vista */
+    ok(rect.bottom > 0, "Expected positive bottom value, got %d\n", rect.bottom);
+    ok(rect.right  > 0, "Expected positive right value, got %d\n", rect.right);
+
+    DestroyWindow(hwnd);
+}
+
+static void test_getitemposition(void)
+{
+    HWND hwnd, header;
+    DWORD r;
+    POINT pt;
+    RECT rect;
+
+    hwnd = create_listview_control(LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+    header = subclass_header(hwnd);
+
+    /* LVS_REPORT, single item, no columns added */
+    insert_item(hwnd, 0);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    pt.x = pt.y = -1;
+    r = SendMessage(hwnd, LVM_GETITEMPOSITION, 0, (LPARAM)&pt);
+    expect(TRUE, r);
+    ok_sequence(sequences, LISTVIEW_SEQ_INDEX, getitemposition_seq1, "get item position 1", FALSE);
+
+    /* LVS_REPORT, single item, single column */
+    insert_column(hwnd, 0);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    pt.x = pt.y = -1;
+    r = SendMessage(hwnd, LVM_GETITEMPOSITION, 0, (LPARAM)&pt);
+    expect(TRUE, r);
+    ok_sequence(sequences, LISTVIEW_SEQ_INDEX, getitemposition_seq2, "get item position 2", TRUE);
+
+    memset(&rect, 0, sizeof(rect));
+    SendMessage(header, HDM_GETITEMRECT, 0, (LPARAM)&rect);
+    /* some padding? */
+    expect(2, pt.x);
+    /* offset by header height */
+    expect(rect.bottom - rect.top, pt.y);
+
+    DestroyWindow(hwnd);
+}
+
+static void test_columnscreation(void)
+{
+    HWND hwnd, header;
+    DWORD r;
+
+    hwnd = create_listview_control(LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+
+    insert_item(hwnd, 0);
+
+    /* headers columns aren't created automatically */
+    header = (HWND)SendMessage(hwnd, LVM_GETHEADER, 0, 0);
+    ok(IsWindow(header), "Expected header handle\n");
+    r = SendMessage(header, HDM_GETITEMCOUNT, 0, 0);
+    expect(0, r);
+
+    DestroyWindow(hwnd);
+}
+
+static void test_getitemrect(void)
+{
+    HWND hwnd;
+    HIMAGELIST himl;
+    HBITMAP hbm;
+    RECT rect;
+    DWORD r;
+    LVITEMA item;
+    LVCOLUMNA col;
+    INT order[2];
+    POINT pt;
+
+    /* rectangle isn't empty for empty text items */
+    hwnd = create_listview_control(LVS_LIST);
+    memset(&item, 0, sizeof(item));
+    item.mask = 0;
+    item.iItem = 0;
+    r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&item);
+    expect(0, r);
+    rect.left = LVIR_LABEL;
+    SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(0, rect.left);
+    expect(0, rect.top);
+    todo_wine expect(96, rect.right);
+    DestroyWindow(hwnd);
+
+    hwnd = create_listview_control(LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+
+    /* empty item */
+    memset(&item, 0, sizeof(item));
+    item.iItem = 0;
+    item.iSubItem = 0;
+    r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&item);
+    expect(0, r);
+
+    rect.left = LVIR_BOUNDS;
+    rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+
+    /* zero width rectangle with no padding */
+    expect(0, rect.left);
+    expect(0, rect.right);
+
+    insert_column(hwnd, 0);
+    insert_column(hwnd, 1);
+
+    col.mask = LVCF_WIDTH;
+    col.cx   = 50;
+    r = SendMessage(hwnd, LVM_SETCOLUMN, 0, (LPARAM)&col);
+    expect(TRUE, r);
+
+    col.mask = LVCF_WIDTH;
+    col.cx   = 100;
+    r = SendMessage(hwnd, LVM_SETCOLUMN, 1, (LPARAM)&col);
+    expect(TRUE, r);
+
+    rect.left = LVIR_BOUNDS;
+    rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+
+    /* still no left padding */
+    expect(0, rect.left);
+    expect(150, rect.right);
+
+    rect.left = LVIR_SELECTBOUNDS;
+    rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+    /* padding */
+    expect(2, rect.left);
+
+    rect.left = LVIR_LABEL;
+    rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+    /* padding, column width */
+    expect(2, rect.left);
+    expect(50, rect.right);
+
+    /* no icons attached */
+    rect.left = LVIR_ICON;
+    rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+    /* padding */
+    expect(2, rect.left);
+    expect(2, rect.right);
+
+    /* change order */
+    order[0] = 1; order[1] = 0;
+    r = SendMessage(hwnd, LVM_SETCOLUMNORDERARRAY, 2, (LPARAM)&order);
+    expect(TRUE, r);
+    pt.x = -1;
+    r = SendMessage(hwnd, LVM_GETITEMPOSITION, 0, (LPARAM)&pt);
+    expect(TRUE, r);
+    /* 1 indexed column width + padding */
+    expect(102, pt.x);
+    /* rect is at zero too */
+    rect.left = LVIR_BOUNDS;
+    rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+    expect(0, rect.left);
+    /* just width sum */
+    expect(150, rect.right);
+
+    rect.left = LVIR_SELECTBOUNDS;
+    rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+    /* column width + padding */
+    expect(102, rect.left);
+
+    /* back to initial order */
+    order[0] = 0; order[1] = 1;
+    r = SendMessage(hwnd, LVM_SETCOLUMNORDERARRAY, 2, (LPARAM)&order);
+    expect(TRUE, r);
+
+    /* state icons */
+    himl = ImageList_Create(16, 16, 0, 2, 2);
+    ok(himl != NULL, "failed to create imagelist\n");
+    hbm = CreateBitmap(16, 16, 1, 1, NULL);
+    ok(hbm != NULL, "failed to create bitmap\n");
+    r = ImageList_Add(himl, hbm, 0);
+    ok(r == 0, "should be zero\n");
+    hbm = CreateBitmap(16, 16, 1, 1, NULL);
+    ok(hbm != NULL, "failed to create bitmap\n");
+    r = ImageList_Add(himl, hbm, 0);
+    ok(r == 1, "should be one\n");
+
+    r = SendMessage(hwnd, LVM_SETIMAGELIST, LVSIL_STATE, (LPARAM)himl);
+    ok(r == 0, "should return zero\n");
+
+    item.mask = LVIF_STATE;
+    item.state = INDEXTOSTATEIMAGEMASK(1);
+    item.stateMask = LVIS_STATEIMAGEMASK;
+    item.iItem = 0;
+    item.iSubItem = 0;
+    r = SendMessage(hwnd, LVM_SETITEM, 0, (LPARAM)&item);
+    expect(TRUE, r);
+
+    /* icon bounds */
+    rect.left = LVIR_ICON;
+    rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+    /* padding + stateicon width */
+    expect(18, rect.left);
+    expect(18, rect.right);
+    /* label bounds */
+    rect.left = LVIR_LABEL;
+    rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+    /* padding + stateicon width -> column width */
+    expect(18, rect.left);
+    expect(50, rect.right);
+
+    r = SendMessage(hwnd, LVM_SETIMAGELIST, LVSIL_STATE, 0);
+    ok(r != 0, "should return current list handle\n");
+
+    r = SendMessage(hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM)himl);
+    ok(r == 0, "should return zero\n");
+
+    item.mask = LVIF_STATE | LVIF_IMAGE;
+    item.iImage = 1;
+    item.state = 0;
+    item.stateMask = ~0;
+    item.iItem = 0;
+    item.iSubItem = 0;
+    r = SendMessage(hwnd, LVM_SETITEM, 0, (LPARAM)&item);
+    expect(TRUE, r);
+
+    /* icon bounds */
+    rect.left = LVIR_ICON;
+    rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+    /* padding, icon width */
+    expect(2, rect.left);
+    expect(18, rect.right);
+    /* label bounds */
+    rect.left = LVIR_LABEL;
+    rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+    /* padding + icon width -> column width */
+    expect(18, rect.left);
+    expect(50, rect.right);
+
+    /* select bounds */
+    rect.left = LVIR_SELECTBOUNDS;
+    rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+    /* padding, column width */
+    expect(2, rect.left);
+    expect(50, rect.right);
+
+    /* try with indentation */
+    item.mask = LVIF_INDENT;
+    item.iIndent = 1;
+    item.iItem = 0;
+    item.iSubItem = 0;
+    r = SendMessage(hwnd, LVM_SETITEM, 0, (LPARAM)&item);
+    expect(TRUE, r);
+
+    /* bounds */
+    rect.left = LVIR_BOUNDS;
+    rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+    /* padding + 1 icon width, column width */
+    expect(0, rect.left);
+    expect(150, rect.right);
+
+    /* select bounds */
+    rect.left = LVIR_SELECTBOUNDS;
+    rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+    /* padding + 1 icon width, column width */
+    expect(2 + 16, rect.left);
+    expect(50, rect.right);
+
+    /* label bounds */
+    rect.left = LVIR_LABEL;
+    rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+    /* padding + 2 icon widths, column width */
+    expect(2 + 16*2, rect.left);
+    expect(50, rect.right);
+
+    /* icon bounds */
+    rect.left = LVIR_ICON;
+    rect.right = rect.top = rect.bottom = -1;
+    r = SendMessage(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect);
+    expect(TRUE, r);
+    /* padding + 1 icon width indentation, icon width */
+    expect(2 + 16, rect.left);
+    expect(34, rect.right);
+
+    DestroyWindow(hwnd);
+}
+
+static void test_editbox(void)
+{
+    static CHAR testitemA[]  = "testitem";
+    static CHAR testitem1A[] = "testitem_quitelongname";
+    static CHAR buffer[25];
+    HWND hwnd, hwndedit, hwndedit2, header;
+    LVITEMA item;
+    DWORD r;
+
+    hwnd = create_listview_control(LVS_EDITLABELS | LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+
+    insert_column(hwnd, 0);
+
+    memset(&item, 0, sizeof(item));
+    item.mask = LVIF_TEXT;
+    item.pszText = testitemA;
+    item.iItem = 0;
+    item.iSubItem = 0;
+    r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&item);
+    expect(0, r);
+
+    /* test notifications without edit created */
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    r = SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(0, EN_SETFOCUS), (LPARAM)0xdeadbeef);
+    expect(0, r);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
+                "edit box WM_COMMAND (EN_SETFOCUS), no edit created", FALSE);
+    /* same thing but with valid window */
+    hwndedit = CreateWindowA("Edit", "Test edit", WS_VISIBLE | WS_CHILD, 0, 0, 20,
+                10, hwnd, (HMENU)1, (HINSTANCE)GetWindowLongPtrA(hwnd, GWLP_HINSTANCE), 0);
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    r = SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(0, EN_SETFOCUS), (LPARAM)hwndedit);
+    expect(0, r);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
+                "edit box WM_COMMAND (EN_SETFOCUS), no edit created #2", FALSE);
+    DestroyWindow(hwndedit);
+
+    /* setting focus is necessary */
+    SetFocus(hwnd);
+    hwndedit = (HWND)SendMessage(hwnd, LVM_EDITLABEL, 0, 0);
+    ok(IsWindow(hwndedit), "Expected Edit window to be created\n");
+
+    /* test children Z-order after Edit box created */
+    header = (HWND)SendMessageA(hwnd, LVM_GETHEADER, 0, 0);
+    ok(IsWindow(header), "Expected header to be created\n");
+    ok(GetTopWindow(hwnd) == header, "Expected header to be on top\n");
+    ok(GetNextWindow(header, GW_HWNDNEXT) == hwndedit, "got %p\n", GetNextWindow(header, GW_HWNDNEXT));
+
+    /* modify initial string */
+    r = SendMessage(hwndedit, WM_SETTEXT, 0, (LPARAM)testitem1A);
+    expect(TRUE, r);
+
+    /* edit window is resized and repositioned,
+       check again for Z-order - it should be preserved */
+    ok(GetTopWindow(hwnd) == header, "Expected header to be on top\n");
+    ok(GetNextWindow(header, GW_HWNDNEXT) == hwndedit, "got %p\n", GetNextWindow(header, GW_HWNDNEXT));
+
+    /* return focus to listview */
+    SetFocus(hwnd);
+
+    memset(&item, 0, sizeof(item));
+    item.mask = LVIF_TEXT;
+    item.pszText = buffer;
+    item.cchTextMax = sizeof(buffer);
+    item.iItem = 0;
+    item.iSubItem = 0;
+    r = SendMessage(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
+    expect(TRUE, r);
+
+    ok(strcmp(buffer, testitem1A) == 0, "Expected item text to change\n");
+
+    /* send LVM_EDITLABEL on already created edit */
+    SetFocus(hwnd);
+    hwndedit = (HWND)SendMessage(hwnd, LVM_EDITLABEL, 0, 0);
+    ok(IsWindow(hwndedit), "Expected Edit window to be created\n");
+    /* focus will be set to edit */
+    ok(GetFocus() == hwndedit, "Expected Edit window to be focused\n");
+    hwndedit2 = (HWND)SendMessage(hwnd, LVM_EDITLABEL, 0, 0);
+    ok(IsWindow(hwndedit2), "Expected Edit window to be created\n");
+
+    /* creating label disabled when control isn't focused */
+    SetFocus(0);
+    hwndedit = (HWND)SendMessage(hwnd, LVM_EDITLABEL, 0, 0);
+    todo_wine ok(hwndedit == NULL, "Expected Edit window not to be created\n");
+
+    /* check EN_KILLFOCUS handling */
+    memset(&item, 0, sizeof(item));
+    item.pszText = testitemA;
+    item.iItem = 0;
+    item.iSubItem = 0;
+    r = SendMessage(hwnd, LVM_SETITEMTEXTA, 0, (LPARAM)&item);
+    expect(TRUE, r);
+
+    SetFocus(hwnd);
+    hwndedit = (HWND)SendMessage(hwnd, LVM_EDITLABEL, 0, 0);
+    ok(IsWindow(hwndedit), "Expected Edit window to be created\n");
+    /* modify edit and notify control that it lost focus */
+    r = SendMessage(hwndedit, WM_SETTEXT, 0, (LPARAM)testitem1A);
+    expect(TRUE, r);
+    r = SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)hwndedit);
+    expect(0, r);
+    memset(&item, 0, sizeof(item));
+    item.pszText = buffer;
+    item.cchTextMax = sizeof(buffer);
+    item.iItem = 0;
+    item.iSubItem = 0;
+    r = SendMessage(hwnd, LVM_GETITEMTEXTA, 0, (LPARAM)&item);
+    expect(lstrlen(item.pszText), r);
+    ok(strcmp(buffer, testitem1A) == 0, "Expected item text to change\n");
+    ok(!IsWindow(hwndedit), "Expected Edit window to be freed\n");
+    /* end edit without saving */
+    SetFocus(hwnd);
+    hwndedit = (HWND)SendMessage(hwnd, LVM_EDITLABEL, 0, 0);
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    r = SendMessage(hwndedit, WM_KEYDOWN, VK_ESCAPE, 0);
+    expect(0, r);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, edit_end_nochange,
+                "edit box - end edit, no change, escape", TRUE);
+    /* end edit with saving */
+    SetFocus(hwnd);
+    hwndedit = (HWND)SendMessage(hwnd, LVM_EDITLABEL, 0, 0);
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    r = SendMessage(hwndedit, WM_KEYDOWN, VK_RETURN, 0);
+    expect(0, r);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, edit_end_nochange,
+                "edit box - end edit, no change, return", TRUE);
+
+    memset(&item, 0, sizeof(item));
+    item.pszText = buffer;
+    item.cchTextMax = sizeof(buffer);
+    item.iItem = 0;
+    item.iSubItem = 0;
+    r = SendMessage(hwnd, LVM_GETITEMTEXTA, 0, (LPARAM)&item);
+    expect(lstrlen(item.pszText), r);
+    ok(strcmp(buffer, testitem1A) == 0, "Expected item text to change\n");
+
+    /* LVM_EDITLABEL with -1 destroys current edit */
+    hwndedit = (HWND)SendMessage(hwnd, LVM_GETEDITCONTROL, 0, 0);
+    ok(hwndedit == NULL, "Expected Edit window not to be created\n");
+    /* no edit present */
+    hwndedit = (HWND)SendMessage(hwnd, LVM_EDITLABEL, -1, 0);
+    ok(hwndedit == NULL, "Expected Edit window not to be created\n");
+    hwndedit = (HWND)SendMessage(hwnd, LVM_EDITLABEL, 0, 0);
+    ok(IsWindow(hwndedit), "Expected Edit window to be created\n");
+    /* edit present */
+    ok(GetFocus() == hwndedit, "Expected Edit to be focused\n");
+    hwndedit2 = (HWND)SendMessage(hwnd, LVM_EDITLABEL, -1, 0);
+    ok(hwndedit2 == NULL, "Expected Edit window not to be created\n");
+    ok(!IsWindow(hwndedit), "Expected Edit window to be destroyed\n");
+    ok(GetFocus() == hwnd, "Expected List to be focused\n");
+    /* check another negative value */
+    hwndedit = (HWND)SendMessage(hwnd, LVM_EDITLABEL, 0, 0);
+    ok(IsWindow(hwndedit), "Expected Edit window to be created\n");
+    ok(GetFocus() == hwndedit, "Expected Edit to be focused\n");
+    hwndedit2 = (HWND)SendMessage(hwnd, LVM_EDITLABEL, -2, 0);
+    ok(hwndedit2 == NULL, "Expected Edit window not to be created\n");
+    ok(!IsWindow(hwndedit), "Expected Edit window to be destroyed\n");
+    ok(GetFocus() == hwnd, "Expected List to be focused\n");
+    /* and value greater than max item index */
+    hwndedit = (HWND)SendMessage(hwnd, LVM_EDITLABEL, 0, 0);
+    ok(IsWindow(hwndedit), "Expected Edit window to be created\n");
+    ok(GetFocus() == hwndedit, "Expected Edit to be focused\n");
+    r = SendMessage(hwnd, LVM_GETITEMCOUNT, 0, 0);
+    hwndedit2 = (HWND)SendMessage(hwnd, LVM_EDITLABEL, r, 0);
+    ok(hwndedit2 == NULL, "Expected Edit window not to be created\n");
+    ok(!IsWindow(hwndedit), "Expected Edit window to be destroyed\n");
+    ok(GetFocus() == hwnd, "Expected List to be focused\n");
+
+    /* messaging tests */
+    SetFocus(hwnd);
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    blockEdit = FALSE;
+    hwndedit = (HWND)SendMessage(hwnd, LVM_EDITLABEL, 0, 0);
+    ok(IsWindow(hwndedit), "Expected Edit window to be created\n");
+    /* testing only sizing messages */
+    ok_sequence(sequences, EDITBOX_SEQ_INDEX, editbox_create_pos,
+                "edit box create - sizing", FALSE);
+
+    /* WM_COMMAND with EN_KILLFOCUS isn't forwared to parent */
+    SetFocus(hwnd);
+    hwndedit = (HWND)SendMessage(hwnd, LVM_EDITLABEL, 0, 0);
+    ok(IsWindow(hwndedit), "Expected Edit window to be created\n");
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    r = SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)hwndedit);
+    expect(0, r);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, edit_end_nochange,
+                "edit box WM_COMMAND (EN_KILLFOCUS)", TRUE);
+
+    DestroyWindow(hwnd);
+}
+
+static void test_notifyformat(void)
+{
+    HWND hwnd, header;
+    DWORD r;
+
+    hwnd = create_listview_control(LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+
+    /* CCM_GETUNICODEFORMAT == LVM_GETUNICODEFORMAT,
+       CCM_SETUNICODEFORMAT == LVM_SETUNICODEFORMAT */
+    r = SendMessage(hwnd, LVM_GETUNICODEFORMAT, 0, 0);
+    expect(0, r);
+    r = SendMessage(hwnd, WM_NOTIFYFORMAT, 0, NF_QUERY);
+    /* set */
+    r = SendMessage(hwnd, LVM_SETUNICODEFORMAT, 1, 0);
+    expect(0, r);
+    r = SendMessage(hwnd, LVM_GETUNICODEFORMAT, 0, 0);
+    if (r == 1)
+    {
+        r = SendMessage(hwnd, LVM_SETUNICODEFORMAT, 0, 0);
+        expect(1, r);
+        r = SendMessage(hwnd, LVM_GETUNICODEFORMAT, 0, 0);
+        expect(0, r);
+    }
+    else
+    {
+        win_skip("LVM_GETUNICODEFORMAT is unsupported\n");
+        DestroyWindow(hwnd);
+        return;
+    }
+
+    DestroyWindow(hwnd);
+
+    /* test failure in parent WM_NOTIFYFORMAT  */
+    notifyFormat = 0;
+    hwnd = create_listview_control(LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+    header = (HWND)SendMessage(hwnd, LVM_GETHEADER, 0, 0);
+    ok(IsWindow(header), "expected header to be created\n");
+    r = SendMessage(hwnd, LVM_GETUNICODEFORMAT, 0, 0);
+    expect(0, r);
+    r = SendMessage(header, HDM_GETUNICODEFORMAT, 0, 0);
+    ok( r == 1 || broken(r == 0), /* win9x */ "Expected 1, got %d\n", r );
+    r = SendMessage(hwnd, WM_NOTIFYFORMAT, 0, NF_QUERY);
+    ok(r != 0, "Expected valid format\n");
+
+    notifyFormat = NFR_UNICODE;
+    r = SendMessage(hwnd, WM_NOTIFYFORMAT, 0, NF_REQUERY);
+    expect(NFR_UNICODE, r);
+    r = SendMessage(hwnd, LVM_GETUNICODEFORMAT, 0, 0);
+    expect(1, r);
+    r = SendMessage(header, HDM_GETUNICODEFORMAT, 0, 0);
+    ok( r == 1 || broken(r == 0), /* win9x */ "Expected 1, got %d\n", r );
+
+    notifyFormat = NFR_ANSI;
+    r = SendMessage(hwnd, WM_NOTIFYFORMAT, 0, NF_REQUERY);
+    expect(NFR_ANSI, r);
+    r = SendMessage(hwnd, LVM_GETUNICODEFORMAT, 0, 0);
+    expect(0, r);
+    r = SendMessage(header, HDM_GETUNICODEFORMAT, 0, 0);
+    ok( r == 1 || broken(r == 0), /* win9x */ "Expected 1, got %d\n", r );
+
+    DestroyWindow(hwnd);
+
+    /* try different unicode window combination and defaults */
+    if (!GetModuleHandleW(NULL))
+    {
+        win_skip("Additional notify format tests are incompatible with Win9x\n");
+        return;
+    }
+
+    hwndparentW = create_parent_window(TRUE);
+    ok(IsWindow(hwndparentW), "Unicode parent creation failed\n");
+    if (!IsWindow(hwndparentW))  return;
+
+    notifyFormat = -1;
+    hwnd = create_listview_controlW(LVS_REPORT, hwndparentW);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+    header = (HWND)SendMessage(hwnd, LVM_GETHEADER, 0, 0);
+    ok(IsWindow(header), "expected header to be created\n");
+    r = SendMessageW(hwnd, LVM_GETUNICODEFORMAT, 0, 0);
+    expect(1, r);
+    r = SendMessage(header, HDM_GETUNICODEFORMAT, 0, 0);
+    expect(1, r);
+    DestroyWindow(hwnd);
+    /* receiving error code defaulting to ansi */
+    notifyFormat = 0;
+    hwnd = create_listview_controlW(LVS_REPORT, hwndparentW);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+    header = (HWND)SendMessage(hwnd, LVM_GETHEADER, 0, 0);
+    ok(IsWindow(header), "expected header to be created\n");
+    r = SendMessageW(hwnd, LVM_GETUNICODEFORMAT, 0, 0);
+    expect(0, r);
+    r = SendMessage(header, HDM_GETUNICODEFORMAT, 0, 0);
+    expect(1, r);
+    DestroyWindow(hwnd);
+    /* receiving ansi code from unicode window, use it */
+    notifyFormat = NFR_ANSI;
+    hwnd = create_listview_controlW(LVS_REPORT, hwndparentW);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+    header = (HWND)SendMessage(hwnd, LVM_GETHEADER, 0, 0);
+    ok(IsWindow(header), "expected header to be created\n");
+    r = SendMessageW(hwnd, LVM_GETUNICODEFORMAT, 0, 0);
+    expect(0, r);
+    r = SendMessage(header, HDM_GETUNICODEFORMAT, 0, 0);
+    expect(1, r);
+    DestroyWindow(hwnd);
+    /* unicode listview with ansi parent window */
+    notifyFormat = -1;
+    hwnd = create_listview_controlW(LVS_REPORT, hwndparent);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+    header = (HWND)SendMessage(hwnd, LVM_GETHEADER, 0, 0);
+    ok(IsWindow(header), "expected header to be created\n");
+    r = SendMessageW(hwnd, LVM_GETUNICODEFORMAT, 0, 0);
+    expect(0, r);
+    r = SendMessage(header, HDM_GETUNICODEFORMAT, 0, 0);
+    expect(1, r);
+    DestroyWindow(hwnd);
+    /* unicode listview with ansi parent window, return error code */
+    notifyFormat = 0;
+    hwnd = create_listview_controlW(LVS_REPORT, hwndparent);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+    header = (HWND)SendMessage(hwnd, LVM_GETHEADER, 0, 0);
+    ok(IsWindow(header), "expected header to be created\n");
+    r = SendMessageW(hwnd, LVM_GETUNICODEFORMAT, 0, 0);
+    expect(0, r);
+    r = SendMessage(header, HDM_GETUNICODEFORMAT, 0, 0);
+    expect(1, r);
+    DestroyWindow(hwnd);
+
+    DestroyWindow(hwndparentW);
+}
+
+static void test_indentation(void)
+{
+    HWND hwnd;
+    LVITEMA item;
+    DWORD r;
+
+    hwnd = create_listview_control(LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+
+    memset(&item, 0, sizeof(item));
+    item.mask = LVIF_INDENT;
+    item.iItem = 0;
+    item.iIndent = I_INDENTCALLBACK;
+    r = SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&item);
+    expect(0, r);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    item.iItem = 0;
+    item.mask = LVIF_INDENT;
+    r = SendMessage(hwnd, LVM_GETITEM, 0, (LPARAM)&item);
+    expect(TRUE, r);
+
+    ok_sequence(sequences, PARENT_SEQ_INDEX, single_getdispinfo_parent_seq,
+                "get indent dispinfo", FALSE);
+
+    DestroyWindow(hwnd);
+}
+
+static INT CALLBACK DummyCompareEx(LPARAM first, LPARAM second, LPARAM param)
+{
+    return 0;
+}
+
+static BOOL is_below_comctl_5(void)
+{
+    HWND hwnd;
+    BOOL ret;
+
+    hwnd = create_listview_control(LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+    insert_item(hwnd, 0);
+
+    ret = SendMessage(hwnd, LVM_SORTITEMSEX, 0, (LPARAM)&DummyCompareEx);
+
+    DestroyWindow(hwnd);
+
+    return !ret;
+}
+
+static void test_get_set_view(void)
+{
+    HWND hwnd;
+    DWORD ret;
+    DWORD_PTR style;
+
+    /* test style->view mapping */
+    hwnd = create_listview_control(LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+
+    ret = SendMessage(hwnd, LVM_GETVIEW, 0, 0);
+    expect(LV_VIEW_DETAILS, ret);
+
+    style = GetWindowLongPtr(hwnd, GWL_STYLE);
+    /* LVS_ICON == 0 */
+    SetWindowLongPtr(hwnd, GWL_STYLE, style & ~LVS_REPORT);
+    ret = SendMessage(hwnd, LVM_GETVIEW, 0, 0);
+    expect(LV_VIEW_ICON, ret);
+
+    style = GetWindowLongPtr(hwnd, GWL_STYLE);
+    SetWindowLongPtr(hwnd, GWL_STYLE, style | LVS_SMALLICON);
+    ret = SendMessage(hwnd, LVM_GETVIEW, 0, 0);
+    expect(LV_VIEW_SMALLICON, ret);
+
+    style = GetWindowLongPtr(hwnd, GWL_STYLE);
+    SetWindowLongPtr(hwnd, GWL_STYLE, (style & ~LVS_SMALLICON) | LVS_LIST);
+    ret = SendMessage(hwnd, LVM_GETVIEW, 0, 0);
+    expect(LV_VIEW_LIST, ret);
+
+    /* switching view doesn't touch window style */
+    ret = SendMessage(hwnd, LVM_SETVIEW, LV_VIEW_DETAILS, 0);
+    expect(1, ret);
+    style = GetWindowLongPtr(hwnd, GWL_STYLE);
+    ok(style & LVS_LIST, "Expected style to be preserved\n");
+    ret = SendMessage(hwnd, LVM_SETVIEW, LV_VIEW_ICON, 0);
+    expect(1, ret);
+    style = GetWindowLongPtr(hwnd, GWL_STYLE);
+    ok(style & LVS_LIST, "Expected style to be preserved\n");
+    ret = SendMessage(hwnd, LVM_SETVIEW, LV_VIEW_SMALLICON, 0);
+    expect(1, ret);
+    style = GetWindowLongPtr(hwnd, GWL_STYLE);
+    ok(style & LVS_LIST, "Expected style to be preserved\n");
+
+    DestroyWindow(hwnd);
+}
+
+static void test_canceleditlabel(void)
+{
+    HWND hwnd, hwndedit;
+    DWORD ret;
+    CHAR buff[10];
+    LVITEMA itema;
+    static CHAR test[] = "test";
+    static const CHAR test1[] = "test1";
+
+    hwnd = create_listview_control(LVS_EDITLABELS | LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+
+    insert_item(hwnd, 0);
+
+    /* try without edit created */
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    ret = SendMessage(hwnd, LVM_CANCELEDITLABEL, 0, 0);
+    expect(TRUE, ret);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq,
+                "cancel edit label without edit", FALSE);
+
+    /* cancel without data change */
+    SetFocus(hwnd);
+    hwndedit = (HWND)SendMessage(hwnd, LVM_EDITLABEL, 0, 0);
+    ok(IsWindow(hwndedit), "Expected edit control to be created\n");
+    ret = SendMessage(hwnd, LVM_CANCELEDITLABEL, 0, 0);
+    expect(TRUE, ret);
+    ok(!IsWindow(hwndedit), "Expected edit control to be destroyed\n");
+
+    /* cancel after data change */
+    memset(&itema, 0, sizeof(itema));
+    itema.pszText = test;
+    ret = SendMessage(hwnd, LVM_SETITEMTEXT, 0, (LPARAM)&itema);
+    expect(TRUE, ret);
+    SetFocus(hwnd);
+    hwndedit = (HWND)SendMessage(hwnd, LVM_EDITLABEL, 0, 0);
+    ok(IsWindow(hwndedit), "Expected edit control to be created\n");
+    ret = SetWindowText(hwndedit, test1);
+    ok(ret != 0, "Expected edit text to change\n");
+    ret = SendMessage(hwnd, LVM_CANCELEDITLABEL, 0, 0);
+    expect(TRUE, ret);
+    ok(!IsWindow(hwndedit), "Expected edit control to be destroyed\n");
+    memset(&itema, 0, sizeof(itema));
+    itema.pszText = buff;
+    itema.cchTextMax = sizeof(buff)/sizeof(CHAR);
+    ret = SendMessage(hwnd, LVM_GETITEMTEXT, 0, (LPARAM)&itema);
+    expect(5, ret);
+    ok(strcmp(buff, test1) == 0, "Expected label text not to change\n");
+
+    DestroyWindow(hwnd);
+}
+
+static void test_mapidindex(void)
+{
+    HWND hwnd;
+    DWORD ret;
+
+    /* LVM_MAPINDEXTOID unsupported with LVS_OWNERDATA */
+    hwnd = create_listview_control(LVS_OWNERDATA | LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+    insert_item(hwnd, 0);
+    ret = SendMessage(hwnd, LVM_MAPINDEXTOID, 0, 0);
+    expect(-1, ret);
+    DestroyWindow(hwnd);
+
+    hwnd = create_listview_control(LVS_REPORT);
+    ok(hwnd != NULL, "failed to create a listview window\n");
+
+    /* LVM_MAPINDEXTOID with invalid index */
+    ret = SendMessage(hwnd, LVM_MAPINDEXTOID, 0, 0);
+    expect(-1, ret);
+
+    insert_item(hwnd, 0);
+    insert_item(hwnd, 1);
+
+    ret = SendMessage(hwnd, LVM_MAPINDEXTOID, -1, 0);
+    expect(-1, ret);
+    ret = SendMessage(hwnd, LVM_MAPINDEXTOID, 2, 0);
+    expect(-1, ret);
+
+    ret = SendMessage(hwnd, LVM_MAPINDEXTOID, 0, 0);
+    expect(0, ret);
+    ret = SendMessage(hwnd, LVM_MAPINDEXTOID, 1, 0);
+    expect(1, ret);
+    /* remove 0 indexed item, id retained */
+    SendMessage(hwnd, LVM_DELETEITEM, 0, 0);
+    ret = SendMessage(hwnd, LVM_MAPINDEXTOID, 0, 0);
+    expect(1, ret);
+    /* new id starts from previous value */
+    insert_item(hwnd, 1);
+    ret = SendMessage(hwnd, LVM_MAPINDEXTOID, 1, 0);
+    expect(2, ret);
+
+    /* get index by id */
+    ret = SendMessage(hwnd, LVM_MAPIDTOINDEX, -1, 0);
+    expect(-1, ret);
+    ret = SendMessage(hwnd, LVM_MAPIDTOINDEX, 0, 0);
+    expect(-1, ret);
+    ret = SendMessage(hwnd, LVM_MAPIDTOINDEX, 1, 0);
+    expect(0, ret);
+    ret = SendMessage(hwnd, LVM_MAPIDTOINDEX, 2, 0);
+    expect(1, ret);
+
+    DestroyWindow(hwnd);
+}
+
+static void test_getitemspacing(void)
+{
+    HWND hwnd;
+    DWORD ret;
+    INT cx, cy;
+    HIMAGELIST himl;
+    HBITMAP hbmp;
+    LVITEMA itema;
+
+    cx = GetSystemMetrics(SM_CXICONSPACING) - GetSystemMetrics(SM_CXICON);
+    cy = GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON);
+
+    /* LVS_ICON */
+    hwnd = create_listview_control(LVS_ICON);
+    ret = SendMessage(hwnd, LVM_GETITEMSPACING, FALSE, 0);
+todo_wine {
+    expect(cx, LOWORD(ret));
+    expect(cy, HIWORD(ret));
+}
+    /* now try with icons */
+    himl = ImageList_Create(40, 40, 0, 4, 4);
+    ok(himl != NULL, "failed to create imagelist\n");
+    hbmp = CreateBitmap(40, 40, 1, 1, NULL);
+    ok(hbmp != NULL, "failed to create bitmap\n");
+    ret = ImageList_Add(himl, hbmp, 0);
+    expect(0, ret);
+    ret = SendMessage(hwnd, LVM_SETIMAGELIST, 0, (LPARAM)himl);
+    expect(0, ret);
+
+    itema.mask = LVIF_IMAGE;
+    itema.iImage = 0;
+    itema.iItem = 0;
+    itema.iSubItem = 0;
+    ret = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM)&itema);
+    expect(0, ret);
+    ret = SendMessage(hwnd, LVM_GETITEMSPACING, FALSE, 0);
+todo_wine {
+    /* spacing + icon size returned */
+    expect(cx + 40, LOWORD(ret));
+    expect(cy + 40, HIWORD(ret));
+}
+    DestroyWindow(hwnd);
+    /* LVS_SMALLICON */
+    hwnd = create_listview_control(LVS_SMALLICON);
+    ret = SendMessage(hwnd, LVM_GETITEMSPACING, FALSE, 0);
+todo_wine {
+    expect(cx, LOWORD(ret));
+    expect(cy, HIWORD(ret));
+}
+    DestroyWindow(hwnd);
+    /* LVS_REPORT */
+    hwnd = create_listview_control(LVS_REPORT);
+    ret = SendMessage(hwnd, LVM_GETITEMSPACING, FALSE, 0);
+todo_wine {
+    expect(cx, LOWORD(ret));
+    expect(cy, HIWORD(ret));
+}
+    DestroyWindow(hwnd);
+    /* LVS_LIST */
+    hwnd = create_listview_control(LVS_LIST);
+    ret = SendMessage(hwnd, LVM_GETITEMSPACING, FALSE, 0);
+todo_wine {
+    expect(cx, LOWORD(ret));
+    expect(cy, HIWORD(ret));
+}
+    DestroyWindow(hwnd);
+}
+
+static void test_getcolumnwidth(void)
+{
+    HWND hwnd;
+    DWORD ret;
+    DWORD_PTR style;
+    LVCOLUMNA col;
+    LVITEMA itema;
+
+    /* default column width */
+    hwnd = create_listview_control(LVS_ICON);
+    ret = SendMessage(hwnd, LVM_GETCOLUMNWIDTH, 0, 0);
+    expect(0, ret);
+    style = GetWindowLong(hwnd, GWL_STYLE);
+    SetWindowLong(hwnd, GWL_STYLE, style | LVS_LIST);
+    ret = SendMessage(hwnd, LVM_GETCOLUMNWIDTH, 0, 0);
+    todo_wine expect(8, ret);
+    style = GetWindowLong(hwnd, GWL_STYLE) & ~LVS_LIST;
+    SetWindowLong(hwnd, GWL_STYLE, style | LVS_REPORT);
+    col.mask = 0;
+    ret = SendMessage(hwnd, LVM_INSERTCOLUMNA, 0, (LPARAM)&col);
+    expect(0, ret);
+    ret = SendMessage(hwnd, LVM_GETCOLUMNWIDTH, 0, 0);
+    expect(10, ret);
+    DestroyWindow(hwnd);
+
+    /* default column width with item added */
+    hwnd = create_listview_control(LVS_LIST);
+    memset(&itema, 0, sizeof(itema));
+    SendMessage(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&itema);
+    ret = SendMessage(hwnd, LVM_GETCOLUMNWIDTH, 0, 0);
+    todo_wine expect(96, ret);
+    DestroyWindow(hwnd);
+}
+
+static void test_scrollnotify(void)
+{
+    HWND hwnd;
+    DWORD ret;
+
+    hwnd = create_listview_control(LVS_REPORT);
+
+    insert_column(hwnd, 0);
+    insert_column(hwnd, 1);
+    insert_item(hwnd, 0);
+
+    /* make it scrollable - resize */
+    ret = SendMessage(hwnd, LVM_SETCOLUMNWIDTH, 0, MAKELPARAM(100, 0));
+    expect(TRUE, ret);
+    ret = SendMessage(hwnd, LVM_SETCOLUMNWIDTH, 1, MAKELPARAM(100, 0));
+    expect(TRUE, ret);
+
+    /* try with dummy call */
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    ret = SendMessage(hwnd, LVM_SCROLL, 0, 0);
+    expect(TRUE, ret);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, scroll_parent_seq,
+                "scroll notify 1", TRUE);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    ret = SendMessage(hwnd, LVM_SCROLL, 1, 0);
+    expect(TRUE, ret);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, scroll_parent_seq,
+                "scroll notify 2", TRUE);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    ret = SendMessage(hwnd, LVM_SCROLL, 1, 1);
+    expect(TRUE, ret);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, scroll_parent_seq,
+                "scroll notify 3", TRUE);
+
+    DestroyWindow(hwnd);
+}
+
+static void test_LVS_EX_TRANSPARENTBKGND(void)
+{
+    HWND hwnd;
+    DWORD ret;
+    HDC hdc;
+
+    hwnd = create_listview_control(LVS_REPORT);
+
+    ret = SendMessage(hwnd, LVM_SETBKCOLOR, 0, RGB(0, 0, 0));
+    expect(TRUE, ret);
+
+    SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_TRANSPARENTBKGND,
+                                                    LVS_EX_TRANSPARENTBKGND);
+
+    ret = SendMessage(hwnd, LVM_GETBKCOLOR, 0, 0);
+    if (ret != CLR_NONE)
+    {
+        win_skip("LVS_EX_TRANSPARENTBKGND unsupported\n");
+        DestroyWindow(hwnd);
+        return;
+    }
+
+    /* try to set some back color and check this style bit */
+    ret = SendMessage(hwnd, LVM_SETBKCOLOR, 0, RGB(0, 0, 0));
+    expect(TRUE, ret);
+    ret = SendMessage(hwnd, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0);
+    ok(!(ret & LVS_EX_TRANSPARENTBKGND), "Expected LVS_EX_TRANSPARENTBKGND to unset\n");
+
+    /* now test what this style actually does */
+    SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_TRANSPARENTBKGND,
+                                                    LVS_EX_TRANSPARENTBKGND);
+
+    hdc = GetWindowDC(hwndparent);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    SendMessageA(hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, lvs_ex_transparentbkgnd_seq,
+                "LVS_EX_TRANSPARENTBKGND parent", FALSE);
+
+    ReleaseDC(hwndparent, hdc);
+
+    DestroyWindow(hwnd);
+}
+
+static void test_approximate_viewrect(void)
+{
+    HWND hwnd;
+    DWORD ret;
+    HIMAGELIST himl;
+    HBITMAP hbmp;
+    LVITEMA itema;
+    static CHAR test[] = "abracadabra, a very long item label";
+
+    hwnd = create_listview_control(LVS_ICON);
+    himl = ImageList_Create(40, 40, 0, 4, 4);
+    ok(himl != NULL, "failed to create imagelist\n");
+    hbmp = CreateBitmap(40, 40, 1, 1, NULL);
+    ok(hbmp != NULL, "failed to create bitmap\n");
+    ret = ImageList_Add(himl, hbmp, 0);
+    expect(0, ret);
+    ret = SendMessage(hwnd, LVM_SETIMAGELIST, 0, (LPARAM)himl);
+    expect(0, ret);
+
+    itema.mask = LVIF_IMAGE;
+    itema.iImage = 0;
+    itema.iItem = 0;
+    itema.iSubItem = 0;
+    ret = SendMessage(hwnd, LVM_INSERTITEM, 0, (LPARAM)&itema);
+    expect(0, ret);
+
+    ret = SendMessage(hwnd, LVM_SETICONSPACING, 0, MAKELPARAM(75, 75));
+    if (ret == 0)
+    {
+        /* version 4.0 */
+        win_skip("LVM_SETICONSPACING unimplemented. Skipping.\n");
+        return;
+    }
+
+    ret = SendMessage(hwnd, LVM_APPROXIMATEVIEWRECT, 11, MAKELPARAM(100,100));
+    ok(MAKELONG(77,827)==ret,"Incorrect Approximate rect\n");
+
+    ret = SendMessage(hwnd, LVM_SETICONSPACING, 0, MAKELPARAM(50, 50));
+    ret = SendMessage(hwnd, LVM_APPROXIMATEVIEWRECT, 11, MAKELPARAM(100,100));
+    ok(MAKELONG(102,302)==ret,"Incorrect Approximate rect\n");
+
+    ret = SendMessage(hwnd, LVM_APPROXIMATEVIEWRECT, -1, MAKELPARAM(100,100));
+    ok(MAKELONG(52,52)==ret,"Incorrect Approximate rect\n");
+
+    itema.pszText = test;
+    ret = SendMessage(hwnd, LVM_SETITEMTEXT, 0, (LPARAM)&itema);
+    expect(TRUE, ret);
+    ret = SendMessage(hwnd, LVM_APPROXIMATEVIEWRECT, -1, MAKELPARAM(100,100));
+    ok(MAKELONG(52,52)==ret,"Incorrect Approximate rect\n");
+
+    ret = SendMessage(hwnd, LVM_APPROXIMATEVIEWRECT, 0, MAKELPARAM(100,100));
+    ok(MAKELONG(52,2)==ret,"Incorrect Approximate rect\n");
+    ret = SendMessage(hwnd, LVM_APPROXIMATEVIEWRECT, 1, MAKELPARAM(100,100));
+    ok(MAKELONG(52,52)==ret,"Incorrect Approximate rect\n");
+    ret = SendMessage(hwnd, LVM_APPROXIMATEVIEWRECT, 2, MAKELPARAM(100,100));
+    ok(MAKELONG(102,52)==ret,"Incorrect Approximate rect\n");
+    ret = SendMessage(hwnd, LVM_APPROXIMATEVIEWRECT, 3, MAKELPARAM(100,100));
+    ok(MAKELONG(102,102)==ret,"Incorrect Approximate rect\n");
+    ret = SendMessage(hwnd, LVM_APPROXIMATEVIEWRECT, 4, MAKELPARAM(100,100));
+    ok(MAKELONG(102,102)==ret,"Incorrect Approximate rect\n");
+    ret = SendMessage(hwnd, LVM_APPROXIMATEVIEWRECT, 5, MAKELPARAM(100,100));
+    ok(MAKELONG(102,152)==ret,"Incorrect Approximate rect\n");
+    ret = SendMessage(hwnd, LVM_APPROXIMATEVIEWRECT, 6, MAKELPARAM(100,100));
+    ok(MAKELONG(102,152)==ret,"Incorrect Approximate rect\n");
+    ret = SendMessage(hwnd, LVM_APPROXIMATEVIEWRECT, 7, MAKELPARAM(160,100));
+    ok(MAKELONG(152,152)==ret,"Incorrect Approximate rect\n");
+
+    DestroyWindow(hwnd);
+}
+
+static void test_finditem(void)
+{
+    LVFINDINFOA fi;
+    static char f[5];
+    HWND hwnd;
+    DWORD r;
+
+    hwnd = create_listview_control(LVS_REPORT);
+    insert_item(hwnd, 0);
+
+    memset(&fi, 0, sizeof(fi));
+
+    /* full string search, inserted text was "foo" */
+    strcpy(f, "foo");
+    fi.flags = LVFI_STRING;
+    fi.psz = f;
+    r = SendMessage(hwnd, LVM_FINDITEMA, -1, (LPARAM)&fi);
+    expect(0, r);
+    /* partial string search, inserted text was "foo" */
+    strcpy(f, "fo");
+    fi.flags = LVFI_STRING | LVFI_PARTIAL;
+    fi.psz = f;
+    r = SendMessage(hwnd, LVM_FINDITEMA, -1, (LPARAM)&fi);
+    expect(0, r);
+    /* partial string search, part after start char */
+    strcpy(f, "oo");
+    fi.flags = LVFI_STRING | LVFI_PARTIAL;
+    fi.psz = f;
+    r = SendMessage(hwnd, LVM_FINDITEMA, -1, (LPARAM)&fi);
+    expect(-1, r);
+
+    /* try with LVFI_SUBSTRING */
+    strcpy(f, "fo");
+    fi.flags = LVFI_SUBSTRING;
+    fi.psz = f;
+    r = SendMessage(hwnd, LVM_FINDITEMA, -1, (LPARAM)&fi);
+    if (r == -1)
+    {
+        win_skip("LVFI_SUBSTRING not supported\n");
+        DestroyWindow(hwnd);
+        return;
+    }
+    expect(0, r);
+    strcpy(f, "f");
+    fi.flags = LVFI_SUBSTRING;
+    fi.psz = f;
+    r = SendMessage(hwnd, LVM_FINDITEMA, -1, (LPARAM)&fi);
+    expect(0, r);
+    strcpy(f, "o");
+    fi.flags = LVFI_SUBSTRING;
+    fi.psz = f;
+    r = SendMessage(hwnd, LVM_FINDITEMA, -1, (LPARAM)&fi);
+    expect(-1, r);
+
+    strcpy(f, "f");
+    fi.flags = LVFI_SUBSTRING | LVFI_STRING;
+    fi.psz = f;
+    r = SendMessage(hwnd, LVM_FINDITEMA, -1, (LPARAM)&fi);
+    expect(0, r);
+
+    DestroyWindow(hwnd);
+}
+
+static void test_LVS_EX_HEADERINALLVIEWS(void)
+{
+    HWND hwnd, header;
+    DWORD style;
+
+    hwnd = create_listview_control(LVS_ICON);
+
+    SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_HEADERINALLVIEWS,
+                                                    LVS_EX_HEADERINALLVIEWS);
+
+    header = (HWND)SendMessage(hwnd, LVM_GETHEADER, 0, 0);
+    if (!IsWindow(header))
+    {
+        win_skip("LVS_EX_HEADERINALLVIEWS unsupported\n");
+        DestroyWindow(hwnd);
+        return;
+    }
+
+    /* LVS_NOCOLUMNHEADER works as before */
+    style = GetWindowLongA(hwnd, GWL_STYLE);
+    SetWindowLongW(hwnd, GWL_STYLE, style | LVS_NOCOLUMNHEADER);
+    style = GetWindowLongA(header, GWL_STYLE);
+    ok(style & HDS_HIDDEN, "Expected HDS_HIDDEN\n");
+    style = GetWindowLongA(hwnd, GWL_STYLE);
+    SetWindowLongW(hwnd, GWL_STYLE, style & ~LVS_NOCOLUMNHEADER);
+    style = GetWindowLongA(header, GWL_STYLE);
+    ok(!(style & HDS_HIDDEN), "Expected HDS_HIDDEN to be unset\n");
+
+    /* try to remove style */
+    SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_HEADERINALLVIEWS, 0);
+    header = (HWND)SendMessage(hwnd, LVM_GETHEADER, 0, 0);
+    ok(IsWindow(header), "Expected header to be created\n");
+    style = GetWindowLongA(header, GWL_STYLE);
+    ok(!(style & HDS_HIDDEN), "HDS_HIDDEN not expected\n");
+
+    DestroyWindow(hwnd);
+
+    /* check other styles */
+    hwnd = create_listview_control(LVS_LIST);
+    SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_HEADERINALLVIEWS,
+                                                    LVS_EX_HEADERINALLVIEWS);
+    header = (HWND)SendMessage(hwnd, LVM_GETHEADER, 0, 0);
+    ok(IsWindow(header), "Expected header to be created\n");
+    DestroyWindow(hwnd);
+
+    hwnd = create_listview_control(LVS_SMALLICON);
+    SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_HEADERINALLVIEWS,
+                                                    LVS_EX_HEADERINALLVIEWS);
+    header = (HWND)SendMessage(hwnd, LVM_GETHEADER, 0, 0);
+    ok(IsWindow(header), "Expected header to be created\n");
+    DestroyWindow(hwnd);
+
+    hwnd = create_listview_control(LVS_REPORT);
+    SendMessage(hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_HEADERINALLVIEWS,
+                                                    LVS_EX_HEADERINALLVIEWS);
+    header = (HWND)SendMessage(hwnd, LVM_GETHEADER, 0, 0);
+    ok(IsWindow(header), "Expected header to be created\n");
+    DestroyWindow(hwnd);
+}
+
+static void test_hover(void)
+{
+    HWND hwnd;
+    DWORD r;
+
+    hwnd = create_listview_control(LVS_ICON);
+
+    /* test WM_MOUSEHOVER forwarding */
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    r = SendMessage(hwnd, WM_MOUSEHOVER, 0, 0);
+    expect(0, r);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, hover_parent, "NM_HOVER allow test", TRUE);
+    g_block_hover = TRUE;
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    r = SendMessage(hwnd, WM_MOUSEHOVER, 0, 0);
+    expect(0, r);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, hover_parent, "NM_HOVER block test", TRUE);
+    g_block_hover = FALSE;
+
+    r = SendMessage(hwnd, LVM_SETHOVERTIME, 0, 500);
+    expect(HOVER_DEFAULT, r);
+    r = SendMessage(hwnd, LVM_GETHOVERTIME, 0, 0);
+    expect(500, r);
+
+    DestroyWindow(hwnd);
+}
+
+static void test_destroynotify(void)
+{
+    HWND hwnd;
+
+    hwnd = create_listview_control(LVS_REPORT);
+    ok(hwnd != NULL, "failed to create listview window\n");
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    DestroyWindow(hwnd);
+    ok_sequence(sequences, COMBINED_SEQ_INDEX, listview_destroy, "check destroy order", FALSE);
+}
+
+static void test_header_notification(void)
+{
+    HWND list, header;
+    HDITEMA item;
+    NMHEADER nmh;
+    LVCOLUMNA col;
+    LRESULT ret;
+
+    list = create_listview_control(LVS_REPORT);
+    ok(list != 0, "failed to create listview window\n");
+
+    memset(&col, 0, sizeof(col));
+    col.mask = LVCF_WIDTH;
+    col.cx = 100;
+    ret = SendMessage(list, LVM_INSERTCOLUMNA, 0, (LPARAM)&col);
+    ok(!ret, "expected 0, got %ld\n", ret);
+
+    header = subclass_header(list);
+
+    ret = SendMessage(header, HDM_GETITEMCOUNT, 0, 0);
+    ok(ret == 1, "expected header item count 1, got %ld\n", ret);
+
+    memset(&item, 0, sizeof(item));
+    item.mask = HDI_WIDTH;
+    ret = SendMessage(header, HDM_GETITEMA, 0, (LPARAM)&item);
+    ok(ret, "HDM_GETITEM failed\n");
+    ok(item.cxy == 100, "expected 100, got %d\n", item.cxy);
+
+    nmh.hdr.hwndFrom = header;
+    nmh.hdr.idFrom = GetWindowLongPtr(header, GWLP_ID);
+    nmh.hdr.code = HDN_ITEMCHANGEDA;
+    nmh.iItem = 0;
+    nmh.iButton = 0;
+    item.mask = HDI_WIDTH;
+    item.cxy = 50;
+    nmh.pitem = &item;
+    ret = SendMessage(list, WM_NOTIFY, 0, (LPARAM)&nmh);
+    ok(!ret, "WM_NOTIFY/HDN_ITEMCHANGED failed\n");
+
+    DestroyWindow(list);
+}
+
+static void test_createdragimage(void)
+{
+    HIMAGELIST himl;
+    POINT pt;
+    HWND list;
+
+    list = create_listview_control(LVS_ICON);
+    ok(list != 0, "failed to create listview window\n");
+
+    insert_item(list, 0);
+
+    /* NULL point */
+    himl = (HIMAGELIST)SendMessageA(list, LVM_CREATEDRAGIMAGE, 0, 0);
+    ok(himl == NULL, "got %p\n", himl);
+
+    himl = (HIMAGELIST)SendMessageA(list, LVM_CREATEDRAGIMAGE, 0, (LPARAM)&pt);
+    ok(himl != NULL, "got %p\n", himl);
+    ImageList_Destroy(himl);
+
+    DestroyWindow(list);
+}
+
+START_TEST(listview)
+{
+    HMODULE hComctl32;
+    BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
+
+    ULONG_PTR ctx_cookie;
+    HANDLE hCtx;
+    HWND hwnd;
+
+    hComctl32 = GetModuleHandleA("comctl32.dll");
+    pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
+    if (pInitCommonControlsEx)
+    {
+        INITCOMMONCONTROLSEX iccex;
+        iccex.dwSize = sizeof(iccex);
+        iccex.dwICC  = ICC_LISTVIEW_CLASSES;
+        pInitCommonControlsEx(&iccex);
+    }
+    else
+        InitCommonControls();
+
+    init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    hwndparent = create_parent_window(FALSE);
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    g_is_below_5 = is_below_comctl_5();
+
+    test_header_notification();
+    test_images();
+    test_checkboxes();
+    test_items();
+    test_create();
     test_redraw();
     test_customdraw();
     test_icon_spacing();
@@ -2105,9 +4557,58 @@ START_TEST(listview)
     test_columns();
     test_getorigin();
     test_multiselect();
+    test_getitemrect();
     test_subitem_rect();
     test_sorting();
     test_ownerdata();
     test_norecompute();
     test_nosortheader();
+    test_setredraw();
+    test_hittest();
+    test_getviewrect();
+    test_getitemposition();
+    test_columnscreation();
+    test_editbox();
+    test_notifyformat();
+    test_indentation();
+    test_getitemspacing();
+    test_getcolumnwidth();
+    test_approximate_viewrect();
+    test_finditem();
+    test_hover();
+    test_destroynotify();
+    test_createdragimage();
+
+    if (!load_v6_module(&ctx_cookie, &hCtx))
+    {
+        DestroyWindow(hwndparent);
+        return;
+    }
+
+    /* this is a XP SP3 failure workaround */
+    hwnd = CreateWindowExA(0, WC_LISTVIEW, "foo",
+                           WS_CHILD | WS_BORDER | WS_VISIBLE | LVS_REPORT,
+                           0, 0, 100, 100,
+                           hwndparent, NULL, GetModuleHandleA(NULL), NULL);
+    if (!IsWindow(hwnd))
+    {
+        win_skip("FIXME: failed to create ListView window.\n");
+        unload_v6_module(ctx_cookie, hCtx);
+        DestroyWindow(hwndparent);
+        return;
+    }
+    else
+        DestroyWindow(hwnd);
+
+    /* comctl32 version 6 tests start here */
+    test_get_set_view();
+    test_canceleditlabel();
+    test_mapidindex();
+    test_scrollnotify();
+    test_LVS_EX_TRANSPARENTBKGND();
+    test_LVS_EX_HEADERINALLVIEWS();
+
+    unload_v6_module(ctx_cookie, hCtx);
+
+    DestroyWindow(hwndparent);
 }
index e3cf04b..8f83260 100644 (file)
@@ -104,7 +104,9 @@ static void test_GetPtrAW(void)
         ok (count == sourcelen ||
             broken(count == 0), /* win9x */
             "Expected count to be %d, it was %d\n", sourcelen, count);
-        ok (!lstrcmp(dest, desttest), "Expected destination to not have changed\n");
+        ok (!lstrcmp(dest, desttest) ||
+            broken(!lstrcmp(dest, "")), /* Win7 */
+            "Expected destination to not have changed\n");
 
         count = 0;
         count = pStr_GetPtrA(source, NULL, destsize);
index 1ca1997..30f682f 100644 (file)
 #include "msg.h"
 
 #define expect(expected, got) ok(expected == got, "Expected %d, got %d\n", expected, got);
+#define expect_hex(expected, got) ok(expected == got, "Expected %x, got %x\n", expected, got);
 
 #define NUM_MSG_SEQUENCES   2
 #define PARENT_SEQ_INDEX    0
 #define MONTHCAL_SEQ_INDEX  1
 
-struct subclass_info
-{
-    WNDPROC oldproc;
-};
-
 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
 
+static HWND parent_wnd;
+
 static const struct message create_parent_window_seq[] = {
     { WM_GETMINMAXINFO, sent },
     { WM_NCCREATE, sent },
@@ -56,7 +54,7 @@ static const struct message create_parent_window_seq[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam|optional, 0 },
     { WM_WINDOWPOSCHANGED, sent|optional },
     { WM_ACTIVATEAPP, sent|wparam, 1 },
-    { WM_NCACTIVATE, sent|wparam, 1 },
+    { WM_NCACTIVATE, sent },
     { WM_ACTIVATE, sent|wparam, 1 },
     { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
     { WM_IME_NOTIFY, sent|defwinproc|optional },
@@ -81,6 +79,7 @@ static const struct message create_monthcal_multi_sel_style_seq[] = {
     { WM_NOTIFYFORMAT, sent|lparam, 0, NF_QUERY },
     { WM_QUERYUISTATE, sent|optional },
     { WM_GETFONT, sent },
+    { WM_PARENTNOTIFY, sent },
     { 0 }
 };
 
@@ -126,7 +125,6 @@ static const struct message monthcal_color_seq[] = {
 static const struct message monthcal_curr_date_seq[] = {
     { MCM_SETCURSEL, sent|wparam, 0},
     { WM_PAINT, sent|wparam|lparam|defwinproc, 0, 0},
-    { WM_ERASEBKGND, sent|lparam|defwinproc, 0},
     { MCM_SETCURSEL, sent|wparam, 0},
     { MCM_SETCURSEL, sent|wparam, 0},
     { MCM_GETCURSEL, sent|wparam, 0},
@@ -213,9 +211,6 @@ static const struct message monthcal_hit_test_seq[] = {
     { MCM_HITTEST, sent|wparam, 0},
     { MCM_HITTEST, sent|wparam, 0},
     { MCM_HITTEST, sent|wparam, 0},
-    { MCM_HITTEST, sent|wparam, 0},
-    { MCM_HITTEST, sent|wparam, 0},
-    { MCM_HITTEST, sent|wparam, 0},
     { 0 }
 };
 
@@ -224,7 +219,7 @@ static const struct message monthcal_todaylink_seq[] = {
     { MCM_SETTODAY, sent|wparam, 0},
     { WM_PAINT, sent|wparam|lparam|defwinproc, 0, 0},
     { MCM_GETTODAY, sent|wparam, 0},
-    { WM_LBUTTONDOWN, sent|wparam|lparam, MK_LBUTTON, MAKELONG(70, 370)},
+    { WM_LBUTTONDOWN, sent|wparam, MK_LBUTTON},
     { WM_CAPTURECHANGED, sent|wparam|lparam|defwinproc, 0, 0},
     { WM_PAINT, sent|wparam|lparam|defwinproc, 0, 0},
     { MCM_GETCURSEL, sent|wparam, 0},
@@ -289,6 +284,9 @@ static const struct message destroy_monthcal_child_msgs_seq[] = {
 
 static const struct message destroy_monthcal_multi_sel_style_seq[] = {
     { 0x0090, sent|optional }, /* Vista */
+    { WM_SHOWWINDOW, sent|wparam|lparam, 0, 0},
+    { WM_WINDOWPOSCHANGING, sent|wparam, 0},
+    { WM_WINDOWPOSCHANGED, sent|wparam, 0},
     { WM_DESTROY, sent|wparam|lparam, 0, 0},
     { WM_NCDESTROY, sent|wparam|lparam, 0, 0},
     { 0 }
@@ -299,12 +297,14 @@ static const struct message destroy_parent_seq[] = {
     { 0x0090, sent|optional }, /* Vista */
     { WM_WINDOWPOSCHANGING, sent|wparam, 0},
     { WM_WINDOWPOSCHANGED, sent|wparam, 0},
-    { WM_NCACTIVATE, sent|wparam, 0},
-    { WM_ACTIVATE, sent|wparam, 0},
+    { WM_IME_SETCONTEXT, sent|wparam|optional, 0},
+    { WM_IME_NOTIFY, sent|wparam|lparam|defwinproc|optional, 1, 0},
+    { WM_NCACTIVATE, sent|wparam|optional, 0},
+    { WM_ACTIVATE, sent|wparam|optional, 0},
     { WM_NCACTIVATE, sent|wparam|lparam|optional, 0, 0},
     { WM_ACTIVATE, sent|wparam|lparam|optional, 0, 0},
-    { WM_ACTIVATEAPP, sent|wparam, 0},
-    { WM_KILLFOCUS, sent|wparam|lparam, 0, 0},
+    { WM_ACTIVATEAPP, sent|wparam|optional, 0},
+    { WM_KILLFOCUS, sent|wparam|lparam|optional, 0, 0},
     { WM_IME_SETCONTEXT, sent|wparam|optional, 0},
     { WM_IME_NOTIFY, sent|wparam|lparam|defwinproc|optional, 1, 0},
     { WM_DESTROY, sent|wparam|lparam, 0, 0},
@@ -315,30 +315,97 @@ static const struct message destroy_parent_seq[] = {
 static void test_monthcal(void)
 {
     HWND hwnd;
-    SYSTEMTIME st[2], st1[2];
+    SYSTEMTIME st[2], st1[2], today;
     int res, month_range;
+    DWORD limits;
 
     hwnd = CreateWindowA(MONTHCAL_CLASSA, "MonthCal", WS_POPUP | WS_VISIBLE, CW_USEDEFAULT,
                          0, 300, 300, 0, 0, NULL, NULL);
     ok(hwnd != NULL, "Failed to create MonthCal\n");
+
+    /* test range just after creation */
+    memset(&st, 0xcc, sizeof(st));
+    limits = SendMessage(hwnd, MCM_GETRANGE, 0, (LPARAM)st);
+    ok(limits == 0 ||
+       broken(limits == GDTR_MIN), /* comctl32 <= 4.70 */
+       "No limits should be set (%d)\n", limits);
+    if (limits == GDTR_MIN)
+    {
+        win_skip("comctl32 <= 4.70 is broken\n");
+        DestroyWindow(hwnd);
+        return;
+    }
+
+    ok(0 == st[0].wYear ||
+       broken(1752 == st[0].wYear), /* comctl32 <= 4.72 */
+       "Expected 0, got %d\n", st[0].wYear);
+    ok(0 == st[0].wMonth ||
+       broken(9 == st[0].wMonth), /* comctl32 <= 4.72 */
+       "Expected 0, got %d\n", st[0].wMonth);
+    ok(0 == st[0].wDay ||
+       broken(14 == st[0].wDay), /* comctl32 <= 4.72 */
+       "Expected 0, got %d\n", st[0].wDay);
+    expect(0, st[0].wDayOfWeek);
+    expect(0, st[0].wHour);
+    expect(0, st[0].wMinute);
+    expect(0, st[0].wSecond);
+    expect(0, st[0].wMilliseconds);
+
+    expect(0, st[1].wYear);
+    expect(0, st[1].wMonth);
+    expect(0, st[1].wDay);
+    expect(0, st[1].wDayOfWeek);
+    expect(0, st[1].wHour);
+    expect(0, st[1].wMinute);
+    expect(0, st[1].wSecond);
+    expect(0, st[1].wMilliseconds);
+
     GetSystemTime(&st[0]);
     st[1] = st[0];
 
+    SendMessage(hwnd, MCM_GETTODAY, 0, (LPARAM)&today);
+
     /* Invalid date/time */
     st[0].wYear  = 2000;
     /* Time should not matter */
     st[1].wHour = st[1].wMinute = st[1].wSecond = 70;
+    st[1].wMilliseconds = 1200;
     ok(SendMessage(hwnd, MCM_SETRANGE, GDTR_MAX, (LPARAM)st), "Failed to set MAX limit\n");
+    /* invalid timestamp is written back with today data and msecs untouched */
+    expect(today.wHour, st[1].wHour);
+    expect(today.wMinute, st[1].wMinute);
+    expect(today.wSecond, st[1].wSecond);
+    expect(1200, st[1].wMilliseconds);
+
     ok(SendMessage(hwnd, MCM_GETRANGE, 0, (LPARAM)st1) == GDTR_MAX, "No limits should be set\n");
-    ok(st1[0].wYear != 2000, "Lover limit changed\n");
+    ok(st1[0].wYear != 2000, "Lower limit changed\n");
+    /* invalid timestamp should be replaced with today data, except msecs */
+    expect(today.wHour, st1[1].wHour);
+    expect(today.wMinute, st1[1].wMinute);
+    expect(today.wSecond, st1[1].wSecond);
+    expect(1200, st1[1].wMilliseconds);
+
+    /* Invalid date/time with invalid milliseconds only */
+    GetSystemTime(&st[0]);
+    st[1] = st[0];
+    /* Time should not matter */
+    st[1].wMilliseconds = 1200;
+    ok(SendMessage(hwnd, MCM_SETRANGE, GDTR_MAX, (LPARAM)st), "Failed to set MAX limit\n");
+    /* invalid milliseconds field doesn't lead to invalid timestamp */
+    expect(st[0].wHour,   st[1].wHour);
+    expect(st[0].wMinute, st[1].wMinute);
+    expect(st[0].wSecond, st[1].wSecond);
+    expect(1200, st[1].wMilliseconds);
+
+    GetSystemTime(&st[0]);
 
     st[1].wMonth = 0;
     ok(!SendMessage(hwnd, MCM_SETRANGE, GDTR_MIN | GDTR_MAX, (LPARAM)st), "Should have failed to set limits\n");
     ok(SendMessage(hwnd, MCM_GETRANGE, 0, (LPARAM)st1) == GDTR_MAX, "No limits should be set\n");
-    ok(st1[0].wYear != 2000, "Lover limit changed\n");
+    ok(st1[0].wYear != 2000, "Lower limit changed\n");
     ok(!SendMessage(hwnd, MCM_SETRANGE, GDTR_MAX, (LPARAM)st), "Should have failed to set MAX limit\n");
     ok(SendMessage(hwnd, MCM_GETRANGE, 0, (LPARAM)st1) == GDTR_MAX, "No limits should be set\n");
-    ok(st1[0].wYear != 2000, "Lover limit changed\n");
+    ok(st1[0].wYear != 2000, "Lower limit changed\n");
 
     GetSystemTime(&st[0]);
     st[0].wDay = 20;
@@ -373,6 +440,60 @@ static void test_monthcal(void)
     ok(SendMessage(hwnd, MCM_SETRANGE, GDTR_MAX, (LPARAM)st), "Failed to set max limit\n");
     ok(SendMessage(hwnd, MCM_GETRANGE, 0, (LPARAM)st1) == GDTR_MAX, "Only MAX limit should be set\n");
 
+    /* set both limits, then set max < min */
+    GetSystemTime(&st[0]);
+    st[1] = st[0];
+    st[1].wYear++;
+    ok(SendMessage(hwnd, MCM_SETRANGE, GDTR_MIN|GDTR_MAX, (LPARAM)st), "Failed to set limits\n");
+    ok(SendMessage(hwnd, MCM_GETRANGE, 0, (LPARAM)st1) == (GDTR_MIN|GDTR_MAX), "Min limit expected\n");
+    st[1].wYear -= 2;
+    ok(SendMessage(hwnd, MCM_SETRANGE, GDTR_MAX, (LPARAM)st), "Failed to set limits\n");
+    ok(SendMessage(hwnd, MCM_GETRANGE, 0, (LPARAM)st1) == GDTR_MAX, "Max limit expected\n");
+
+    expect(0, st1[0].wYear);
+    expect(0, st1[0].wMonth);
+    expect(0, st1[0].wDay);
+    expect(0, st1[0].wDayOfWeek);
+    expect(0, st1[0].wHour);
+    expect(0, st1[0].wMinute);
+    expect(0, st1[0].wSecond);
+    expect(0, st1[0].wMilliseconds);
+
+    expect(st[1].wYear,      st1[1].wYear);
+    expect(st[1].wMonth,     st1[1].wMonth);
+    expect(st[1].wDay,       st1[1].wDay);
+    expect(st[1].wDayOfWeek, st1[1].wDayOfWeek);
+    expect(st[1].wHour,      st1[1].wHour);
+    expect(st[1].wMinute,    st1[1].wMinute);
+    expect(st[1].wSecond,    st1[1].wSecond);
+    expect(st[1].wMilliseconds, st1[1].wMilliseconds);
+
+    st[1] = st[0];
+    st[1].wYear++;
+    ok(SendMessage(hwnd, MCM_SETRANGE, GDTR_MIN|GDTR_MAX, (LPARAM)st), "Failed to set limits\n");
+    ok(SendMessage(hwnd, MCM_GETRANGE, 0, (LPARAM)st1) == (GDTR_MIN|GDTR_MAX), "Min limit expected\n");
+    st[0].wYear++; /* start == end now */
+    ok(SendMessage(hwnd, MCM_SETRANGE, GDTR_MIN, (LPARAM)st), "Failed to set limits\n");
+    ok(SendMessage(hwnd, MCM_GETRANGE, 0, (LPARAM)st1) == GDTR_MIN, "Min limit expected\n");
+
+    expect(st[0].wYear,      st1[0].wYear);
+    expect(st[0].wMonth,     st1[0].wMonth);
+    expect(st[0].wDay,       st1[0].wDay);
+    expect(st[0].wDayOfWeek, st1[0].wDayOfWeek);
+    expect(st[0].wHour,      st1[0].wHour);
+    expect(st[0].wMinute,    st1[0].wMinute);
+    expect(st[0].wSecond,    st1[0].wSecond);
+    expect(st[0].wMilliseconds, st1[0].wMilliseconds);
+
+    expect(0, st1[1].wYear);
+    expect(0, st1[1].wMonth);
+    expect(0, st1[1].wDay);
+    expect(0, st1[1].wDayOfWeek);
+    expect(0, st1[1].wHour);
+    expect(0, st1[1].wMinute);
+    expect(0, st1[1].wSecond);
+    expect(0, st1[1].wMilliseconds);
+
     DestroyWindow(hwnd);
 }
 
@@ -455,7 +576,7 @@ static HWND create_parent_window(void)
 
 static LRESULT WINAPI monthcal_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
-    struct subclass_info *info = (struct subclass_info *)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
+    WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
     static LONG defwndproc_counter = 0;
     LRESULT ret;
     struct message msg;
@@ -467,38 +588,40 @@ static LRESULT WINAPI monthcal_subclass_proc(HWND hwnd, UINT message, WPARAM wPa
     msg.lParam = lParam;
     add_message(sequences, MONTHCAL_SEQ_INDEX, &msg);
 
+    /* some debug output for style changing */
+    if ((message == WM_STYLECHANGING ||
+         message == WM_STYLECHANGED) && lParam)
+    {
+        STYLESTRUCT *style = (STYLESTRUCT*)lParam;
+        trace("\told style: 0x%08x, new style: 0x%08x\n", style->styleOld, style->styleNew);
+    }
+
     defwndproc_counter++;
-    ret = CallWindowProcA(info->oldproc, hwnd, message, wParam, lParam);
+    ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
     defwndproc_counter--;
 
     return ret;
 }
 
-static HWND create_monthcal_control(DWORD style, HWND parent_window)
+static HWND create_monthcal_control(DWORD style)
 {
-    struct subclass_info *info;
+    WNDPROC oldproc;
     HWND hwnd;
 
-    info = HeapAlloc(GetProcessHeap(), 0, sizeof(struct subclass_info));
-    if (!info)
-        return NULL;
-
     hwnd = CreateWindowEx(0,
                     MONTHCAL_CLASS,
                     "",
-                    style,
+                    WS_CHILD | WS_BORDER | WS_VISIBLE | style,
                     0, 0, 300, 400,
-                    parent_window, NULL, GetModuleHandleA(NULL), NULL);
+                    parent_wnd, NULL, GetModuleHandleA(NULL), NULL);
 
-    if (!hwnd)
-    {
-        HeapFree(GetProcessHeap(), 0, info);
-        return NULL;
-    }
+    if (!hwnd) return NULL;
+
+    oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
+                                        (LONG_PTR)monthcal_subclass_proc);
+    SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc);
 
-    info->oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
-                                            (LONG_PTR)monthcal_subclass_proc);
-    SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)info);
+    SendMessage(hwnd, WM_SETFONT, (WPARAM)GetStockObject(SYSTEM_FONT), 0);
 
     return hwnd;
 }
@@ -506,9 +629,12 @@ static HWND create_monthcal_control(DWORD style, HWND parent_window)
 
 /* Setter and Getters Tests */
 
-static void test_monthcal_color(HWND hwnd)
+static void test_monthcal_color(void)
 {
     int res, temp;
+    HWND hwnd;
+
+    hwnd = create_monthcal_control(0);
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
@@ -574,12 +700,17 @@ static void test_monthcal_color(HWND hwnd)
     expect(RGB(255,255,255), temp);
 
     ok_sequence(sequences, MONTHCAL_SEQ_INDEX, monthcal_color_seq, "monthcal color", FALSE);
+
+    DestroyWindow(hwnd);
 }
 
-static void test_monthcal_currDate(HWND hwnd)
+static void test_monthcal_currdate(void)
 {
     SYSTEMTIME st_original, st_new, st_test;
     int res;
+    HWND hwnd;
+
+    hwnd = create_monthcal_control(0);
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
@@ -629,28 +760,94 @@ static void test_monthcal_currDate(HWND hwnd)
     expect(st_original.wYear, st_new.wYear);
     expect(st_original.wMonth, st_new.wMonth);
     expect(st_original.wDay, st_new.wDay);
-    expect(st_original.wHour, st_new.wHour);
-    expect(st_original.wMinute, st_new.wMinute);
-    expect(st_original.wSecond, st_new.wSecond);
+    ok(st_original.wHour == st_new.wHour ||
+       broken(0 == st_new.wHour), /* comctl32 <= 4.70 */
+       "Expected %d, got %d\n", st_original.wHour, st_new.wHour);
+    ok(st_original.wMinute == st_new.wMinute ||
+       broken(0 == st_new.wMinute), /* comctl32 <= 4.70 */
+       "Expected %d, got %d\n", st_original.wMinute, st_new.wMinute);
+    ok(st_original.wSecond == st_new.wSecond ||
+       broken(0 == st_new.wSecond), /* comctl32 <= 4.70 */
+       "Expected %d, got %d\n", st_original.wSecond, st_new.wSecond);
 
     /* lparam cannot be NULL */
     res = SendMessage(hwnd, MCM_GETCURSEL, 0, 0);
     expect(0, res);
 
     ok_sequence(sequences, MONTHCAL_SEQ_INDEX, monthcal_curr_date_seq, "monthcal currDate", TRUE);
+
+    /* December, 31, 9999 is the maximum allowed date */
+    memset(&st_new, 0, sizeof(st_new));
+    st_new.wYear = 9999;
+    st_new.wMonth = 12;
+    st_new.wDay = 31;
+    res = SendMessage(hwnd, MCM_SETCURSEL, 0, (LPARAM)&st_new);
+    expect(1, res);
+    memset(&st_test, 0, sizeof(st_test));
+    res = SendMessage(hwnd, MCM_GETCURSEL, 0, (LPARAM)&st_test);
+    expect(1, res);
+    expect(st_new.wYear, st_test.wYear);
+    expect(st_new.wMonth, st_test.wMonth);
+    expect(st_new.wDay, st_test.wDay);
+    expect(st_new.wHour, st_test.wHour);
+    expect(st_new.wMinute, st_test.wMinute);
+    expect(st_new.wSecond, st_test.wSecond);
+    /* try one day later */
+    st_original = st_new;
+    st_new.wYear = 10000;
+    st_new.wMonth = 1;
+    st_new.wDay = 1;
+    res = SendMessage(hwnd, MCM_SETCURSEL, 0, (LPARAM)&st_new);
+    ok(0 == res ||
+       broken(1 == res), /* comctl32 <= 4.72 */
+       "Expected 0, got %d\n", res);
+    if (0 == res)
+    {
+        memset(&st_test, 0, sizeof(st_test));
+        res = SendMessage(hwnd, MCM_GETCURSEL, 0, (LPARAM)&st_test);
+        expect(1, res);
+        expect(st_original.wYear, st_test.wYear);
+        expect(st_original.wMonth, st_test.wMonth);
+        expect(st_original.wDay, st_test.wDay);
+        expect(st_original.wHour, st_test.wHour);
+        expect(st_original.wMinute, st_test.wMinute);
+        expect(st_original.wSecond, st_test.wSecond);
+    }
+
+    /* setting selection equal to current reports success even if out range */
+    memset(&st_new, 0, sizeof(st_new));
+    st_new.wYear = 2009;
+    st_new.wDay  = 5;
+    st_new.wMonth = 10;
+    res = SendMessage(hwnd, MCM_SETCURSEL, 0, (LPARAM)&st_new);
+    expect(1, res);
+    memset(&st_test, 0, sizeof(st_test));
+    st_test.wYear = 2009;
+    st_test.wDay  = 6;
+    st_test.wMonth = 10;
+    res = SendMessage(hwnd, MCM_SETRANGE, GDTR_MIN, (LPARAM)&st_test);
+    expect(1, res);
+    /* set to current again */
+    res = SendMessage(hwnd, MCM_SETCURSEL, 0, (LPARAM)&st_new);
+    expect(1, res);
+
+    DestroyWindow(hwnd);
 }
 
-static void test_monthcal_firstDay(HWND hwnd)
+static void test_monthcal_firstDay(void)
 {
     int res, fday, i, prev;
-    TCHAR b[128];
+    CHAR b[128];
     LCID lcid = LOCALE_USER_DEFAULT;
+    HWND hwnd;
+
+    hwnd = create_monthcal_control(0);
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
     /* Setter and Getters for first day of week */
     /* check for locale first day */
-    if(GetLocaleInfo(lcid, LOCALE_IFIRSTDAYOFWEEK, b, 128)){
+    if(GetLocaleInfoA(lcid, LOCALE_IFIRSTDAYOFWEEK, b, 128)){
         fday = atoi(b);
         trace("fday: %d\n", fday);
         res = SendMessage(hwnd, MCM_GETFIRSTDAYOFWEEK, 0, 0);
@@ -660,7 +857,7 @@ static void test_monthcal_firstDay(HWND hwnd)
         /* checking for the values that actually will be stored as */
         /* current first day when we set a new value */
         for (i = -5; i < 12; i++){
-            res = SendMessage(hwnd, MCM_SETFIRSTDAYOFWEEK, 0, (LPARAM) i);
+            res = SendMessage(hwnd, MCM_SETFIRSTDAYOFWEEK, 0, i);
             expect(prev, res);
             res = SendMessage(hwnd, MCM_GETFIRSTDAYOFWEEK, 0, 0);
             prev = res;
@@ -681,11 +878,15 @@ static void test_monthcal_firstDay(HWND hwnd)
         skip("Cannot retrieve first day of the week\n");
     }
 
+    DestroyWindow(hwnd);
 }
 
-static void test_monthcal_unicode(HWND hwnd)
+static void test_monthcal_unicode(void)
 {
     int res, temp;
+    HWND hwnd;
+
+    hwnd = create_monthcal_control(0);
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
@@ -700,11 +901,15 @@ static void test_monthcal_unicode(HWND hwnd)
 
     /* current setting is 1, so, should return 1 */
     res = SendMessage(hwnd, MCM_GETUNICODEFORMAT, 0, 0);
-    todo_wine {expect(1, res);}
+    ok(1 == res ||
+       broken(0 == res), /* comctl32 <= 4.70 */
+       "Expected 1, got %d\n", res);
 
     /* setting to 0, should return previous settings */
     res = SendMessage(hwnd, MCM_SETUNICODEFORMAT, 0, 0);
-    todo_wine {expect(1, res);}
+    ok(1 == res ||
+       broken(0 == res), /* comctl32 <= 4.70 */
+       "Expected 1, got %d\n", res);
 
     /* current setting is 0, so, it should return 0 */
     res = SendMessage(hwnd, MCM_GETUNICODEFORMAT, 0, 0);
@@ -715,22 +920,67 @@ static void test_monthcal_unicode(HWND hwnd)
     expect(0, res);
 
     ok_sequence(sequences, MONTHCAL_SEQ_INDEX, monthcal_unicode_seq, "monthcal unicode", FALSE);
+
+    DestroyWindow(hwnd);
 }
 
-static void test_monthcal_HitTest(HWND hwnd)
+static void test_monthcal_hittest(void)
 {
+    typedef struct hittest_test
+    {
+       UINT ht;
+        int  todo;
+    } hittest_test_t;
+
+    static const hittest_test_t title_hits[] = {
+        /* Start is the same everywhere */
+        { MCHT_TITLE,        0 },
+        { MCHT_TITLEBTNPREV, 0 },
+        /* The middle piece is only tested for presence of items */
+        /* End is the same everywhere */
+        { MCHT_TITLEBTNNEXT, 0 },
+        { MCHT_TITLE,        0 },
+        { MCHT_NOWHERE,      1 }
+    };
+
     MCHITTESTINFO mchit;
-    UINT res;
+    UINT res, old_res;
     SYSTEMTIME st;
     LONG x;
     UINT title_index;
-    static const UINT title_hits[] =
-        { MCHT_NOWHERE, MCHT_TITLEBK, MCHT_TITLEBTNPREV, MCHT_TITLEBK,
-          MCHT_TITLEMONTH, MCHT_TITLEBK, MCHT_TITLEYEAR, MCHT_TITLEBK,
-          MCHT_TITLEBTNNEXT, MCHT_TITLEBK, MCHT_NOWHERE };
+    HWND hwnd;
+    RECT r;
+    char yearmonth[80], *locale_month, *locale_year;
+    int month_count, year_count;
+    BOOL in_the_middle;
 
     memset(&mchit, 0, sizeof(MCHITTESTINFO));
 
+    hwnd = create_monthcal_control(0);
+
+    /* test with invalid structure size */
+    mchit.cbSize = MCHITTESTINFO_V1_SIZE - 1;
+    mchit.pt.x = 0;
+    mchit.pt.y = 0;
+    res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM)&mchit);
+    expect(0, mchit.pt.x);
+    expect(0, mchit.pt.y);
+    expect(-1, res);
+    expect(0, mchit.uHit);
+    /* test with invalid pointer */
+    res = SendMessage(hwnd, MCM_HITTEST, 0, 0);
+    expect(-1, res);
+
+    /* resize control to display single Calendar */
+    res = SendMessage(hwnd, MCM_GETMINREQRECT, 0, (LPARAM)&r);
+    if (res == 0)
+    {
+        win_skip("Message MCM_GETMINREQRECT unsupported. Skipping.\n");
+        DestroyWindow(hwnd);
+        return;
+    }
+    MoveWindow(hwnd, 0, 0, r.right, r.bottom, FALSE);
+
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
     st.wYear = 2007;
@@ -745,60 +995,60 @@ static void test_monthcal_HitTest(HWND hwnd)
     res = SendMessage(hwnd, MCM_SETCURSEL, 0, (LPARAM)&st);
     expect(1,res);
 
-    /* (0, 0) is the top left of the control and should not be active */
-    mchit.cbSize = sizeof(MCHITTESTINFO);
+    /* (0, 0) is the top left of the control - title */
+    mchit.cbSize = MCHITTESTINFO_V1_SIZE;
     mchit.pt.x = 0;
     mchit.pt.y = 0;
-    res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM) & mchit);
+    res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM)&mchit);
     expect(0, mchit.pt.x);
     expect(0, mchit.pt.y);
     expect(mchit.uHit, res);
-    todo_wine {expect(MCHT_NOWHERE, res);}
-
-    /* (300, 400) is the bottom right of the control and should not be active */
-    mchit.pt.x = 300;
-    mchit.pt.y = 400;
-    res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM) & mchit);
-    expect(300, mchit.pt.x);
-    expect(400, mchit.pt.y);
+    expect_hex(MCHT_TITLE, res);
+
+    /* bottom right of the control and should not be active */
+    mchit.pt.x = r.right;
+    mchit.pt.y = r.bottom;
+    res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM)&mchit);
+    expect(r.right,  mchit.pt.x);
+    expect(r.bottom, mchit.pt.y);
     expect(mchit.uHit, res);
-    todo_wine {expect(MCHT_NOWHERE, res);}
+    todo_wine expect_hex(MCHT_NOWHERE, res);
 
-    /* (500, 500) is completely out of the control and should not be active */
-    mchit.pt.x = 500;
-    mchit.pt.y = 500;
+    /* completely out of the control, should not be active */
+    mchit.pt.x = 2 * r.right;
+    mchit.pt.y = 2 * r.bottom;
     res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM) & mchit);
-    expect(500, mchit.pt.x);
-    expect(500, mchit.pt.y);
+    expect(2 * r.right, mchit.pt.x);
+    expect(2 * r.bottom, mchit.pt.y);
     expect(mchit.uHit, res);
-    todo_wine {expect(MCHT_NOWHERE, res);}
+    todo_wine expect_hex(MCHT_NOWHERE, res);
 
-    /* (120, 180) is in active area - calendar background */
-    mchit.pt.x = 120;
-    mchit.pt.y = 180;
+    /* in active area - day of the week */
+    mchit.pt.x = r.right / 2;
+    mchit.pt.y = r.bottom / 2;
     res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM) & mchit);
-    expect(120, mchit.pt.x);
-    expect(180, mchit.pt.y);
+    expect(r.right / 2, mchit.pt.x);
+    expect(r.bottom / 2, mchit.pt.y);
     expect(mchit.uHit, res);
-    expect(MCHT_CALENDARBK, res);
+    expect_hex(MCHT_CALENDARDATE, res);
 
-    /* (70, 70) is in active area - day of the week */
-    mchit.pt.x = 70;
-    mchit.pt.y = 70;
+    /* in active area - day of the week #2 */
+    mchit.pt.x = r.right / 14; /* half of first day rect */
+    mchit.pt.y = r.bottom / 2;
     res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM) & mchit);
-    expect(70, mchit.pt.x);
-    expect(70, mchit.pt.y);
+    expect(r.right / 14, mchit.pt.x);
+    expect(r.bottom / 2, mchit.pt.y);
     expect(mchit.uHit, res);
-    todo_wine {expect(MCHT_CALENDARDAY, res);}
+    expect_hex(MCHT_CALENDARDATE, res);
 
-    /* (70, 90) is in active area - date from prev month */
-    mchit.pt.x = 70;
-    mchit.pt.y = 90;
+    /* in active area - date from prev month */
+    mchit.pt.x = r.right / 14; /* half of first day rect */
+    mchit.pt.y = 6 * r.bottom / 19;
     res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM) & mchit);
-    expect(70, mchit.pt.x);
-    expect(90, mchit.pt.y);
+    expect(r.right / 14, mchit.pt.x);
+    expect(6 * r.bottom / 19, mchit.pt.y);
     expect(mchit.uHit, res);
-    todo_wine {expect(MCHT_CALENDARDATEPREV, res);}
+    expect_hex(MCHT_CALENDARDATEPREV, res);
 
 #if 0
     /* (125, 115) is in active area - date from this month */
@@ -811,142 +1061,164 @@ static void test_monthcal_HitTest(HWND hwnd)
     expect(MCHT_CALENDARDATE, res);
 #endif
 
-    /* (80, 220) is in active area - background section of the title */
-    mchit.pt.x = 80;
-    mchit.pt.y = 220;
+    /* in active area - date from next month */
+    mchit.pt.x = 11 * r.right / 14;
+    mchit.pt.y = 16 * r.bottom / 19;
     res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM) & mchit);
-    expect(80, mchit.pt.x);
-    expect(220, mchit.pt.y);
+    expect(11 * r.right / 14, mchit.pt.x);
+    expect(16 * r.bottom / 19, mchit.pt.y);
     expect(mchit.uHit, res);
-    todo_wine {expect(MCHT_TITLEBK, res);}
+    expect_hex(MCHT_CALENDARDATENEXT, res);
 
-    /* (140, 215) is in active area - month section of the title */
-    mchit.pt.x = 140;
-    mchit.pt.y = 215;
+    /* in active area - today link */
+    mchit.pt.x = r.right / 14;
+    mchit.pt.y = 18 * r.bottom / 19;
     res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM) & mchit);
-    expect(140, mchit.pt.x);
-    expect(215, mchit.pt.y);
+    expect(r.right / 14, mchit.pt.x);
+    expect(18 * r.bottom / 19, mchit.pt.y);
     expect(mchit.uHit, res);
-    todo_wine {expect(MCHT_TITLEMONTH, res);}
+    expect_hex(MCHT_TODAYLINK, res);
 
-    /* (170, 215) is in active area - year section of the title */
-    mchit.pt.x = 170;
-    mchit.pt.y = 215;
+    /* in active area - today link */
+    mchit.pt.x = r.right / 2;
+    mchit.pt.y = 18 * r.bottom / 19;
     res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM) & mchit);
-    expect(170, mchit.pt.x);
-    expect(215, mchit.pt.y);
+    expect(r.right / 2, mchit.pt.x);
+    expect(18 * r.bottom / 19, mchit.pt.y);
     expect(mchit.uHit, res);
-    todo_wine {expect(MCHT_TITLEYEAR, res);}
+    expect_hex(MCHT_TODAYLINK, res);
 
-    /* (150, 260) is in active area - date from this month */
-    mchit.pt.x = 150;
-    mchit.pt.y = 260;
+    /* in active area - today link */
+    mchit.pt.x = r.right / 10;
+    mchit.pt.y = 18 * r.bottom / 19;
     res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM) & mchit);
-    expect(150, mchit.pt.x);
-    expect(260, mchit.pt.y);
+    expect(r.right / 10, mchit.pt.x);
+    expect(18 * r.bottom / 19, mchit.pt.y);
     expect(mchit.uHit, res);
-    todo_wine {expect(MCHT_CALENDARDATE, res);}
-
-    /* (150, 350) is in active area - date from next month */
-    mchit.pt.x = 150;
-    mchit.pt.y = 350;
-    res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM) & mchit);
-    expect(150, mchit.pt.x);
-    expect(350, mchit.pt.y);
-    expect(mchit.uHit, res);
-    todo_wine {expect(MCHT_CALENDARDATENEXT, res);}
-
-    /* (150, 370) is in active area - today link */
-    mchit.pt.x = 150;
-    mchit.pt.y = 370;
-    res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM) & mchit);
-    expect(150, mchit.pt.x);
-    expect(370, mchit.pt.y);
-    expect(mchit.uHit, res);
-    todo_wine {expect(MCHT_TODAYLINK, res);}
-
-    /* (70, 370) is in active area - today link */
-    mchit.pt.x = 70;
-    mchit.pt.y = 370;
-    res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM) & mchit);
-    expect(70, mchit.pt.x);
-    expect(370, mchit.pt.y);
-    expect(mchit.uHit, res);
-    todo_wine {expect(MCHT_TODAYLINK, res);}
+    expect_hex(MCHT_TODAYLINK, res);
 
     ok_sequence(sequences, MONTHCAL_SEQ_INDEX, monthcal_hit_test_seq, "monthcal hit test", TRUE);
 
     /* The horizontal position of title bar elements depends on locale (y pos
        is constant), so we sample across a horizontal line and make sure we
        find all elements. */
-    mchit.pt.y = 40;
+
+    /* Get the format of the title */
+    GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SYEARMONTH, yearmonth, 80);
+    /* Find out if we have a month and/or year */
+    locale_year = strstr(yearmonth, "y");
+    locale_month = strstr(yearmonth, "M");
+
+    mchit.pt.x = 0;
+    mchit.pt.y = (5/2) * r.bottom / 19;
     title_index = 0;
-    for (x = 0; x < 300; x++){
+    old_res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM) & mchit);
+    expect_hex(title_hits[title_index].ht, old_res);
+
+    in_the_middle = FALSE;
+    month_count = year_count = 0;
+    for (x = 0; x < r.right; x++){
         mchit.pt.x = x;
         res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM) & mchit);
         expect(x, mchit.pt.x);
-        expect(40, mchit.pt.y);
+        expect((5/2) * r.bottom / 19, mchit.pt.y);
         expect(mchit.uHit, res);
-        if (res != title_hits[title_index]){
-            title_index++;
-            if (sizeof(title_hits) / sizeof(title_hits[0]) <= title_index)
-                break;
-            todo_wine {expect(title_hits[title_index], res);}
+        if (res != old_res) {
+
+            if (old_res == MCHT_TITLEBTNPREV)
+                in_the_middle = TRUE;
+
+            if (res == MCHT_TITLEBTNNEXT)
+                in_the_middle = FALSE;
+
+            if (in_the_middle) {
+                if (res == MCHT_TITLEMONTH)
+                    month_count++;
+                else if (res == MCHT_TITLEYEAR)
+                    year_count++;
+            } else {
+                title_index++;
+
+                if (sizeof(title_hits) / sizeof(title_hits[0]) <= title_index)
+                    break;
+
+                if (title_hits[title_index].todo) {
+                    todo_wine
+                    ok(title_hits[title_index].ht == res, "Expected %x, got %x, pos %d\n",
+                                                          title_hits[title_index].ht, res, x);
+                } else {
+                    ok(title_hits[title_index].ht == res, "Expected %x, got %x, pos %d\n",
+                                                          title_hits[title_index].ht, res, x);
+                }
+            }
+            old_res = res;
         }
     }
-    todo_wine {ok(300 <= x && title_index + 1 == sizeof(title_hits) / sizeof(title_hits[0]),
-        "Wrong title layout\n");}
+
+    /* There are some limits, even if LOCALE_SYEARMONTH contains rubbish
+     * or no month/year indicators at all */
+    if (locale_month)
+        todo_wine ok(month_count == 1, "Expected 1 month item, got %d\n", month_count);
+    else
+        ok(month_count <= 1, "Too many month items: %d\n", month_count);
+
+    if (locale_year)
+        todo_wine ok(year_count == 1, "Expected 1 year item, got %d\n", year_count);
+    else
+        ok(year_count <= 1, "Too many year items: %d\n", year_count);
+
+    todo_wine ok(month_count + year_count >= 1, "Not enough month and year items\n");
+
+    ok(r.right <= x && title_index + 1 == sizeof(title_hits) / sizeof(title_hits[0]),
+       "Wrong title layout\n");
+
+    DestroyWindow(hwnd);
 }
 
-static void test_monthcal_todaylink(HWND hwnd)
+static void test_monthcal_todaylink(void)
 {
     MCHITTESTINFO mchit;
     SYSTEMTIME st_test, st_new;
-    BOOL error = FALSE;
     UINT res;
+    HWND hwnd;
+    RECT r;
 
     memset(&mchit, 0, sizeof(MCHITTESTINFO));
 
+    hwnd = create_monthcal_control(0);
+
+    res = SendMessage(hwnd, MCM_GETMINREQRECT, 0, (LPARAM)&r);
+    MoveWindow(hwnd, 0, 0, r.right, r.bottom, FALSE);
+
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
-    /* (70, 370) is in active area - today link */
-    mchit.cbSize = sizeof(MCHITTESTINFO);
-    mchit.pt.x = 70;
-    mchit.pt.y = 370;
+    /* hit active area - today link */
+    mchit.cbSize = MCHITTESTINFO_V1_SIZE;
+    mchit.pt.x = r.right / 14;
+    mchit.pt.y = 18 * r.bottom / 19;
     res = SendMessage(hwnd, MCM_HITTEST, 0, (LPARAM) & mchit);
-    expect(70, mchit.pt.x);
-    expect(370, mchit.pt.y);
+    expect(r.right / 14, mchit.pt.x);
+    expect(18 * r.bottom / 19, mchit.pt.y);
     expect(mchit.uHit, res);
-    todo_wine {expect(MCHT_TODAYLINK, res);}
-    if (70 != mchit.pt.x || 370 != mchit.pt.y || mchit.uHit != res
-        || MCHT_TODAYLINK != res)
-        error = TRUE;
+    expect(MCHT_TODAYLINK, res);
 
     st_test.wDay = 1;
     st_test.wMonth = 1;
     st_test.wYear = 2005;
-    memset(&st_new, 0, sizeof(SYSTEMTIME));
 
     SendMessage(hwnd, MCM_SETTODAY, 0, (LPARAM)&st_test);
 
+    memset(&st_new, 0, sizeof(st_new));
     res = SendMessage(hwnd, MCM_GETTODAY, 0, (LPARAM)&st_new);
     expect(1, res);
     expect(1, st_new.wDay);
     expect(1, st_new.wMonth);
     expect(2005, st_new.wYear);
-    if (1 != res || 1 != st_new.wDay || 1 != st_new.wMonth
-        || 2005 != st_new.wYear)
-        error = TRUE;
 
-    if (error) {
-        skip("cannot perform today link test\n");
-        return;
-    }
-
-    res = SendMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELONG(70, 370));
+    res = SendMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELONG(mchit.pt.x, mchit.pt.y));
     expect(0, res);
 
-    memset(&st_new, 0, sizeof(SYSTEMTIME));
+    memset(&st_new, 0, sizeof(st_new));
     res = SendMessage(hwnd, MCM_GETCURSEL, 0, (LPARAM)&st_new);
     expect(1, res);
     expect(1, st_new.wDay);
@@ -954,18 +1226,24 @@ static void test_monthcal_todaylink(HWND hwnd)
     expect(2005, st_new.wYear);
 
     ok_sequence(sequences, MONTHCAL_SEQ_INDEX, monthcal_todaylink_seq, "monthcal hit test", TRUE);
+
+    DestroyWindow(hwnd);
 }
 
-static void test_monthcal_today(HWND hwnd)
+static void test_monthcal_today(void)
 {
     SYSTEMTIME st_test, st_new;
     int res;
+    HWND hwnd;
+
+    hwnd = create_monthcal_control(0);
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
     /* Setter and Getters for "today" information */
 
     /* check for overflow, should be ok */
+    memset(&st_test, 0, sizeof(st_test));
     st_test.wDay = 38;
     st_test.wMonth = 38;
 
@@ -1003,11 +1281,16 @@ static void test_monthcal_today(HWND hwnd)
     expect(0, st_new.wMonth);
 
     ok_sequence(sequences, MONTHCAL_SEQ_INDEX, monthcal_today_seq, "monthcal today", TRUE);
+
+    DestroyWindow(hwnd);
 }
 
-static void test_monthcal_scroll(HWND hwnd)
+static void test_monthcal_scroll(void)
 {
     int res;
+    HWND hwnd;
+
+    hwnd = create_monthcal_control(0);
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
@@ -1036,46 +1319,138 @@ static void test_monthcal_scroll(HWND hwnd)
     expect(-5, res);
 
     ok_sequence(sequences, MONTHCAL_SEQ_INDEX, monthcal_scroll_seq, "monthcal scroll", FALSE);
+
+    DestroyWindow(hwnd);
 }
 
-static void test_monthcal_monthrange(HWND hwnd)
+static void test_monthcal_monthrange(void)
 {
     int res;
-    SYSTEMTIME st_visible[2], st_daystate[2];
+    SYSTEMTIME st_visible[2], st_daystate[2], st;
+    HWND hwnd;
+    RECT r;
+
+    hwnd = create_monthcal_control(0);
 
-    flush_sequences(sequences, NUM_MSG_SEQUENCES);
     st_visible[0].wYear = 0;
     st_visible[0].wMonth = 0;
     st_visible[0].wDay = 0;
     st_daystate[1] = st_daystate[0] = st_visible[1] = st_visible[0];
 
+    st.wYear = 2000;
+    st.wMonth = 11;
+    st.wDay = 28;
+    st.wHour = 11;
+    st.wMinute = 59;
+    st.wSecond = 30;
+    st.wMilliseconds = 0;
+    st.wDayOfWeek = 0;
+
+    res = SendMessage(hwnd, MCM_SETCURSEL, 0, (LPARAM)&st);
+    expect(1,res);
+
+    /* to be locale independent */
+    SendMessage(hwnd, MCM_SETFIRSTDAYOFWEEK, 0, (LPARAM)6);
+
+    res = SendMessage(hwnd, MCM_GETMINREQRECT, 0, (LPARAM)&r);
+    expect(TRUE, res);
+    /* resize control to display two Calendars */
+    MoveWindow(hwnd, 0, 0, r.right, (5/2)*r.bottom, FALSE);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
     res = SendMessage(hwnd, MCM_GETMONTHRANGE, GMR_VISIBLE, (LPARAM)st_visible);
     todo_wine {
         expect(2, res);
-        expect(2000, st_visible[0].wYear);
-        expect(11, st_visible[0].wMonth);
-        expect(1, st_visible[0].wDay);
-        expect(2000, st_visible[1].wYear);
+    }
+    expect(2000, st_visible[0].wYear);
+    expect(11, st_visible[0].wMonth);
+    expect(1, st_visible[0].wDay);
+    expect(2000, st_visible[1].wYear);
+
+    todo_wine {
         expect(12, st_visible[1].wMonth);
         expect(31, st_visible[1].wDay);
     }
     res = SendMessage(hwnd, MCM_GETMONTHRANGE, GMR_DAYSTATE, (LPARAM)st_daystate);
     todo_wine {
         expect(4, res);
-        expect(2000, st_daystate[0].wYear);
-        expect(10, st_daystate[0].wMonth);
-        expect(29, st_daystate[0].wDay);
+    }
+    expect(2000, st_daystate[0].wYear);
+    expect(10, st_daystate[0].wMonth);
+    expect(29, st_daystate[0].wDay);
+
+    todo_wine {
         expect(2001, st_daystate[1].wYear);
         expect(1, st_daystate[1].wMonth);
         expect(6, st_daystate[1].wDay);
     }
 
     ok_sequence(sequences, MONTHCAL_SEQ_INDEX, monthcal_monthrange_seq, "monthcal monthrange", FALSE);
+
+    /* resize control to display single Calendar */
+    MoveWindow(hwnd, 0, 0, r.right, r.bottom, FALSE);
+
+    memset(&st, 0, sizeof(st));
+    st.wMonth = 9;
+    st.wYear  = 1752;
+    st.wDay   = 14;
+
+    res = SendMessage(hwnd, MCM_SETCURSEL, 0, (LPARAM)&st);
+    expect(1, res);
+
+    /* September 1752 has 19 days */
+    res = SendMessage(hwnd, MCM_GETMONTHRANGE, GMR_VISIBLE, (LPARAM)st_visible);
+    expect(1, res);
+
+    expect(1752, st_visible[0].wYear);
+    expect(9, st_visible[0].wMonth);
+    ok(14 == st_visible[0].wDay ||
+       broken(1 == st_visible[0].wDay), /* comctl32 <= 4.72 */
+       "Expected 14, got %d\n", st_visible[0].wDay);
+
+    expect(1752, st_visible[1].wYear);
+    expect(9, st_visible[1].wMonth);
+    expect(19, st_visible[1].wDay);
+
+    DestroyWindow(hwnd);
 }
 
-static void test_monthcal_MaxSelDay(HWND hwnd)
+static void test_monthcal_maxselday(void)
 {
     int res;
+    HWND hwnd;
+    DWORD style;
+
+    hwnd = create_monthcal_control(0);
+    /* if no style specified default to 1 */
+    res = SendMessage(hwnd, MCM_GETMAXSELCOUNT, 0, 0);
+    expect(1, res);
+    res = SendMessage(hwnd, MCM_SETMAXSELCOUNT, 5, 0);
+    expect(0, res);
+    res = SendMessage(hwnd, MCM_GETMAXSELCOUNT, 0, 0);
+    expect(1, res);
+
+    /* try to set style */
+    style = GetWindowLong(hwnd, GWL_STYLE);
+    SetWindowLong(hwnd, GWL_STYLE, style | MCS_MULTISELECT);
+    style = GetWindowLong(hwnd, GWL_STYLE);
+    ok(!(style & MCS_MULTISELECT), "Expected MCS_MULTISELECT not to be set\n");
+    DestroyWindow(hwnd);
+
+    hwnd = create_monthcal_control(MCS_MULTISELECT);
+    /* try to remove style */
+    style = GetWindowLong(hwnd, GWL_STYLE);
+    SetWindowLong(hwnd, GWL_STYLE, style & ~MCS_MULTISELECT);
+    style = GetWindowLong(hwnd, GWL_STYLE);
+    ok(style & MCS_MULTISELECT, "Expected MCS_MULTISELECT to be set\n");
+    DestroyWindow(hwnd);
+
+    hwnd = create_monthcal_control(MCS_MULTISELECT);
+
+    /* default width is a week */
+    res = SendMessage(hwnd, MCM_GETMAXSELCOUNT, 0, 0);
+    expect(7, res);
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
 
@@ -1090,20 +1465,32 @@ static void test_monthcal_MaxSelDay(HWND hwnd)
     res = SendMessage(hwnd, MCM_GETMAXSELCOUNT, 0, 0);
     expect(15, res);
 
+    /* test invalid value */
     res = SendMessage(hwnd, MCM_SETMAXSELCOUNT, -1, 0);
-    todo_wine {expect(0, res);}
+    expect(0, res);
     res = SendMessage(hwnd, MCM_GETMAXSELCOUNT, 0, 0);
-    todo_wine {expect(15, res);}
+    expect(15, res);
 
     ok_sequence(sequences, MONTHCAL_SEQ_INDEX, monthcal_max_sel_day_seq, "monthcal MaxSelDay", FALSE);
+
+    /* zero value is invalid too */
+    res = SendMessage(hwnd, MCM_SETMAXSELCOUNT, 0, 0);
+    expect(0, res);
+    res = SendMessage(hwnd, MCM_GETMAXSELCOUNT, 0, 0);
+    expect(15, res);
+
+    DestroyWindow(hwnd);
 }
 
-static void test_monthcal_size(HWND hwnd)
+static void test_monthcal_size(void)
 {
     int res;
     RECT r1, r2;
     HFONT hFont1, hFont2;
     LOGFONTA logfont;
+    HWND hwnd;
+
+    hwnd = create_monthcal_control(0);
 
     lstrcpyA(logfont.lfFaceName, "Arial");
     memset(&logfont, 0, sizeof(logfont));
@@ -1116,15 +1503,181 @@ static void test_monthcal_size(HWND hwnd)
     /* initialize to a font we can compare against */
     SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont1, 0);
     res = SendMessage(hwnd, MCM_GETMINREQRECT, 0, (LPARAM)&r1);
+    ok(res, "SendMessage(MCM_GETMINREQRECT) failed\n");
 
     /* check that setting a larger font results in an larger rect */
     SendMessage(hwnd, WM_SETFONT, (WPARAM)hFont2, 0);
     res = SendMessage(hwnd, MCM_GETMINREQRECT, 0, (LPARAM)&r2);
+    ok(res, "SendMessage(MCM_GETMINREQRECT) failed\n");
 
     OffsetRect(&r1, -r1.left, -r1.top);
     OffsetRect(&r2, -r2.left, -r2.top);
 
     ok(r1.bottom < r2.bottom, "Failed to get larger rect with larger font\n");
+
+    DestroyWindow(hwnd);
+}
+
+static void test_monthcal_create(void)
+{
+    HWND hwnd;
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    hwnd = create_monthcal_control(0);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, create_monthcal_control_seq, "create monthcal control", TRUE);
+
+    DestroyWindow(hwnd);
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    hwnd = create_monthcal_control(MCS_MULTISELECT);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, create_monthcal_multi_sel_style_seq, "create monthcal (multi sel style)", TRUE);
+    DestroyWindow(hwnd);
+}
+
+static void test_monthcal_destroy(void)
+{
+    HWND hwnd;
+
+    hwnd = create_monthcal_control(0);
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    DestroyWindow(hwnd);
+    ok_sequence(sequences, PARENT_SEQ_INDEX, destroy_monthcal_parent_msgs_seq, "Destroy monthcal (parent msg)", FALSE);
+    ok_sequence(sequences, MONTHCAL_SEQ_INDEX, destroy_monthcal_child_msgs_seq, "Destroy monthcal (child msg)", FALSE);
+
+    /* MCS_MULTISELECT */
+    hwnd = create_monthcal_control(MCS_MULTISELECT);
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    DestroyWindow(hwnd);
+    ok_sequence(sequences, MONTHCAL_SEQ_INDEX, destroy_monthcal_multi_sel_style_seq, "Destroy monthcal (multi sel style)", FALSE);
+}
+
+static void test_monthcal_selrange(void)
+{
+    HWND hwnd;
+    SYSTEMTIME st, range[2], range2[2];
+    BOOL ret, old_comctl32 = FALSE;
+
+    hwnd = create_monthcal_control(MCS_MULTISELECT);
+
+    /* just after creation selection should start and end today */
+    ret = SendMessage(hwnd, MCM_GETTODAY, 0, (LPARAM)&st);
+    expect(TRUE, ret);
+
+    memset(range, 0xcc, sizeof(range));
+    ret = SendMessage(hwnd, MCM_GETSELRANGE, 0, (LPARAM)range);
+    expect(TRUE, ret);
+    expect(st.wYear,      range[0].wYear);
+    expect(st.wMonth,     range[0].wMonth);
+    expect(st.wDay,       range[0].wDay);
+    if (range[0].wDayOfWeek != st.wDayOfWeek)
+    {
+        win_skip("comctl32 <= 4.70 doesn't set some values\n");
+        old_comctl32 = TRUE;
+    }
+    else
+    {
+        expect(st.wDayOfWeek, range[0].wDayOfWeek);
+        expect(st.wHour,      range[0].wHour);
+        expect(st.wMinute,    range[0].wMinute);
+        expect(st.wSecond,    range[0].wSecond);
+        expect(st.wMilliseconds, range[0].wMilliseconds);
+    }
+
+    expect(st.wYear,      range[1].wYear);
+    expect(st.wMonth,     range[1].wMonth);
+    expect(st.wDay,       range[1].wDay);
+    if (!old_comctl32)
+    {
+        expect(st.wDayOfWeek, range[1].wDayOfWeek);
+        expect(st.wHour,      range[1].wHour);
+        expect(st.wMinute,    range[1].wMinute);
+        expect(st.wSecond,    range[1].wSecond);
+        expect(st.wMilliseconds, range[1].wMilliseconds);
+    }
+
+    /* bounds are swapped if min > max */
+    memset(&range[0], 0, sizeof(range[0]));
+    range[0].wYear  = 2009;
+    range[0].wMonth = 10;
+    range[0].wDay   = 5;
+    range[1] = range[0];
+    range[1].wDay   = 3;
+
+    ret = SendMessage(hwnd, MCM_SETSELRANGE, 0, (LPARAM)range);
+    expect(TRUE, ret);
+
+    ret = SendMessage(hwnd, MCM_GETSELRANGE, 0, (LPARAM)range2);
+    expect(TRUE, ret);
+
+    expect(range[1].wYear,      range2[0].wYear);
+    expect(range[1].wMonth,     range2[0].wMonth);
+    expect(range[1].wDay,       range2[0].wDay);
+    expect(6, range2[0].wDayOfWeek);
+    expect(range[1].wHour,      range2[0].wHour);
+    expect(range[1].wMinute,    range2[0].wMinute);
+    expect(range[1].wSecond,    range2[0].wSecond);
+    expect(range[1].wMilliseconds, range2[0].wMilliseconds);
+
+    expect(range[0].wYear,      range2[1].wYear);
+    expect(range[0].wMonth,     range2[1].wMonth);
+    expect(range[0].wDay,       range2[1].wDay);
+    expect(1, range2[1].wDayOfWeek);
+    expect(range[0].wHour,      range2[1].wHour);
+    expect(range[0].wMinute,    range2[1].wMinute);
+    expect(range[0].wSecond,    range2[1].wSecond);
+    expect(range[0].wMilliseconds, range2[1].wMilliseconds);
+
+    /* try with range larger than maximum configured */
+    memset(&range[0], 0, sizeof(range[0]));
+    range[0].wYear  = 2009;
+    range[0].wMonth = 10;
+    range[0].wDay   = 1;
+    range[1] = range[0];
+
+    ret = SendMessage(hwnd, MCM_SETSELRANGE, 0, (LPARAM)range);
+    expect(TRUE, ret);
+
+    range[1] = range[0];
+    /* default max. range is 7 days */
+    range[1].wDay = 8;
+
+    ret = SendMessage(hwnd, MCM_SETSELRANGE, 0, (LPARAM)range);
+    expect(FALSE, ret);
+
+    ret = SendMessage(hwnd, MCM_GETSELRANGE, 0, (LPARAM)range2);
+    expect(TRUE, ret);
+
+    expect(range[0].wYear,  range2[0].wYear);
+    expect(range[0].wMonth, range2[0].wMonth);
+    expect(range[0].wDay,   range2[0].wDay);
+    expect(range[0].wYear,  range2[1].wYear);
+    expect(range[0].wMonth, range2[1].wMonth);
+    expect(range[0].wDay,   range2[1].wDay);
+
+    DestroyWindow(hwnd);
+}
+
+static void test_killfocus(void)
+{
+    HWND hwnd;
+    DWORD style;
+
+    hwnd = create_monthcal_control(0);
+
+    /* make parent invisible */
+    style = GetWindowLong(parent_wnd, GWL_STYLE);
+    SetWindowLong(parent_wnd, GWL_STYLE, style &~ WS_VISIBLE);
+
+    SendMessage(hwnd, WM_KILLFOCUS, (WPARAM)GetDesktopWindow(), 0);
+
+    style = GetWindowLong(hwnd, GWL_STYLE);
+    ok(style & WS_VISIBLE, "Expected WS_VISIBLE to be set\n");
+
+    style = GetWindowLong(parent_wnd, GWL_STYLE);
+    SetWindowLong(parent_wnd, GWL_STYLE, style | WS_VISIBLE);
+
+    DestroyWindow(hwnd);
 }
 
 START_TEST(monthcal)
@@ -1132,7 +1685,6 @@ START_TEST(monthcal)
     HMODULE hComctl32;
     BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*);
     INITCOMMONCONTROLSEX iccex;
-    HWND hwnd, parent_wnd;
 
     hComctl32 = GetModuleHandleA("comctl32.dll");
     pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx");
@@ -1151,39 +1703,21 @@ START_TEST(monthcal)
 
     parent_wnd = create_parent_window();
 
-    flush_sequences(sequences, NUM_MSG_SEQUENCES);
-    hwnd = create_monthcal_control(WS_CHILD | WS_BORDER | WS_VISIBLE, parent_wnd);
-    assert(hwnd);
-    ok_sequence(sequences, PARENT_SEQ_INDEX, create_monthcal_control_seq, "create monthcal control", TRUE);
-
-    SendMessage(hwnd, WM_SETFONT, (WPARAM)GetStockObject(SYSTEM_FONT), 0);
-
-    test_monthcal_color(hwnd);
-    test_monthcal_currDate(hwnd);
-    test_monthcal_firstDay(hwnd);
-    test_monthcal_unicode(hwnd);
-    test_monthcal_today(hwnd);
-    test_monthcal_scroll(hwnd);
-    test_monthcal_monthrange(hwnd);
-    test_monthcal_HitTest(hwnd);
-    test_monthcal_todaylink(hwnd);
-    test_monthcal_size(hwnd);
-
-    flush_sequences(sequences, NUM_MSG_SEQUENCES);
-    DestroyWindow(hwnd);
-    ok_sequence(sequences, PARENT_SEQ_INDEX, destroy_monthcal_parent_msgs_seq, "Destroy monthcal (parent msg)", FALSE);
-    ok_sequence(sequences, MONTHCAL_SEQ_INDEX, destroy_monthcal_child_msgs_seq, "Destroy monthcal (child msg)", FALSE);
-
-    flush_sequences(sequences, NUM_MSG_SEQUENCES);
-    hwnd = create_monthcal_control(MCS_MULTISELECT, parent_wnd);
-    assert(hwnd);
-    ok_sequence(sequences, PARENT_SEQ_INDEX, create_monthcal_multi_sel_style_seq, "create monthcal (multi sel style)", TRUE);
-
-    test_monthcal_MaxSelDay(hwnd);
-
-    flush_sequences(sequences, NUM_MSG_SEQUENCES);
-    DestroyWindow(hwnd);
-    ok_sequence(sequences, MONTHCAL_SEQ_INDEX, destroy_monthcal_multi_sel_style_seq, "Destroy monthcal (multi sel style)", FALSE);
+    test_monthcal_create();
+    test_monthcal_destroy();
+    test_monthcal_color();
+    test_monthcal_currdate();
+    test_monthcal_firstDay();
+    test_monthcal_unicode();
+    test_monthcal_today();
+    test_monthcal_scroll();
+    test_monthcal_monthrange();
+    test_monthcal_hittest();
+    test_monthcal_todaylink();
+    test_monthcal_size();
+    test_monthcal_maxselday();
+    test_monthcal_selrange();
+    test_killfocus();
 
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
     DestroyWindow(parent_wnd);
index 8ebec86..24fcff5 100644 (file)
@@ -69,11 +69,27 @@ static HANDLE (WINAPI *pCreateMRUListA)(LPCREATEMRULISTA);
 static void   (WINAPI *pFreeMRUList)(HANDLE);
 static INT    (WINAPI *pAddMRUStringA)(HANDLE,LPCSTR);
 static INT    (WINAPI *pEnumMRUList)(HANDLE,INT,LPVOID,DWORD);
+static INT    (WINAPI *pEnumMRUListW)(HANDLE,INT,LPVOID,DWORD);
+static HANDLE (WINAPI *pCreateMRUListLazyA)(LPCREATEMRULISTA, DWORD, DWORD, DWORD);
+static INT    (WINAPI *pFindMRUData)(HANDLE, LPCVOID, DWORD, LPINT);
+static INT    (WINAPI *pAddMRUData)(HANDLE, LPCVOID, DWORD);
 /*
 static INT    (WINAPI *pFindMRUStringA)(HANDLE,LPCSTR,LPINT);
 */
 
 
+static void InitPointers(void)
+{
+    pCreateMRUListA = (void*)GetProcAddress(hComctl32,(LPCSTR)151);
+    pFreeMRUList    = (void*)GetProcAddress(hComctl32,(LPCSTR)152);
+    pAddMRUStringA  = (void*)GetProcAddress(hComctl32,(LPCSTR)153);
+    pEnumMRUList    = (void*)GetProcAddress(hComctl32,(LPCSTR)154);
+    pCreateMRUListLazyA = (void*)GetProcAddress(hComctl32,(LPCSTR)157);
+    pAddMRUData     = (void*)GetProcAddress(hComctl32,(LPCSTR)167);
+    pFindMRUData    = (void*)GetProcAddress(hComctl32,(LPCSTR)169);
+    pEnumMRUListW   = (void*)GetProcAddress(hComctl32,(LPCSTR)403);
+}
+
 /* Based on RegDeleteTreeW from dlls/advapi32/registry.c */
 static LSTATUS mru_RegDeleteTreeA(HKEY hKey, LPCSTR lpszSubKey)
 {
@@ -227,11 +243,6 @@ static void test_MRUListA(void)
     HKEY hKey;
     INT iRet;
 
-    pCreateMRUListA = (void*)GetProcAddress(hComctl32,(LPCSTR)151);
-    pFreeMRUList = (void*)GetProcAddress(hComctl32,(LPCSTR)152);
-    pAddMRUStringA = (void*)GetProcAddress(hComctl32,(LPCSTR)153);
-    pEnumMRUList = (void*)GetProcAddress(hComctl32,(LPCSTR)154);
-
     if (!pCreateMRUListA || !pFreeMRUList || !pAddMRUStringA || !pEnumMRUList)
     {
         skip("MRU entry points not found\n");
@@ -374,7 +385,7 @@ static void test_MRUListA(void)
         /* check entry 0 */
         buffer[0] = 0;
         iRet = pEnumMRUList(hMRU, 0, buffer, 255);
-        todo_wine ok(iRet == lstrlen(checks[3]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[3]), iRet);
+        ok(iRet == lstrlen(checks[3]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[3]), iRet);
         ok(strcmp(buffer, checks[3]) == 0, "EnumMRUList expected %s, got %s\n", checks[3], buffer);
 
         /* check entry 0 with a too small buffer */
@@ -383,21 +394,21 @@ static void test_MRUListA(void)
         buffer[2] = 'A'; /* unchanged */
         buffer[3] = 0;   /* unchanged */
         iRet = pEnumMRUList(hMRU, 0, buffer, 2);
-        todo_wine ok(iRet == lstrlen(checks[3]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[3]), iRet);
-        todo_wine ok(strcmp(buffer, "T") == 0, "EnumMRUList expected %s, got %s\n", "T", buffer);
+        ok(iRet == lstrlen(checks[3]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[3]), iRet);
+        ok(strcmp(buffer, "T") == 0, "EnumMRUList expected %s, got %s\n", "T", buffer);
         /* make sure space after buffer has old values */
         ok(buffer[2] == 'A', "EnumMRUList expected %02x, got %02x\n", 'A', buffer[2]);
 
         /* check entry 1 */
         buffer[0] = 0;
         iRet = pEnumMRUList(hMRU, 1, buffer, 255);
-        todo_wine ok(iRet == lstrlen(checks[1]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[1]), iRet);
+        ok(iRet == lstrlen(checks[1]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[1]), iRet);
         ok(strcmp(buffer, checks[1]) == 0, "EnumMRUList expected %s, got %s\n", checks[1], buffer);
 
         /* check entry 2 */
         buffer[0] = 0;
         iRet = pEnumMRUList(hMRU, 2, buffer, 255);
-        todo_wine ok(iRet == lstrlen(checks[2]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[2]), iRet);
+        ok(iRet == lstrlen(checks[2]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[2]), iRet);
         ok(strcmp(buffer, checks[2]) == 0, "EnumMRUList expected %s, got %s\n", checks[2], buffer);
 
         /* check out of bounds entry 3 */
@@ -413,6 +424,89 @@ static void test_MRUListA(void)
     /* FreeMRUList(NULL) crashes on Win98 OSR0 */
 }
 
+static void test_CreateMRUListLazyA(void)
+{
+    HANDLE hMRU;
+    HKEY hKey;
+    CREATEMRULISTA listA = { 0 };
+
+    if (!pCreateMRUListLazyA || !pFreeMRUList)
+    {
+        win_skip("CreateMRUListLazyA or FreeMRUList entry points not found\n");
+        return;
+    }
+
+    /* wrong size */
+    listA.cbSize = sizeof(listA) + 1;
+    hMRU = pCreateMRUListLazyA(&listA, 0, 0, 0);
+    ok(hMRU == NULL, "Expected NULL handle, got %p\n", hMRU);
+    listA.cbSize = 4;
+    hMRU = pCreateMRUListLazyA(&listA, 0, 0, 0);
+    ok(hMRU == NULL, "Expected NULL handle, got %p\n", hMRU);
+    /* NULL hKey */
+    listA.cbSize = sizeof(listA);
+    listA.hKey = NULL;
+    hMRU = pCreateMRUListLazyA(&listA, 0, 0, 0);
+    ok(hMRU == NULL, "Expected NULL handle, got %p\n", hMRU);
+    /* NULL subkey */
+    ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEYA, &hKey),
+       "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);
+    listA.cbSize = sizeof(listA);
+    listA.hKey = hKey;
+    listA.lpszSubKey = NULL;
+    hMRU = pCreateMRUListLazyA(&listA, 0, 0, 0);
+    ok(hMRU == NULL || broken(hMRU != NULL), /* Win9x */
+       "Expected NULL handle, got %p\n", hMRU);
+    if (hMRU) pFreeMRUList(hMRU);
+}
+
+static void test_EnumMRUList(void)
+{
+    if (!pEnumMRUList || !pEnumMRUListW)
+    {
+        win_skip("EnumMRUListA/EnumMRUListW entry point not found\n");
+        return;
+    }
+
+    /* NULL handle */
+    if (0)
+    {
+        /* crashes on NT4, passed on Win2k, XP, 2k3, Vista, 2k8 */
+        pEnumMRUList(NULL, 0, NULL, 0);
+        pEnumMRUListW(NULL, 0, NULL, 0);
+    }
+}
+
+static void test_FindMRUData(void)
+{
+    INT iRet;
+
+    if (!pFindMRUData)
+    {
+        win_skip("FindMRUData entry point not found\n");
+        return;
+    }
+
+    /* NULL handle */
+    iRet = pFindMRUData(NULL, NULL, 0, NULL);
+    ok(iRet == -1, "FindMRUData expected -1, got %d\n", iRet);
+}
+
+static void test_AddMRUData(void)
+{
+    INT iRet;
+
+    if (!pAddMRUData)
+    {
+        win_skip("AddMRUData entry point not found\n");
+        return;
+    }
+
+    /* NULL handle */
+    iRet = pFindMRUData(NULL, NULL, 0, NULL);
+    ok(iRet == -1, "AddMRUData expected -1, got %d\n", iRet);
+}
+
 START_TEST(mru)
 {
     hComctl32 = GetModuleHandleA("comctl32.dll");
@@ -421,7 +515,13 @@ START_TEST(mru)
     if (!create_reg_entries())
         return;
 
+    InitPointers();
+
     test_MRUListA();
+    test_CreateMRUListLazyA();
+    test_EnumMRUList();
+    test_FindMRUData();
+    test_AddMRUData();
 
     delete_reg_entries();
 }
diff --git a/rostests/winetests/comctl32/msg.c b/rostests/winetests/comctl32/msg.c
deleted file mode 100644 (file)
index 2129f22..0000000
+++ /dev/null
@@ -1,251 +0,0 @@
-/* Message Sequence Testing Code
- *
- * Copyright (C) 2007 James Hawkins
- * Copyright (C) 2007 Lei Zhang
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-#include "msg.h"
-
-void add_message(struct msg_sequence **seq, int sequence_index,
-    const struct message *msg)
-{
-    struct msg_sequence *msg_seq = seq[sequence_index];
-
-    if (!msg_seq->sequence)
-    {
-        msg_seq->size = 10;
-        msg_seq->sequence = HeapAlloc(GetProcessHeap(), 0,
-                                      msg_seq->size * sizeof (struct message));
-    }
-
-    if (msg_seq->count == msg_seq->size)
-    {
-        msg_seq->size *= 2;
-        msg_seq->sequence = HeapReAlloc(GetProcessHeap(), 0,
-                                        msg_seq->sequence,
-                                        msg_seq->size * sizeof (struct message));
-    }
-
-    assert(msg_seq->sequence);
-
-    msg_seq->sequence[msg_seq->count].message = msg->message;
-    msg_seq->sequence[msg_seq->count].flags = msg->flags;
-    msg_seq->sequence[msg_seq->count].wParam = msg->wParam;
-    msg_seq->sequence[msg_seq->count].lParam = msg->lParam;
-    msg_seq->sequence[msg_seq->count].id = msg->id;
-
-    msg_seq->count++;
-}
-
-void flush_sequence(struct msg_sequence **seg, int sequence_index)
-{
-    struct msg_sequence *msg_seq = seg[sequence_index];
-    HeapFree(GetProcessHeap(), 0, msg_seq->sequence);
-    msg_seq->sequence = NULL;
-    msg_seq->count = msg_seq->size = 0;
-}
-
-void flush_sequences(struct msg_sequence **seq, int n)
-{
-    int i;
-
-    for (i = 0; i < n; i++)
-        flush_sequence(seq, i);
-}
-
-void ok_sequence_(struct msg_sequence **seq, int sequence_index,
-    const struct message *expected, const char *context, int todo,
-    const char *file, int line)
-{
-    struct msg_sequence *msg_seq = seq[sequence_index];
-    static const struct message end_of_sequence = {0, 0, 0, 0};
-    const struct message *actual, *sequence;
-    int failcount = 0;
-
-    add_message(seq, sequence_index, &end_of_sequence);
-
-    sequence = msg_seq->sequence;
-    actual = sequence;
-
-    while (expected->message && actual->message)
-    {
-        trace_( file, line)("expected %04x - actual %04x\n", expected->message, actual->message);
-
-        if (expected->message == actual->message)
-        {
-            if (expected->flags & wparam)
-            {
-                if (expected->wParam != actual->wParam && todo)
-                {
-                    todo_wine
-                    {
-                        failcount++;
-                        ok_(file, line) (FALSE,
-                            "%s: in msg 0x%04x expecting wParam 0x%lx got 0x%lx\n",
-                            context, expected->message, expected->wParam, actual->wParam);
-                    }
-                }
-                else
-                {
-                    ok_(file, line) (expected->wParam == actual->wParam,
-                        "%s: in msg 0x%04x expecting wParam 0x%lx got 0x%lx\n",
-                        context, expected->message, expected->wParam, actual->wParam);
-                }
-            }
-
-            if (expected->flags & lparam)
-            {
-                if (expected->lParam != actual->lParam && todo)
-                {
-                    todo_wine
-                    {
-                        failcount++;
-                        ok_(file, line) (FALSE,
-                            "%s: in msg 0x%04x expecting lParam 0x%lx got 0x%lx\n",
-                            context, expected->message, expected->lParam, actual->lParam);
-                    }
-                }
-                else
-                {
-                    ok_(file, line) (expected->lParam == actual->lParam,
-                        "%s: in msg 0x%04x expecting lParam 0x%lx got 0x%lx\n",
-                        context, expected->message, expected->lParam, actual->lParam);
-                }
-            }
-
-            if (expected->flags & id)
-            {
-                if (expected->id != actual->id && todo)
-                {
-                    todo_wine
-                    {
-                        failcount++;
-                        ok_(file, line) (FALSE,
-                            "%s: in msg 0x%04x expecting id 0x%x got 0x%x\n",
-                            context, expected->message, expected->id, actual->id);
-                    }
-                }
-                else
-                {
-                    ok_(file, line) (expected->id == actual->id,
-                        "%s: in msg 0x%04x expecting id 0x%x got 0x%x\n",
-                        context, expected->message, expected->id, actual->id);
-                }
-            }
-
-            if ((expected->flags & defwinproc) != (actual->flags & defwinproc) && todo)
-            {
-                todo_wine
-                {
-                    failcount++;
-                    ok_(file, line) (FALSE,
-                        "%s: the msg 0x%04x should %shave been sent by DefWindowProc\n",
-                        context, expected->message, (expected->flags & defwinproc) ? "" : "NOT ");
-                }
-            }
-            else
-            {
-                ok_(file, line) ((expected->flags & defwinproc) == (actual->flags & defwinproc),
-                    "%s: the msg 0x%04x should %shave been sent by DefWindowProc\n",
-                    context, expected->message, (expected->flags & defwinproc) ? "" : "NOT ");
-            }
-
-            ok_(file, line) ((expected->flags & beginpaint) == (actual->flags & beginpaint),
-                "%s: the msg 0x%04x should %shave been sent by BeginPaint\n",
-                context, expected->message, (expected->flags & beginpaint) ? "" : "NOT ");
-            ok_(file, line) ((expected->flags & (sent|posted)) == (actual->flags & (sent|posted)),
-                "%s: the msg 0x%04x should have been %s\n",
-                context, expected->message, (expected->flags & posted) ? "posted" : "sent");
-            ok_(file, line) ((expected->flags & parent) == (actual->flags & parent),
-                "%s: the msg 0x%04x was expected in %s\n",
-                context, expected->message, (expected->flags & parent) ? "parent" : "child");
-            ok_(file, line) ((expected->flags & hook) == (actual->flags & hook),
-                "%s: the msg 0x%04x should have been sent by a hook\n",
-                context, expected->message);
-            ok_(file, line) ((expected->flags & winevent_hook) == (actual->flags & winevent_hook),
-                "%s: the msg 0x%04x should have been sent by a winevent hook\n",
-                context, expected->message);
-            expected++;
-            actual++;
-        }
-        else if (expected->flags & optional)
-            expected++;
-        else if (todo)
-        {
-            failcount++;
-            todo_wine
-            {
-                ok_(file, line) (FALSE, "%s: the msg 0x%04x was expected, but got msg 0x%04x instead\n",
-                    context, expected->message, actual->message);
-            }
-
-            flush_sequence(seq, sequence_index);
-            return;
-        }
-        else
-        {
-            ok_(file, line) (FALSE, "%s: the msg 0x%04x was expected, but got msg 0x%04x instead\n",
-                context, expected->message, actual->message);
-            expected++;
-            actual++;
-        }
-    }
-
-    /* skip all optional trailing messages */
-    while (expected->message && ((expected->flags & optional)))
-        expected++;
-
-    if (todo)
-    {
-        todo_wine
-        {
-            if (expected->message || actual->message)
-            {
-                failcount++;
-                ok_(file, line) (FALSE, "%s: the msg sequence is not complete: expected %04x - actual %04x\n",
-                    context, expected->message, actual->message);
-            }
-        }
-    }
-    else if (expected->message || actual->message)
-    {
-        ok_(file, line) (FALSE, "%s: the msg sequence is not complete: expected %04x - actual %04x\n",
-            context, expected->message, actual->message);
-    }
-
-    if(todo && !failcount) /* succeeded yet marked todo */
-    {
-        todo_wine
-        {
-            ok_(file, line)(TRUE, "%s: marked \"todo_wine\" but succeeds\n", context);
-        }
-    }
-
-    flush_sequence(seq, sequence_index);
-}
-
-void init_msg_sequences(struct msg_sequence **seq, int n)
-{
-    int i;
-
-    for (i = 0; i < n; i++)
-        seq[i] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct msg_sequence));
-}
-
-START_TEST(msg)
-{
-}
index 2ec3ea1..361ccdb 100644 (file)
@@ -47,7 +47,8 @@ struct message
     msg_flags_t flags;  /* message props */
     WPARAM wParam;      /* expected value of wParam */
     LPARAM lParam;      /* expected value of lParam */
-    UINT id;            /* id of the window */
+    UINT id;            /* extra message data: id of the window,
+                           notify code etc. */
 };
 
 struct msg_sequence
@@ -57,17 +58,237 @@ struct msg_sequence
     struct message *sequence;
 };
 
-void add_message(struct msg_sequence **seq, int sequence_index,
-    const struct message *msg);
-void flush_sequence(struct msg_sequence **seg, int sequence_index);
-void flush_sequences(struct msg_sequence **seq, int n);
+static void add_message(struct msg_sequence **seq, int sequence_index,
+    const struct message *msg)
+{
+    struct msg_sequence *msg_seq = seq[sequence_index];
+
+    if (!msg_seq->sequence)
+    {
+        msg_seq->size = 10;
+        msg_seq->sequence = HeapAlloc(GetProcessHeap(), 0,
+                                      msg_seq->size * sizeof (struct message));
+    }
+
+    if (msg_seq->count == msg_seq->size)
+    {
+        msg_seq->size *= 2;
+        msg_seq->sequence = HeapReAlloc(GetProcessHeap(), 0,
+                                        msg_seq->sequence,
+                                        msg_seq->size * sizeof (struct message));
+    }
+
+    assert(msg_seq->sequence);
+
+    msg_seq->sequence[msg_seq->count].message = msg->message;
+    msg_seq->sequence[msg_seq->count].flags = msg->flags;
+    msg_seq->sequence[msg_seq->count].wParam = msg->wParam;
+    msg_seq->sequence[msg_seq->count].lParam = msg->lParam;
+    msg_seq->sequence[msg_seq->count].id = msg->id;
+
+    msg_seq->count++;
+}
+
+static void flush_sequence(struct msg_sequence **seg, int sequence_index)
+{
+    struct msg_sequence *msg_seq = seg[sequence_index];
+    HeapFree(GetProcessHeap(), 0, msg_seq->sequence);
+    msg_seq->sequence = NULL;
+    msg_seq->count = msg_seq->size = 0;
+}
+
+static void flush_sequences(struct msg_sequence **seq, int n)
+{
+    int i;
+
+    for (i = 0; i < n; i++)
+        flush_sequence(seq, i);
+}
+
+static void ok_sequence_(struct msg_sequence **seq, int sequence_index,
+    const struct message *expected, const char *context, int todo,
+    const char *file, int line)
+{
+    struct msg_sequence *msg_seq = seq[sequence_index];
+    static const struct message end_of_sequence = {0, 0, 0, 0};
+    const struct message *actual, *sequence;
+    int failcount = 0;
+
+    add_message(seq, sequence_index, &end_of_sequence);
+
+    sequence = msg_seq->sequence;
+    actual = sequence;
+
+    while (expected->message && actual->message)
+    {
+        trace_( file, line)("expected %04x - actual %04x\n", expected->message, actual->message);
+
+        if (expected->message == actual->message)
+        {
+            if (expected->flags & wparam)
+            {
+                if (expected->wParam != actual->wParam && todo)
+                {
+                    todo_wine
+                    {
+                        failcount++;
+                        ok_(file, line) (FALSE,
+                            "%s: in msg 0x%04x expecting wParam 0x%lx got 0x%lx\n",
+                            context, expected->message, expected->wParam, actual->wParam);
+                    }
+                }
+                else
+                {
+                    ok_(file, line) (expected->wParam == actual->wParam,
+                        "%s: in msg 0x%04x expecting wParam 0x%lx got 0x%lx\n",
+                        context, expected->message, expected->wParam, actual->wParam);
+                }
+            }
+
+            if (expected->flags & lparam)
+            {
+                if (expected->lParam != actual->lParam && todo)
+                {
+                    todo_wine
+                    {
+                        failcount++;
+                        ok_(file, line) (FALSE,
+                            "%s: in msg 0x%04x expecting lParam 0x%lx got 0x%lx\n",
+                            context, expected->message, expected->lParam, actual->lParam);
+                    }
+                }
+                else
+                {
+                    ok_(file, line) (expected->lParam == actual->lParam,
+                        "%s: in msg 0x%04x expecting lParam 0x%lx got 0x%lx\n",
+                        context, expected->message, expected->lParam, actual->lParam);
+                }
+            }
+
+            if (expected->flags & id)
+            {
+                if (expected->id != actual->id && expected->flags & optional)
+                {
+                    expected++;
+                    continue;
+                }
+                if (expected->id != actual->id && todo)
+                {
+                    todo_wine
+                    {
+                        failcount++;
+                        ok_(file, line) (FALSE,
+                            "%s: in msg 0x%04x expecting id 0x%x got 0x%x\n",
+                            context, expected->message, expected->id, actual->id);
+                    }
+                }
+                else
+                {
+                    ok_(file, line) (expected->id == actual->id,
+                        "%s: in msg 0x%04x expecting id 0x%x got 0x%x\n",
+                        context, expected->message, expected->id, actual->id);
+                }
+            }
+
+            if ((expected->flags & defwinproc) != (actual->flags & defwinproc) && todo)
+            {
+                todo_wine
+                {
+                    failcount++;
+                    ok_(file, line) (FALSE,
+                        "%s: the msg 0x%04x should %shave been sent by DefWindowProc\n",
+                        context, expected->message, (expected->flags & defwinproc) ? "" : "NOT ");
+                }
+            }
+            else
+            {
+                ok_(file, line) ((expected->flags & defwinproc) == (actual->flags & defwinproc),
+                    "%s: the msg 0x%04x should %shave been sent by DefWindowProc\n",
+                    context, expected->message, (expected->flags & defwinproc) ? "" : "NOT ");
+            }
+
+            ok_(file, line) ((expected->flags & beginpaint) == (actual->flags & beginpaint),
+                "%s: the msg 0x%04x should %shave been sent by BeginPaint\n",
+                context, expected->message, (expected->flags & beginpaint) ? "" : "NOT ");
+            ok_(file, line) ((expected->flags & (sent|posted)) == (actual->flags & (sent|posted)),
+                "%s: the msg 0x%04x should have been %s\n",
+                context, expected->message, (expected->flags & posted) ? "posted" : "sent");
+            ok_(file, line) ((expected->flags & parent) == (actual->flags & parent),
+                "%s: the msg 0x%04x was expected in %s\n",
+                context, expected->message, (expected->flags & parent) ? "parent" : "child");
+            ok_(file, line) ((expected->flags & hook) == (actual->flags & hook),
+                "%s: the msg 0x%04x should have been sent by a hook\n",
+                context, expected->message);
+            ok_(file, line) ((expected->flags & winevent_hook) == (actual->flags & winevent_hook),
+                "%s: the msg 0x%04x should have been sent by a winevent hook\n",
+                context, expected->message);
+            expected++;
+            actual++;
+        }
+        else if (expected->flags & optional)
+            expected++;
+        else if (todo)
+        {
+            failcount++;
+            todo_wine
+            {
+                ok_(file, line) (FALSE, "%s: the msg 0x%04x was expected, but got msg 0x%04x instead\n",
+                    context, expected->message, actual->message);
+            }
+
+            flush_sequence(seq, sequence_index);
+            return;
+        }
+        else
+        {
+            ok_(file, line) (FALSE, "%s: the msg 0x%04x was expected, but got msg 0x%04x instead\n",
+                context, expected->message, actual->message);
+            expected++;
+            actual++;
+        }
+    }
+
+    /* skip all optional trailing messages */
+    while (expected->message && ((expected->flags & optional)))
+        expected++;
+
+    if (todo)
+    {
+        todo_wine
+        {
+            if (expected->message || actual->message)
+            {
+                failcount++;
+                ok_(file, line) (FALSE, "%s: the msg sequence is not complete: expected %04x - actual %04x\n",
+                    context, expected->message, actual->message);
+            }
+        }
+    }
+    else if (expected->message || actual->message)
+    {
+        ok_(file, line) (FALSE, "%s: the msg sequence is not complete: expected %04x - actual %04x\n",
+            context, expected->message, actual->message);
+    }
+
+    if(todo && !failcount) /* succeeded yet marked todo */
+    {
+        todo_wine
+        {
+            ok_(file, line)(TRUE, "%s: marked \"todo_wine\" but succeeds\n", context);
+        }
+    }
+
+    flush_sequence(seq, sequence_index);
+}
 
 #define ok_sequence(seq, index, exp, contx, todo) \
         ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
 
 
-void ok_sequence_(struct msg_sequence **seq, int sequence_index,
-    const struct message *expected, const char *context, int todo,
-    const char *file, int line);
+static void init_msg_sequences(struct msg_sequence **seq, int n)
+{
+    int i;
 
-void init_msg_sequences(struct msg_sequence **seq, int n);
+    for (i = 0; i < n; i++)
+        seq[i] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct msg_sequence));
+}
index 10bbbef..3083e8b 100644 (file)
@@ -163,6 +163,7 @@ static void cleanup(void)
 static void test_redraw(void)
 {
     RECT client_rect;
+    LRESULT ret;
 
     SendMessageA(hProgressWnd, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
     SendMessageA(hProgressWnd, PBM_SETPOS, 10, 0);
@@ -184,7 +185,11 @@ static void test_redraw(void)
     /* PBM_STEPIT */
     ok(SendMessageA(hProgressWnd, PBM_STEPIT, 0, 0) == 80, "PBM_STEPIT must return the previous position\n");
     ok(!GetUpdateRect(hProgressWnd, NULL, FALSE), "PBM_STEPIT: The progress bar should be redrawn immediately\n");
-    ok((UINT)SendMessageA(hProgressWnd, PBM_GETPOS, 0, 0) == 100, "PBM_GETPOS returned a wrong position\n");
+    ret = SendMessageA(hProgressWnd, PBM_GETPOS, 0, 0);
+    if (ret == 0)
+        win_skip("PBM_GETPOS needs comctl32 > 4.70\n");
+    else
+        ok(ret == 100, "PBM_GETPOS returned a wrong position : %d\n", (UINT)ret);
     
     /* PBM_SETRANGE and PBM_SETRANGE32:
     Usually the progress bar doesn't repaint itself immediately. If the
index 4164f68..bf40db4 100644 (file)
@@ -1,6 +1,7 @@
 /* Unit test suite for property sheet control.
  *
  * Copyright 2006 Huw Davies
+ * Copyright 2009 Jan de Mooij
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 #include <windows.h>
 #include <commctrl.h>
 
+#include "resources.h"
+
 #include "wine/test.h"
 
 static HWND parent;
+static HWND sheethwnd;
+
+static LONG active_page = -1;
+
+#define IDC_APPLY_BUTTON 12321
 
 static int CALLBACK sheet_callback(HWND hwnd, UINT msg, LPARAM lparam)
 {
@@ -33,12 +41,13 @@ static int CALLBACK sheet_callback(HWND hwnd, UINT msg, LPARAM lparam)
         char caption[256];
         GetWindowTextA(hwnd, caption, sizeof(caption));
         ok(!strcmp(caption,"test caption"), "caption: %s\n", caption);
+        sheethwnd = hwnd;
         return 0;
       }
     }
     return 0;
 }
-        
+
 static INT_PTR CALLBACK page_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
                                       LPARAM lparam)
 {
@@ -64,6 +73,10 @@ static INT_PTR CALLBACK page_dlg_proc(HWND hwnd, UINT msg, WPARAM wparam,
             return FALSE;
         }
       }
+    case WM_NCDESTROY:
+        ok(!SendMessageA(sheethwnd, PSM_INDEXTOHWND, 400, 0),"Should always be 0\n");
+        return TRUE;
+
     default:
         return FALSE;
     }
@@ -79,7 +92,7 @@ static void test_title(void)
     memset(&psp, 0, sizeof(psp));
     psp.dwSize = sizeof(psp);
     psp.dwFlags = 0;
-    psp.hInstance = GetModuleHandleW(NULL);
+    psp.hInstance = GetModuleHandleA(NULL);
     U(psp).pszTemplate = "prop_page1";
     U2(psp).pszIcon = NULL;
     psp.pfnDlgProc = page_dlg_proc;
@@ -97,6 +110,12 @@ static void test_title(void)
     psh.pfnCallback = sheet_callback;
 
     hdlg = (HWND)PropertySheetA(&psh);
+    if (hdlg == INVALID_HANDLE_VALUE)
+    {
+        win_skip("comctl32 4.70 needs dwSize adjustment\n");
+        psh.dwSize = sizeof(psh) - sizeof(HBITMAP) - sizeof(HPALETTE) - sizeof(HBITMAP);
+        hdlg = (HWND)PropertySheetA(&psh);
+    }
     DestroyWindow(hdlg);
 }
 
@@ -110,7 +129,7 @@ static void test_nopage(void)
     memset(&psp, 0, sizeof(psp));
     psp.dwSize = sizeof(psp);
     psp.dwFlags = 0;
-    psp.hInstance = GetModuleHandleW(NULL);
+    psp.hInstance = GetModuleHandleA(NULL);
     U(psp).pszTemplate = "prop_page1";
     U2(psp).pszIcon = NULL;
     psp.pfnDlgProc = page_dlg_proc;
@@ -128,6 +147,12 @@ static void test_nopage(void)
     psh.pfnCallback = sheet_callback;
 
     hdlg = (HWND)PropertySheetA(&psh);
+    if (hdlg == INVALID_HANDLE_VALUE)
+    {
+        win_skip("comctl32 4.70 needs dwSize adjustment\n");
+        psh.dwSize = sizeof(psh) - sizeof(HBITMAP) - sizeof(HPALETTE) - sizeof(HBITMAP);
+        hdlg = (HWND)PropertySheetA(&psh);
+    }
     ShowWindow(hdlg,SW_NORMAL);
     SendMessage(hdlg, PSM_REMOVEPAGE, 0, 0);
     RedrawWindow(hdlg,NULL,NULL,RDW_UPDATENOW|RDW_ERASENOW);
@@ -170,6 +195,7 @@ static void test_disableowner(void)
     HPROPSHEETPAGE hpsp[1];
     PROPSHEETPAGEA psp;
     PROPSHEETHEADERA psh;
+    INT_PTR p;
 
     register_parent_wnd_class();
     parent = CreateWindowA("parent class", "", WS_CAPTION | WS_SYSMENU | WS_VISIBLE, 100, 100, 100, 100, GetDesktopWindow(), NULL, GetModuleHandleA(NULL), 0);
@@ -177,7 +203,7 @@ static void test_disableowner(void)
     memset(&psp, 0, sizeof(psp));
     psp.dwSize = sizeof(psp);
     psp.dwFlags = 0;
-    psp.hInstance = GetModuleHandleW(NULL);
+    psp.hInstance = GetModuleHandleA(NULL);
     U(psp).pszTemplate = "prop_page1";
     U2(psp).pszIcon = NULL;
     psp.pfnDlgProc = NULL;
@@ -194,14 +220,227 @@ static void test_disableowner(void)
     U3(psh).phpage = hpsp;
     psh.pfnCallback = disableowner_callback;
 
-    PropertySheetA(&psh);
+    p = PropertySheetA(&psh);
+    todo_wine
+    ok(p == 0, "Expected 0, got %ld\n", p);
     ok(IsWindowEnabled(parent) != 0, "parent window should be enabled\n");
     DestroyWindow(parent);
 }
 
+static INT_PTR CALLBACK nav_page_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+    switch(msg){
+    case WM_NOTIFY:
+        {
+            LPNMHDR hdr = (LPNMHDR)lparam;
+            switch(hdr->code){
+            case PSN_SETACTIVE:
+                active_page = PropSheet_HwndToIndex(hdr->hwndFrom, hwnd);
+                return TRUE;
+            case PSN_KILLACTIVE:
+                /* prevent navigation away from the fourth page */
+                if(active_page == 3){
+                    SetWindowLongPtr(hwnd, DWLP_MSGRESULT, TRUE);
+                    return TRUE;
+                }
+            }
+            break;
+        }
+    }
+    return FALSE;
+}
+
+static void test_wiznavigation(void)
+{
+    HPROPSHEETPAGE hpsp[4];
+    PROPSHEETPAGEA psp[4];
+    PROPSHEETHEADERA psh;
+    HWND hdlg, control;
+    LONG_PTR controlID;
+    LRESULT defidres;
+    BOOL hwndtoindex_supported = TRUE;
+    const INT nextID = 12324;
+    const INT backID = 12323;
+
+    /* create the property sheet pages */
+    memset(psp, 0, sizeof(PROPSHEETPAGEA) * 4);
+
+    psp[0].dwSize = sizeof(PROPSHEETPAGEA);
+    psp[0].hInstance = GetModuleHandleA(NULL);
+    U(psp[0]).pszTemplate = MAKEINTRESOURCE(IDD_PROP_PAGE_INTRO);
+    psp[0].pfnDlgProc = nav_page_proc;
+    hpsp[0] = CreatePropertySheetPageA(&psp[0]);
+
+    psp[1].dwSize = sizeof(PROPSHEETPAGEA);
+    psp[1].hInstance = GetModuleHandleA(NULL);
+    U(psp[1]).pszTemplate = MAKEINTRESOURCE(IDD_PROP_PAGE_EDIT);
+    psp[1].pfnDlgProc = nav_page_proc;
+    hpsp[1] = CreatePropertySheetPageA(&psp[1]);
+
+    psp[2].dwSize = sizeof(PROPSHEETPAGEA);
+    psp[2].hInstance = GetModuleHandleA(NULL);
+    U(psp[2]).pszTemplate = MAKEINTRESOURCE(IDD_PROP_PAGE_RADIO);
+    psp[2].pfnDlgProc = nav_page_proc;
+    hpsp[2] = CreatePropertySheetPageA(&psp[2]);
+
+    psp[3].dwSize = sizeof(PROPSHEETPAGEA);
+    psp[3].hInstance = GetModuleHandleA(NULL);
+    U(psp[3]).pszTemplate = MAKEINTRESOURCE(IDD_PROP_PAGE_EXIT);
+    psp[3].pfnDlgProc = nav_page_proc;
+    hpsp[3] = CreatePropertySheetPageA(&psp[3]);
+
+    /* set up the property sheet dialog */
+    memset(&psh, 0, sizeof(psh));
+    psh.dwSize = sizeof(psh);
+    psh.dwFlags = PSH_MODELESS | PSH_WIZARD;
+    psh.pszCaption = "A Wizard";
+    psh.nPages = 4;
+    psh.hwndParent = GetDesktopWindow();
+    U3(psh).phpage = hpsp;
+    hdlg = (HWND)PropertySheetA(&psh);
+    if (hdlg == INVALID_HANDLE_VALUE)
+    {
+        win_skip("comctl32 4.70 needs dwSize adjustment\n");
+        psh.dwSize = sizeof(psh) - sizeof(HBITMAP) - sizeof(HPALETTE) - sizeof(HBITMAP);
+        hdlg = (HWND)PropertySheetA(&psh);
+    }
+
+    ok(active_page == 0, "Active page should be 0. Is: %d\n", active_page);
+
+    control = GetFocus();
+    controlID = GetWindowLongPtr(control, GWLP_ID);
+    ok(controlID == nextID, "Focus should have been set to the Next button. Expected: %d, Found: %ld\n", nextID, controlID);
+
+    /* simulate pressing the Next button */
+    SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0);
+    if (!active_page) hwndtoindex_supported = FALSE;
+    if (hwndtoindex_supported)
+        ok(active_page == 1, "Active page should be 1 after pressing Next. Is: %d\n", active_page);
+
+    control = GetFocus();
+    controlID = GetWindowLongPtr(control, GWLP_ID);
+    ok(controlID == IDC_PS_EDIT1, "Focus should be set to the first item on the second page. Expected: %d, Found: %ld\n", IDC_PS_EDIT1, controlID);
+
+    defidres = SendMessage(hdlg, DM_GETDEFID, 0, 0);
+    ok(defidres == MAKELRESULT(nextID, DC_HASDEFID), "Expected default button ID to be %d, is %d\n", nextID, LOWORD(defidres));
+
+    /* set the focus to the second edit box on this page */
+    SetFocus(GetNextDlgTabItem(hdlg, control, FALSE));
+
+    /* press next again */
+    SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0);
+    if (hwndtoindex_supported)
+        ok(active_page == 2, "Active page should be 2 after pressing Next. Is: %d\n", active_page);
+
+    control = GetFocus();
+    controlID = GetWindowLongPtr(control, GWLP_ID);
+    ok(controlID == IDC_PS_RADIO1, "Focus should have been set to item on third page. Expected: %d, Found %ld\n", IDC_PS_RADIO1, controlID);
+
+    /* back button */
+    SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_BACK, 0);
+    if (hwndtoindex_supported)
+        ok(active_page == 1, "Active page should be 1 after pressing Back. Is: %d\n", active_page);
+
+    control = GetFocus();
+    controlID = GetWindowLongPtr(control, GWLP_ID);
+    ok(controlID == IDC_PS_EDIT1, "Focus should have been set to the first item on second page. Expected: %d, Found %ld\n", IDC_PS_EDIT1, controlID);
+
+    defidres = SendMessage(hdlg, DM_GETDEFID, 0, 0);
+    ok(defidres == MAKELRESULT(backID, DC_HASDEFID), "Expected default button ID to be %d, is %d\n", backID, LOWORD(defidres));
+
+    /* press next twice */
+    SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0);
+    if (hwndtoindex_supported)
+        ok(active_page == 2, "Active page should be 2 after pressing Next. Is: %d\n", active_page);
+    SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_NEXT, 0);
+    if (hwndtoindex_supported)
+        ok(active_page == 3, "Active page should be 3 after pressing Next. Is: %d\n", active_page);
+    else
+        active_page = 3;
+
+    control = GetFocus();
+    controlID = GetWindowLongPtr(control, GWLP_ID);
+    ok(controlID == nextID, "Focus should have been set to the Next button. Expected: %d, Found: %ld\n", nextID, controlID);
+
+    /* try to navigate away, but shouldn't be able to */
+    SendMessage(hdlg, PSM_PRESSBUTTON, PSBTN_BACK, 0);
+    ok(active_page == 3, "Active page should still be 3 after pressing Back. Is: %d\n", active_page);
+
+    defidres = SendMessage(hdlg, DM_GETDEFID, 0, 0);
+    ok(defidres == MAKELRESULT(nextID, DC_HASDEFID), "Expected default button ID to be %d, is %d\n", nextID, LOWORD(defidres));
+
+    DestroyWindow(hdlg);
+}
+static void test_buttons(void)
+{
+    HPROPSHEETPAGE hpsp[1];
+    PROPSHEETPAGEA psp;
+    PROPSHEETHEADERA psh;
+    HWND hdlg;
+    HWND button;
+    RECT rc;
+    int prevRight, top;
+
+    memset(&psp, 0, sizeof(psp));
+    psp.dwSize = sizeof(psp);
+    psp.dwFlags = 0;
+    psp.hInstance = GetModuleHandleA(NULL);
+    U(psp).pszTemplate = "prop_page1";
+    U2(psp).pszIcon = NULL;
+    psp.pfnDlgProc = page_dlg_proc;
+    psp.lParam = 0;
+
+    hpsp[0] = CreatePropertySheetPageA(&psp);
+
+    memset(&psh, 0, sizeof(psh));
+    psh.dwSize = sizeof(psh);
+    psh.dwFlags = PSH_MODELESS | PSH_USECALLBACK;
+    psh.pszCaption = "test caption";
+    psh.nPages = 1;
+    psh.hwndParent = GetDesktopWindow();
+    U3(psh).phpage = hpsp;
+    psh.pfnCallback = sheet_callback;
+
+    hdlg = (HWND)PropertySheetA(&psh);
+    if (hdlg == INVALID_HANDLE_VALUE)
+    {
+        win_skip("comctl32 4.70 needs dwSize adjustment\n");
+        psh.dwSize = sizeof(psh) - sizeof(HBITMAP) - sizeof(HPALETTE) - sizeof(HBITMAP);
+        hdlg = (HWND)PropertySheetA(&psh);
+    }
+
+    /* OK button */
+    button = GetDlgItem(hdlg, IDOK);
+    GetWindowRect(button, &rc);
+    prevRight = rc.right;
+    top = rc.top;
+
+    /* Cancel button */
+    button = GetDlgItem(hdlg, IDCANCEL);
+    GetWindowRect(button, &rc);
+    ok(rc.top == top, "Cancel button should have same top as OK button\n");
+    ok(rc.left > prevRight, "Cancel button should be to the right of OK button\n");
+    prevRight = rc.right;
+
+    button = GetDlgItem(hdlg, IDC_APPLY_BUTTON);
+    GetWindowRect(button, &rc);
+    ok(rc.top == top, "Apply button should have same top as OK button\n");
+    ok(rc.left > prevRight, "Apply button should be to the right of Cancel button\n");
+    prevRight = rc.right;
+
+    button = GetDlgItem(hdlg, IDHELP);
+    GetWindowRect(button, &rc);
+    ok(rc.top == top, "Help button should have same top as OK button\n");
+    ok(rc.left > prevRight, "Help button should be to the right of Apply button\n");
+
+    DestroyWindow(hdlg);
+}
+
 START_TEST(propsheet)
 {
     test_title();
     test_nopage();
     test_disableowner();
+    test_wiznavigation();
+    test_buttons();
 }
index d055d8a..a75a064 100644 (file)
@@ -32,7 +32,6 @@
 
 RECT height_change_notify_rect;
 static HWND hMainWnd;
-static HWND hRebar;
 
 
 #define check_rect(name, val, exp) ok(val.top == exp.top && val.bottom == exp.bottom && \
@@ -66,14 +65,17 @@ static BOOL is_font_installed(const char *name)
     return ret;
 }
 
-static void rebuild_rebar(HWND *hRebar)
+static HWND create_rebar_control(void)
 {
-    if (*hRebar)
-        DestroyWindow(*hRebar);
+    HWND hwnd;
 
-    *hRebar = CreateWindow(REBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0,
+    hwnd = CreateWindow(REBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0,
         hMainWnd, (HMENU)17, GetModuleHandle(NULL), NULL);
-    SendMessageA(*hRebar, WM_SETFONT, (WPARAM)GetStockObject(SYSTEM_FONT), 0);
+    ok(hwnd != NULL, "Failed to create Rebar\n");
+
+    SendMessageA(hwnd, WM_SETFONT, (WPARAM)GetStockObject(SYSTEM_FONT), 0);
+
+    return hwnd;
 }
 
 static HWND build_toolbar(int nr, HWND hParent)
@@ -85,7 +87,7 @@ static HWND build_toolbar(int nr, HWND hParent)
     int i;
 
     ok(hToolbar != NULL, "Toolbar creation problem\n");
-    ok(SendMessage(hToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0) == 0, "TB_BUTTONSTRUCTSIZE failed\n");
+    ok(SendMessage(hToolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0) == 0, "TB_BUTTONSTRUCTSIZE failed\n");
     ok(SendMessage(hToolbar, TB_AUTOSIZE, 0, 0) == 0, "TB_AUTOSIZE failed\n");
     ok(SendMessage(hToolbar, WM_SETFONT, (WPARAM)GetStockObject(SYSTEM_FONT), 0)==1, "WM_SETFONT\n");
 
@@