[COMCTL32_WINETEST] Sync with Wine Staging 1.7.47. CORE-9924
authorAmine Khaldi <amine.khaldi@reactos.org>
Wed, 22 Jul 2015 19:30:52 +0000 (19:30 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Wed, 22 Jul 2015 19:30:52 +0000 (19:30 +0000)
svn path=/trunk/; revision=68554

rostests/winetests/comctl32/button.c
rostests/winetests/comctl32/listview.c
rostests/winetests/comctl32/monthcal.c
rostests/winetests/comctl32/propsheet.c
rostests/winetests/comctl32/toolbar.c
rostests/winetests/comctl32/tooltips.c
rostests/winetests/comctl32/treeview.c

index f368bfc..b56b1af 100644 (file)
@@ -321,6 +321,7 @@ static const struct message setstyle_seq[] =
     { WM_PAINT, sent },
     { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */
     { WM_ERASEBKGND, sent|defwinproc|optional },
+    { WM_PAINT, sent|optional },
     { 0 }
 };
 
@@ -368,6 +369,7 @@ static const struct message setstate_seq[] =
     { WM_PAINT, sent },
     { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
     { WM_ERASEBKGND, sent|defwinproc|optional },
+    { WM_PAINT, sent|optional },
     { 0 }
 };
 
@@ -429,6 +431,7 @@ static const struct message setcheck_ignored_seq[] =
 {
     { BM_SETCHECK, sent },
     { WM_APP, sent|wparam|lparam, 0, 0 },
+    { WM_PAINT, sent|optional },
     { 0 }
 };
 
index 468da56..9387fcb 100644 (file)
@@ -179,6 +179,16 @@ static const struct message listview_ownerdata_switchto_seq[] = {
 static const struct message listview_getorderarray_seq[] = {
     { LVM_GETCOLUMNORDERARRAY, sent|id|wparam, 2, 0, LISTVIEW_ID },
     { HDM_GETORDERARRAY,       sent|id|wparam, 2, 0, HEADER_ID },
+    { LVM_GETCOLUMNORDERARRAY, sent|id|wparam, 0, 0, LISTVIEW_ID },
+    { HDM_GETORDERARRAY,       sent|id|wparam, 0, 0, HEADER_ID },
+    { 0 }
+};
+
+static const struct message listview_setorderarray_seq[] = {
+    { LVM_SETCOLUMNORDERARRAY, sent|id|wparam, 2, 0, LISTVIEW_ID },
+    { HDM_SETORDERARRAY,       sent|id|wparam, 2, 0, HEADER_ID },
+    { LVM_SETCOLUMNORDERARRAY, sent|id|wparam, 0, 0, LISTVIEW_ID },
+    { HDM_SETORDERARRAY,       sent|id|wparam, 0, 0, HEADER_ID },
     { 0 }
 };
 
@@ -1416,13 +1426,28 @@ static void test_items(void)
 
 static void test_columns(void)
 {
-    HWND hwnd;
+    HWND hwnd, header;
     LVCOLUMNA column;
     LVITEMA item;
     INT order[2];
     CHAR buff[5];
     DWORD rc;
 
+    hwnd = CreateWindowExA(0, "SysListView32", "foo", LVS_LIST,
+                10, 10, 100, 200, hwndparent, NULL, NULL, NULL);
+    ok(hwnd != NULL, "failed to create listview window\n");
+
+    header = (HWND)SendMessageA(hwnd, LVM_GETHEADER, 0, 0);
+    ok(header == NULL, "got %p\n", header);
+
+    rc = SendMessageA(hwnd, LVM_GETCOLUMNORDERARRAY, 2, (LPARAM)&order);
+    ok(rc == 0, "got %d\n", rc);
+
+    header = (HWND)SendMessageA(hwnd, LVM_GETHEADER, 0, 0);
+    ok(header == NULL, "got %p\n", header);
+
+    DestroyWindow(hwnd);
+
     hwnd = CreateWindowExA(0, "SysListView32", "foo", LVS_REPORT,
                 10, 10, 100, 200, hwndparent, NULL, NULL, NULL);
     ok(hwnd != NULL, "failed to create listview window\n");
@@ -1460,8 +1485,24 @@ static void test_columns(void)
     ok(order[0] == 0, "Expected order 0, got %d\n", order[0]);
     ok(order[1] == 1, "Expected order 1, got %d\n", order[1]);
 
+    rc = SendMessageA(hwnd, LVM_GETCOLUMNORDERARRAY, 0, 0);
+    expect(0, rc);
+
     ok_sequence(sequences, LISTVIEW_SEQ_INDEX, listview_getorderarray_seq, "get order array", FALSE);
 
+    /* LVM_SETCOLUMNORDERARRAY */
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+
+    order[0] = 0;
+    order[1] = 1;
+    rc = SendMessageA(hwnd, LVM_SETCOLUMNORDERARRAY, 2, (LPARAM)&order);
+    expect(1, rc);
+
+    rc = SendMessageA(hwnd, LVM_SETCOLUMNORDERARRAY, 0, 0);
+    expect(0, rc);
+
+    ok_sequence(sequences, LISTVIEW_SEQ_INDEX, listview_setorderarray_seq, "set order array", FALSE);
+
     /* after column added subitem is considered as present */
     insert_item(hwnd, 0);
 
@@ -3877,7 +3918,6 @@ static void test_getitemrect(void)
     LVCOLUMNA col;
     INT order[2];
     POINT pt;
-    HDC hdc;
 
     /* rectangle isn't empty for empty text items */
     hwnd = create_listview_control(LVS_LIST);
@@ -3891,9 +3931,9 @@ static void test_getitemrect(void)
     expect(TRUE, r);
     expect(0, rect.left);
     expect(0, rect.top);
-    hdc = GetDC(hwnd);
-    todo_wine expect(((GetDeviceCaps(hdc, LOGPIXELSX) + 15) / 16) * 16, rect.right);
-    ReleaseDC(hwnd, hdc);
+    /* estimate it as width / height ratio */
+todo_wine
+    ok((rect.right / rect.bottom) >= 5, "got right %d, bottom %d\n", rect.right, rect.bottom);
     DestroyWindow(hwnd);
 
     hwnd = create_listview_control(LVS_REPORT);
@@ -4557,6 +4597,12 @@ static void test_get_set_view(void)
     style = GetWindowLongPtrA(hwnd, GWL_STYLE);
     ok(style & LVS_LIST, "Expected style to be preserved\n");
 
+    /* now change window style to see if view is remapped */
+    style = GetWindowLongPtrA(hwnd, GWL_STYLE);
+    SetWindowLongPtrA(hwnd, GWL_STYLE, style | LVS_SHOWSELALWAYS);
+    ret = SendMessageA(hwnd, LVM_GETVIEW, 0, 0);
+    expect(LV_VIEW_SMALLICON, ret);
+
     DestroyWindow(hwnd);
 }
 
@@ -4789,6 +4835,36 @@ static void test_getitemspacing(void)
     DestroyWindow(hwnd);
 }
 
+static INT get_current_font_height(HWND listview)
+{
+    TEXTMETRICA tm;
+    HFONT hfont;
+    HWND hwnd;
+    HDC hdc;
+
+    hwnd = (HWND)SendMessageA(listview, LVM_GETHEADER, 0, 0);
+    if (!hwnd)
+        hwnd = listview;
+
+    hfont = (HFONT)SendMessageA(hwnd, WM_GETFONT, 0, 0);
+    if (!hfont) {
+        hdc = GetDC(hwnd);
+        GetTextMetricsA(hdc, &tm);
+        ReleaseDC(hwnd, hdc);
+    }
+    else {
+        HFONT oldfont;
+
+        hdc = GetDC(0);
+        oldfont = SelectObject(hdc, hfont);
+        GetTextMetricsA(hdc, &tm);
+        SelectObject(hdc, oldfont);
+        ReleaseDC(0, hdc);
+    }
+
+    return tm.tmHeight;
+}
+
 static void test_getcolumnwidth(void)
 {
     HWND hwnd;
@@ -4796,7 +4872,7 @@ static void test_getcolumnwidth(void)
     DWORD_PTR style;
     LVCOLUMNA col;
     LVITEMA itema;
-    HDC hdc;
+    INT height;
 
     /* default column width */
     hwnd = create_listview_control(LVS_ICON);
@@ -4820,9 +4896,8 @@ static void test_getcolumnwidth(void)
     memset(&itema, 0, sizeof(itema));
     SendMessageA(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&itema);
     ret = SendMessageA(hwnd, LVM_GETCOLUMNWIDTH, 0, 0);
-    hdc = GetDC(hwnd);
-    todo_wine expect(((GetDeviceCaps(hdc, LOGPIXELSX) + 15) / 16) * 16, ret);
-    ReleaseDC(hwnd, hdc);
+    height = get_current_font_height(hwnd);
+    ok((ret / height) >= 6, "got width %d, height %d\n", ret, height);
     DestroyWindow(hwnd);
 }
 
@@ -5106,10 +5181,18 @@ static void test_LVS_EX_HEADERINALLVIEWS(void)
 
 static void test_hover(void)
 {
-    HWND hwnd;
+    HWND hwnd, fg;
     DWORD r;
 
     hwnd = create_listview_control(LVS_ICON);
+    SetForegroundWindow(hwndparent);
+    fg = GetForegroundWindow();
+    if (fg != hwndparent)
+    {
+        skip("Window is not in the foreground. Skipping hover tests.\n");
+        DestroyWindow(hwnd);
+        return;
+    }
 
     /* test WM_MOUSEHOVER forwarding */
     flush_sequences(sequences, NUM_MSG_SEQUENCES);
@@ -5641,6 +5724,30 @@ static void test_insertitem(void)
     DestroyWindow(hwnd);
 }
 
+static void test_header_proc(void)
+{
+    HWND hwnd, header, hdr;
+    WNDPROC proc1, proc2;
+
+    hwnd = create_listview_control(LVS_REPORT);
+
+    header = (HWND)SendMessageA(hwnd, LVM_GETHEADER, 0, 0);
+    ok(header != NULL, "got %p\n", header);
+
+    hdr = CreateWindowExA(0, WC_HEADERA, NULL,
+                            WS_BORDER|WS_VISIBLE|HDS_BUTTONS|HDS_HORZ,
+                            0, 0, 0, 0,
+                            NULL, NULL, NULL, NULL);
+    ok(hdr != NULL, "got %p\n", hdr);
+
+    proc1 = (WNDPROC)GetWindowLongPtrW(header, GWLP_WNDPROC);
+    proc2 = (WNDPROC)GetWindowLongPtrW(hdr, GWLP_WNDPROC);
+    ok(proc1 == proc2, "got %p, expected %p\n", proc1, proc2);
+
+    DestroyWindow(hdr);
+    DestroyWindow(hwnd);
+}
+
 START_TEST(listview)
 {
     HMODULE hComctl32;
@@ -5709,6 +5816,7 @@ START_TEST(listview)
     test_imagelists();
     test_deleteitem();
     test_insertitem();
+    test_header_proc();
 
     if (!load_v6_module(&ctx_cookie, &hCtx))
     {
@@ -5726,6 +5834,7 @@ START_TEST(listview)
     test_deleteitem();
     test_multiselect();
     test_insertitem();
+    test_header_proc();
 
     unload_v6_module(ctx_cookie, hCtx);
 
index 466612b..4653f52 100644 (file)
@@ -615,7 +615,9 @@ static LRESULT WINAPI monthcal_subclass_proc(HWND hwnd, UINT message, WPARAM wPa
 static HWND create_monthcal_control(DWORD style)
 {
     WNDPROC oldproc;
+    RECT rect;
     HWND hwnd;
+    BOOL ret;
 
     hwnd = CreateWindowExA(0, MONTHCAL_CLASSA, "", WS_CHILD | WS_BORDER | WS_VISIBLE | style,
                     0, 0, 300, 400, parent_wnd, NULL, GetModuleHandleA(NULL), NULL);
@@ -628,6 +630,13 @@ static HWND create_monthcal_control(DWORD style)
 
     SendMessageA(hwnd, WM_SETFONT, (WPARAM)GetStockObject(SYSTEM_FONT), 0);
 
+    /* make sure calendar grid is 2x1 */
+    ret = SendMessageA(hwnd, MCM_GETMINREQRECT, 0, (LPARAM)&rect);
+    ok(ret, "got %d\n", ret);
+
+    ret = SetWindowPos(hwnd, NULL, 0, 0, rect.right * 5 / 2, rect.bottom * 3 / 2, SWP_NOMOVE);
+    ok(ret, "got %d\n", ret);
+
     return hwnd;
 }
 
index 93a75d9..0ba78d3 100644 (file)
@@ -877,6 +877,7 @@ if (0)
     r = SendMessageA(tab, TCM_GETITEMCOUNT, 0, 0);
     ok(r == 3, "got %d\n", r);
 
+    DestroyPropertySheetPage(hpsp[4]);
     DestroyWindow(hdlg);
 }
 
index da21924..23eb250 100644 (file)
@@ -54,6 +54,40 @@ static const struct message ttgetdispinfo_parent_seq[] = {
     { 0 }
 };
 
+static const struct message save_parent_seq[] = {
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_SAVE, -1 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_SAVE, 0 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_SAVE, 1 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_SAVE, 2 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_SAVE, 3 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_SAVE, 4 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_SAVE, 5 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_SAVE, 6 },
+    { 0 }
+};
+
+static const struct message restore_parent_seq[] = {
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_RESTORE, -1 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_RESTORE, 0 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_RESTORE, 1 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_RESTORE, 2 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_RESTORE, 3 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_RESTORE, 4 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_RESTORE, 5 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_RESTORE, 6 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_RESTORE, 7 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_RESTORE, 8 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_RESTORE, 9 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_RESTORE, 0xa },
+    { WM_NOTIFY, sent|id, 0, 0, TBN_BEGINADJUST },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_GETBUTTONINFOA, 0 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_GETBUTTONINFOA, 1 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_GETBUTTONINFOA, 2 },
+    { WM_NOTIFY, sent|id|custdraw, 0, 0, TBN_GETBUTTONINFOA, 3 },
+    { WM_NOTIFY, sent|id, 0, 0, TBN_ENDADJUST },
+    { 0 }
+};
+
 #define DEFINE_EXPECT(func) \
     static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
 
@@ -93,6 +127,8 @@ static void MakeButton(TBBUTTON *p, int idCommand, int fsStyle, int nString) {
   p->iString = nString;
 }
 
+static void *alloced_str;
+
 static LRESULT parent_wnd_notify(LPARAM lParam)
 {
     NMHDR *hdr = (NMHDR *)lParam;
@@ -133,6 +169,135 @@ static LRESULT parent_wnd_notify(LPARAM lParam)
             compare(nmdisp->dwMask, g_dwExpectedDispInfoMask, "%x");
             ok(nmdisp->pszText == NULL, "pszText is not NULL\n");
         break;
+        case TBN_SAVE:
+        {
+            NMTBSAVE *save = (NMTBSAVE *)lParam;
+            if (save->iItem == -1)
+            {
+                save->cbData = save->cbData * 2 + 11 * sizeof(DWORD);
+                save->pData = HeapAlloc( GetProcessHeap(), 0, save->cbData );
+                save->pData[0] = 0xcafe;
+                save->pCurrent = save->pData + 1;
+            }
+            else
+            {
+                save->pCurrent[0] = 0xcafe0000 + save->iItem;
+                save->pCurrent++;
+            }
+
+            /* Add on 5 more pseudo buttons. */
+            if (save->iItem == save->cButtons - 1)
+            {
+                save->pCurrent[0] = 0xffffffff;
+                save->pCurrent[1] = 0xcafe0007;
+                save->pCurrent[2] = 0xfffffffe;
+                save->pCurrent[3] = 0xcafe0008;
+                save->pCurrent[4] = 0x80000000;
+                save->pCurrent[5] = 0xcafe0009;
+                save->pCurrent[6] = 0x7fffffff;
+                save->pCurrent[7] = 0xcafe000a;
+                save->pCurrent[8] = 0x100;
+                save->pCurrent[9] = 0xcafe000b;
+            }
+
+            /* Return value is ignored */
+            return 1;
+        }
+        case TBN_RESTORE:
+        {
+            NMTBRESTORE *restore = (NMTBRESTORE *)lParam;
+
+            if (restore->iItem == -1)
+            {
+                ok( restore->cButtons == 25, "got %d\n", restore->cButtons );
+                ok( *restore->pCurrent == 0xcafe, "got %08x\n", *restore->pCurrent );
+                /* Skip the last one */
+                restore->cButtons = 11;
+                restore->pCurrent++;
+                /* BytesPerRecord is ignored */
+                restore->cbBytesPerRecord = 10;
+            }
+            else
+            {
+                ok( *restore->pCurrent == 0xcafe0000 + restore->iItem, "got %08x\n", *restore->pCurrent );
+                if (restore->iItem < 7 || restore->iItem == 10)
+                {
+                    ok( restore->tbButton.iBitmap == -1, "got %08x\n", restore->tbButton.iBitmap );
+                    if (restore->iItem < 7)
+                        ok( restore->tbButton.idCommand == restore->iItem * 2 + 1, "%d: got %08x\n", restore->iItem, restore->tbButton.idCommand );
+                    else
+                        ok( restore->tbButton.idCommand == 0x7fffffff, "%d: got %08x\n", restore->iItem, restore->tbButton.idCommand );
+                    ok( restore->tbButton.fsState == 0, "%d: got %02x\n", restore->iItem, restore->tbButton.fsState );
+                    ok( restore->tbButton.fsStyle == 0, "%d: got %02x\n", restore->iItem, restore->tbButton.fsStyle );
+                }
+                else
+                {
+                    ok( restore->tbButton.iBitmap == 8, "got %08x\n", restore->tbButton.iBitmap );
+                    ok( restore->tbButton.idCommand == 0, "%d: got %08x\n", restore->iItem, restore->tbButton.idCommand );
+                    if (restore->iItem == 7)
+                        ok( restore->tbButton.fsState == 0, "%d: got %02x\n", restore->iItem, restore->tbButton.fsState );
+                    else
+                        ok( restore->tbButton.fsState == TBSTATE_HIDDEN, "%d: got %02x\n", restore->iItem, restore->tbButton.fsState );
+                    ok( restore->tbButton.fsStyle == BTNS_SEP, "%d: got %02x\n", restore->iItem, restore->tbButton.fsStyle );
+                }
+
+                ok( restore->tbButton.dwData == 0, "got %08lx\n", restore->tbButton.dwData );
+                ok( restore->tbButton.iString == 0, "got %08lx\n", restore->tbButton.iString );
+
+                restore->tbButton.iBitmap = 0;
+                restore->tbButton.fsState = TBSTATE_ENABLED;
+                restore->tbButton.fsStyle = 0;
+                restore->tbButton.dwData = restore->iItem;
+
+                if (restore->iItem == 0)
+                {
+                    restore->tbButton.iString = (INT_PTR)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 8 );
+                    strcpy( (char *)restore->tbButton.iString, "foo" );
+                }
+                else if (restore->iItem == 1)
+                    restore->tbButton.iString = 2;
+                else
+                    restore->tbButton.iString = -1;
+
+                restore->pCurrent++;
+                /* Altering cButtons after the 1st call makes no difference. */
+                restore->cButtons--;
+            }
+
+            /* Returning non-zero from the 1st call aborts the restore,
+               otherwise the return value is ignored. */
+            if (restore->iItem == -1) return 0;
+            return 1;
+        }
+        case TBN_GETBUTTONINFOA:
+        {
+            NMTOOLBARA *tb = (NMTOOLBARA *)lParam;
+            tb->tbButton.iBitmap = 0;
+            tb->tbButton.fsState = 0;
+            tb->tbButton.fsStyle = 0;
+            tb->tbButton.dwData = 0;
+            ok( tb->cchText == 128, "got %d\n", tb->cchText );
+            switch (tb->iItem)
+            {
+            case 0:
+                tb->tbButton.idCommand = 7;
+                alloced_str = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 8 );
+                strcpy( alloced_str, "foo" );
+                tb->tbButton.iString = (INT_PTR)alloced_str;
+                return 1;
+            case 1:
+                tb->tbButton.idCommand = 9;
+                tb->tbButton.iString = 0;
+                /* tb->pszText is ignored */
+                strcpy( tb->pszText, "foo" );
+                return 1;
+           case 2:
+                tb->tbButton.idCommand = 11;
+                tb->tbButton.iString = 3;
+                return 1;
+            }
+            return 0;
+        }
     }
     return 0;
 }
@@ -148,7 +313,31 @@ static LRESULT CALLBACK parent_wnd_proc(HWND hWnd, UINT message, WPARAM wParam,
     if (defwndproc_counter) msg.flags |= defwinproc;
     msg.wParam = wParam;
     msg.lParam = lParam;
-    if (message == WM_NOTIFY && lParam) msg.id = ((NMHDR*)lParam)->code;
+    if (message == WM_NOTIFY && lParam)
+    {
+        msg.id = ((NMHDR*)lParam)->code;
+        switch (msg.id)
+        {
+        case TBN_SAVE:
+        {
+            NMTBSAVE *save = (NMTBSAVE *)lParam;
+            msg.stage = save->iItem;
+        }
+        break;
+        case TBN_RESTORE:
+        {
+            NMTBRESTORE *restore = (NMTBRESTORE *)lParam;
+            msg.stage = restore->iItem;
+        }
+        break;
+        case TBN_GETBUTTONINFOA:
+        {
+            NMTOOLBARA *tb = (NMTOOLBARA *)lParam;
+            msg.stage = tb->iItem;
+        }
+        break;
+        }
+    }
 
     /* log system messages, except for painting */
     if (message < WM_USER &&
@@ -748,7 +937,7 @@ static void tbsize_addbutton(tbsize_result_t *tbsr, int left, int top, int right
 
 static tbsize_result_t *tbsize_results;
 
-#define tbsize_results_num 24
+#define tbsize_results_num 28
 
 static void init_tbsize_results(void) {
     int fontheight = system_font_height();
@@ -991,6 +1180,18 @@ static void init_tbsize_results(void) {
     tbsize_results[23] = init_tbsize_result(2, 0, 0, 672, 42, 67, 41);
     tbsize_addbutton(&tbsize_results[23],   0,   2, 672,  25 + fontheight);
     tbsize_addbutton(&tbsize_results[23],   0,  25 + fontheight, 672,  48 + 2*fontheight);
+
+    tbsize_results[24] = init_tbsize_result(1, 0, 0, 672, 42, 67, 40);
+    tbsize_addbutton(&tbsize_results[24],   0,   2,  11 + string_width(STRING2),  24);
+
+    tbsize_results[25] = init_tbsize_result(1, 0, 0, 672, 42, 67, 40);
+    tbsize_addbutton(&tbsize_results[25],   0,   2,  40,  24);
+
+    tbsize_results[26] = init_tbsize_result(1, 0, 0, 672, 42, 67, 40);
+    tbsize_addbutton(&tbsize_results[26],   0,   2,  40,  24);
+
+    tbsize_results[27] = init_tbsize_result(1, 0, 0, 672, 42, 67, 40);
+    tbsize_addbutton(&tbsize_results[27],   0,   2,  40,  24);
 }
 
 static void free_tbsize_results(void) {
@@ -1062,12 +1263,18 @@ static TBBUTTON buttons3[] = {
     {0, 32, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0, }, 0, 1},
     {0, 33, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0, }, 0, (UINT_PTR)STRING2}
 };
+static TBBUTTON buttons4[] = {
+    {0, 40, TBSTATE_ENABLED, BTNS_AUTOSIZE, {0, }, 0, (UINT_PTR)STRING2},
+    {0, 41, TBSTATE_ENABLED, 0, {0, }, 0, (UINT_PTR)STRING2},
+    {0, 41, TBSTATE_ENABLED, BTNS_SHOWTEXT, {0, }, 0, (UINT_PTR)STRING2}
+};
 
 static void test_sizes(void)
 {
     HWND hToolbar = NULL;
     HIMAGELIST himl, himl2;
     TBBUTTONINFOA tbinfo;
+    TBBUTTON button;
     int style;
     int i;
     int fontheight = system_font_height();
@@ -1083,9 +1290,13 @@ static void test_sizes(void)
     check_sizes();
     SendMessageA(hToolbar, TB_AUTOSIZE, 0, 0);
     check_sizes();
+    SendMessageA(hToolbar, TB_GETBUTTON, 5, (LPARAM)&button);
+    ok(button.fsState == (TBSTATE_WRAP|TBSTATE_ENABLED), "got %08x\n", button.fsState);
     /* after setting the TBSTYLE_WRAPABLE the TBSTATE_WRAP is ignored */
     SetWindowLongA(hToolbar, GWL_STYLE, style|TBSTYLE_WRAPABLE);
     check_sizes();
+    SendMessageA(hToolbar, TB_GETBUTTON, 5, (LPARAM)&button);
+    ok(button.fsState == TBSTATE_ENABLED, "got %08x\n", button.fsState);
     /* adding new buttons with TBSTYLE_WRAPABLE doesn't add a new row */
     SendMessageA(hToolbar, TB_ADDBUTTONSA, 2, (LPARAM)buttons1);
     check_sizes();
@@ -1094,6 +1305,11 @@ static void test_sizes(void)
     for (i=0; i<15; i++)
         SendMessageA(hToolbar, TB_ADDBUTTONSA, 2, (LPARAM)buttons1);
     check_sizes_todo(0x4);
+    SendMessageA(hToolbar, TB_GETBUTTON, 31, (LPARAM)&button);
+    ok(button.fsState == (TBSTATE_WRAP|TBSTATE_ENABLED), "got %08x\n", button.fsState);
+    SetWindowLongA(hToolbar, GWL_STYLE, style);
+    SendMessageA(hToolbar, TB_GETBUTTON, 31, (LPARAM)&button);
+    ok(button.fsState == TBSTATE_ENABLED, "got %08x\n", button.fsState);
 
     rebuild_toolbar_with_buttons(&hToolbar);
     SendMessageA(hToolbar, TB_ADDBUTTONSA, 2, (LPARAM)buttons1);
@@ -1300,8 +1516,41 @@ static void test_sizes(void)
     {
         tbinfo.dwMask = TBIF_SIZE;
         ok(SendMessageA(hToolbar, TB_SETBUTTONINFOA, 33, (LPARAM)&tbinfo) != 0, "TB_SETBUTTONINFOA failed\n");
+        tbsize_numtests++;
     }
 
+    /* Single BTNS_AUTOSIZE button with string. */
+    rebuild_toolbar(&hToolbar);
+    ok(SendMessageA(hToolbar, TB_ADDBUTTONSA, 1, (LPARAM)&buttons4[0]) == 1, "TB_ADDBUTTONSA failed\n");
+    ok(SendMessageA(hToolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(40, 20)) == 1, "TB_SETBUTTONSIZE failed\n");
+    SendMessageA(hToolbar, TB_AUTOSIZE, 0, 0 );
+    check_sizes();
+
+    /* Single non-BTNS_AUTOSIZE button with string. */
+    rebuild_toolbar(&hToolbar);
+    ok(SendMessageA(hToolbar, TB_ADDBUTTONSA, 1, (LPARAM)&buttons4[1]) == 1, "TB_ADDBUTTONSA failed\n");
+    ok(SendMessageA(hToolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(40, 20)) == 1, "TB_SETBUTTONSIZE failed\n");
+    SendMessageA(hToolbar, TB_AUTOSIZE, 0, 0 );
+    check_sizes();
+
+    /* Single non-BTNS_AUTOSIZE button with string with TBSTYLE_EX_MIXEDBUTTONS set. */
+    rebuild_toolbar(&hToolbar);
+    SendMessageA(hToolbar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_MIXEDBUTTONS);
+    style = SendMessageA(hToolbar, TB_GETSTYLE, 0, 0);
+    ok(SendMessageA(hToolbar, TB_ADDBUTTONSA, 1, (LPARAM)&buttons4[1]) == 1, "TB_ADDBUTTONSA failed\n");
+    ok(SendMessageA(hToolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(40, 20)) == 1, "TB_SETBUTTONSIZE failed\n");
+    SendMessageA(hToolbar, TB_AUTOSIZE, 0, 0 );
+    check_sizes();
+
+    /* Single non-BTNS_AUTOSIZE, BTNS_SHOWTEXT button with string with TBSTYLE_EX_MIXEDBUTTONS set. */
+    rebuild_toolbar(&hToolbar);
+    SendMessageA(hToolbar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_MIXEDBUTTONS);
+    style = SendMessageA(hToolbar, TB_GETSTYLE, 0, 0);
+    ok(SendMessageA(hToolbar, TB_ADDBUTTONSA, 1, (LPARAM)&buttons4[2]) == 1, "TB_ADDBUTTONSA failed\n");
+    ok(SendMessageA(hToolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(40, 20)) == 1, "TB_SETBUTTONSIZE failed\n");
+    SendMessageA(hToolbar, TB_AUTOSIZE, 0, 0 );
+    check_sizes();
+
     free_tbsize_results();
     DestroyWindow(hToolbar);
 }
@@ -1338,12 +1587,12 @@ static void restore_recalc_state(HWND hToolbar)
     RECT rect;
     /* return to style with a 2px top margin */
     SetWindowLongA(hToolbar, GWL_STYLE,
-        GetWindowLongA(hToolbar, GWL_STYLE) & ~TBSTYLE_FLAT);
+                   SendMessageA(hToolbar, TB_GETSTYLE, 0, 0) & ~TBSTYLE_FLAT);
     /* recalc */
     SendMessageA(hToolbar, TB_ADDBUTTONSA, 1, (LPARAM)&buttons3[3]);
     /* top margin will be 0px if a recalc occurs */
     SetWindowLongA(hToolbar, GWL_STYLE,
-        GetWindowLongA(hToolbar, GWL_STYLE) | TBSTYLE_FLAT);
+                   SendMessageA(hToolbar, TB_GETSTYLE, 0, 0) | TBSTYLE_FLAT);
     /* safety check */
     SendMessageA(hToolbar, TB_GETITEMRECT, 1, (LPARAM)&rect);
     ok(rect.top == 2, "Test will make no sense because initial top is %d instead of 2\n",
@@ -1358,6 +1607,7 @@ static void test_recalc(void)
     const int EX_STYLES_COUNT = 5;
     int i;
     BOOL recalc;
+    DWORD style;
 
     /* Like TB_ADDBUTTONSA tested in test_sized, inserting a button without text
      * results in a relayout, while adding one with text forces a recalc */
@@ -1418,6 +1668,47 @@ static void test_recalc(void)
     /* undocumented exstyle 0x2 seems to change the top margin, which
      * interferes with these tests */
 
+    /* Show that a change in TBSTYLE_WRAPABLE causes a recalc */
+    prepare_recalc_test(&hToolbar);
+    style = SendMessageA(hToolbar, TB_GETSTYLE, 0, 0);
+    SendMessageA(hToolbar, TB_SETSTYLE, 0, style);
+    recalc = did_recalc(hToolbar);
+    ok(!recalc, "recalc %d\n", recalc);
+
+    SendMessageA(hToolbar, TB_SETSTYLE, 0, style | TBSTYLE_TOOLTIPS | TBSTYLE_TRANSPARENT | CCS_BOTTOM);
+    recalc = did_recalc(hToolbar);
+    ok(!recalc, "recalc %d\n", recalc);
+
+    SendMessageA(hToolbar, TB_SETSTYLE, 0, style | TBSTYLE_WRAPABLE);
+    recalc = did_recalc(hToolbar);
+    ok(recalc, "recalc %d\n", recalc);
+    restore_recalc_state(hToolbar);
+
+    SendMessageA(hToolbar, TB_SETSTYLE, 0, style | TBSTYLE_WRAPABLE);
+    recalc = did_recalc(hToolbar);
+    ok(!recalc, "recalc %d\n", recalc);
+
+    SendMessageA(hToolbar, TB_SETSTYLE, 0, style);
+    recalc = did_recalc(hToolbar);
+    ok(recalc, "recalc %d\n", recalc);
+    restore_recalc_state(hToolbar);
+
+    /* Changing CCS_VERT does not recalc */
+    SendMessageA(hToolbar, TB_SETSTYLE, 0, style | CCS_VERT);
+    recalc = did_recalc(hToolbar);
+    ok(!recalc, "recalc %d\n", recalc);
+    restore_recalc_state(hToolbar);
+
+    SendMessageA(hToolbar, TB_SETSTYLE, 0, style);
+    recalc = did_recalc(hToolbar);
+    ok(!recalc, "recalc %d\n", recalc);
+    restore_recalc_state(hToolbar);
+
+    /* Setting the window's style directly also causes recalc */
+    SetWindowLongA(hToolbar, GWL_STYLE, style | TBSTYLE_WRAPABLE);
+    recalc = did_recalc(hToolbar);
+    ok(recalc, "recalc %d\n", recalc);
+
     DestroyWindow(hToolbar);
 }
 
@@ -1917,11 +2208,161 @@ static void test_TB_GET_SET_EXTENDEDSTYLE(void)
     ok(style == TBSTYLE_EX_VERTICAL, "got style 0x%08x, expected 0x%08x\n", style, TBSTYLE_EX_VERTICAL);
     style = SendMessageA(hwnd, TB_GETSTYLE, 0, 0);
  todo_wine
-    ok(style == CCS_VERT, "got style 0x%08x, expected 0x%08x\n", style, CCS_VERT);
+    ok(style == CCS_VERT, "got style 0x%08x, expected CCS_VERT\n", style);
 
     DestroyWindow(hwnd);
 }
 
+static void test_noresize(void)
+{
+    HWND wnd;
+    int i;
+    TBBUTTON button = {0, 10, TBSTATE_ENABLED, 0, {0, }, 0, -1};
+
+    wnd = CreateWindowExA(0, TOOLBARCLASSNAMEA, NULL, WS_CHILD | WS_VISIBLE | CCS_NORESIZE | TBSTYLE_WRAPABLE, 0, 0, 100, 20,
+                          hMainWnd, (HMENU)5, GetModuleHandleA(NULL), NULL);
+    SendMessageA(wnd, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
+
+    for (i=0; i<30; i++)
+    {
+        button.idCommand = 10 + i;
+        SendMessageA(wnd, TB_ADDBUTTONSA, 1, (LPARAM)&button);
+    }
+
+    SendMessageA(wnd, TB_SETSTATE, 10, TBSTATE_WRAP|TBSTATE_ENABLED);
+
+    /* autosize clears the wrap on button 0 */
+    SendMessageA(wnd, TB_AUTOSIZE, 0, 0);
+    for (i=0; i<30; i++)
+    {
+        SendMessageA(wnd, TB_GETBUTTON, i, (LPARAM)&button);
+        if (i % 4 == 3)
+            ok(button.fsState == (TBSTATE_WRAP|TBSTATE_ENABLED), "%d: got %08x\n", i, button.fsState);
+        else
+            ok(button.fsState == TBSTATE_ENABLED, "%d: got %08x\n", i, button.fsState);
+    }
+
+    /* changing the parent doesn't do anything */
+    MoveWindow(hMainWnd, 0,0, 400, 200, FALSE);
+    for (i=0; i<30; i++)
+    {
+        SendMessageA(wnd, TB_GETBUTTON, i, (LPARAM)&button);
+        if (i % 4 == 3)
+            ok(button.fsState == (TBSTATE_WRAP|TBSTATE_ENABLED), "%d: got %08x\n", i, button.fsState);
+        else
+            ok(button.fsState == TBSTATE_ENABLED, "%d: got %08x\n", i, button.fsState);
+    }
+
+    /* again nothing here */
+    SendMessageA(wnd, TB_AUTOSIZE, 0, 0);
+    for (i=0; i<30; i++)
+    {
+        SendMessageA(wnd, TB_GETBUTTON, i, (LPARAM)&button);
+        if (i % 4 == 3)
+            ok(button.fsState == (TBSTATE_WRAP|TBSTATE_ENABLED), "%d: got %08x\n", i, button.fsState);
+        else
+            ok(button.fsState == TBSTATE_ENABLED, "%d: got %08x\n", i, button.fsState);
+    }
+
+    DestroyWindow(wnd);
+
+}
+
+static void test_save(void)
+{
+    HWND wnd = NULL;
+    TBSAVEPARAMSW params;
+    static const WCHAR subkey[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','T','e','s','t',0};
+    static const WCHAR value[] = {'t','o','o','l','b','a','r','t','e','s','t',0};
+    LONG res;
+    HKEY key;
+    BYTE data[100];
+    DWORD size = sizeof(data), type, i, count;
+    TBBUTTON tb;
+    static const TBBUTTON more_btns[2] =
+        {
+            {0, 11, TBSTATE_HIDDEN, BTNS_BUTTON, {0}, 0, -1},
+            {0, 13, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, -1}
+        };
+    static const DWORD expect[] = {0xcafe, 1, 0xcafe0000, 3, 0xcafe0001, 5, 0xcafe0002, 7, 0xcafe0003,
+                                   9, 0xcafe0004, 11, 0xcafe0005, 13, 0xcafe0006, 0xffffffff, 0xcafe0007,
+                                   0xfffffffe, 0xcafe0008, 0x80000000, 0xcafe0009, 0x7fffffff, 0xcafe000a,
+                                   0x100, 0xcafe000b};
+    static const TBBUTTON expect_btns[] =
+    {
+        {0, 1, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
+        {0, 3, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 1, 2},
+        {0, 5, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 2, 0},
+        {0, 7, 0, BTNS_BUTTON, {0}, 0, (INT_PTR)"foo"},
+        {0, 9, 0, BTNS_BUTTON, {0}, 0, 0},
+        {0, 11, 0, BTNS_BUTTON, {0}, 0, 3},
+        {0, 13, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 6, 0},
+        {0, 0, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 7, 0},
+        {0, 0, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 8, 0},
+        {0, 0, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 9, 0},
+        {0, 0x7fffffff, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0xa, 0},
+    };
+
+    params.hkr = HKEY_CURRENT_USER;
+    params.pszSubKey = subkey;
+    params.pszValueName = value;
+
+    rebuild_toolbar_with_buttons( &wnd );
+    SendMessageW( wnd, TB_ADDBUTTONSW, sizeof(more_btns) / sizeof(more_btns[0]), (LPARAM)more_btns );
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    res = SendMessageW( wnd, TB_SAVERESTOREW, TRUE, (LPARAM)&params );
+    ok( res, "saving failed\n" );
+    ok_sequence(sequences, PARENT_SEQ_INDEX, save_parent_seq, "save", FALSE);
+    DestroyWindow( wnd );
+
+    res = RegOpenKeyW( HKEY_CURRENT_USER, subkey, &key );
+    ok( !res, "got %08x\n", res );
+    res = RegQueryValueExW( key, value, NULL, &type, data, &size );
+    ok( !res, "got %08x\n", res );
+    ok( type == REG_BINARY, "got %08x\n", type );
+    ok( size == sizeof(expect), "got %08x\n", size );
+    ok( !memcmp( data, expect, size ), "mismatch\n" );
+
+    RegCloseKey( key );
+
+    wnd = NULL;
+    rebuild_toolbar( &wnd );
+
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    res = SendMessageW( wnd, TB_SAVERESTOREW, FALSE, (LPARAM)&params );
+    ok( res, "restoring failed\n" );
+    ok_sequence(sequences, PARENT_SEQ_INDEX, restore_parent_seq, "restore", FALSE);
+    count = SendMessageW( wnd, TB_BUTTONCOUNT, 0, 0 );
+    ok( count == sizeof(expect_btns) / sizeof(expect_btns[0]), "got %d\n", count );
+
+    for (i = 0; i < count; i++)
+    {
+        res = SendMessageW( wnd, TB_GETBUTTON, i, (LPARAM)&tb );
+        ok( res, "got %d\n", res );
+
+        ok( tb.iBitmap == expect_btns[i].iBitmap, "%d: got %d\n", i, tb.iBitmap );
+        ok( tb.idCommand == expect_btns[i].idCommand, "%d: got %d\n", i, tb.idCommand );
+        ok( tb.fsState == expect_btns[i].fsState, "%d: got %02x\n", i, tb.fsState );
+        ok( tb.fsStyle == expect_btns[i].fsStyle, "%d: got %02x\n", i, tb.fsStyle );
+        ok( tb.dwData == expect_btns[i].dwData, "%d: got %lx\n", i, tb.dwData );
+        if (IS_INTRESOURCE(expect_btns[i].iString))
+            ok( tb.iString == expect_btns[i].iString, "%d: got %lx\n", i, tb.iString );
+        else
+            ok( !strcmp( (char *)tb.iString, (char *)expect_btns[i].iString ),
+                "%d: got %s\n", i, (char *)tb.iString );
+
+        /* In fact the ptr value set in TBN_GETBUTTONINFOA is simply copied */
+        if (tb.idCommand == 7)
+            ok( tb.iString == (INT_PTR)alloced_str, "string not set\n");
+    }
+
+    DestroyWindow( wnd );
+    RegOpenKeyW( HKEY_CURRENT_USER, subkey, &key );
+    RegDeleteValueW( key, value );
+    RegCloseKey( key );
+}
+
 START_TEST(toolbar)
 {
     WNDCLASSA wc;
@@ -1964,6 +2405,8 @@ START_TEST(toolbar)
     test_get_set_style();
     test_create();
     test_TB_GET_SET_EXTENDEDSTYLE();
+    test_noresize();
+    test_save();
 
     PostQuitMessage(0);
     while(GetMessageA(&msg,0,0,0)) {
index 272c2e4..4c0c7df 100644 (file)
@@ -26,6 +26,8 @@
 #include <winnls.h>
 #include <commctrl.h>
 
+#include "resources.h"
+
 #define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
 
 static void test_create_tooltip(void)
@@ -330,6 +332,30 @@ static void test_gettext(void)
         SendMessageA(hwnd, TTM_GETTOOLINFOA, 0, (LPARAM)&toolinfoA);
         ok(toolinfoA.lpszText == NULL,
            "expected NULL, got %p\n", toolinfoA.lpszText);
+
+        /* NULL hinst, valid resource id for text */
+        toolinfoA.cbSize = sizeof(TTTOOLINFOA);
+        toolinfoA.hwnd = NULL;
+        toolinfoA.hinst = NULL;
+        toolinfoA.uFlags = 0;
+        toolinfoA.uId = 0x1233ABCD;
+        toolinfoA.lpszText = MAKEINTRESOURCEA(IDS_TBADD1);
+        toolinfoA.lParam = 0xdeadbeef;
+        GetClientRect(hwnd, &toolinfoA.rect);
+        r = SendMessageA(hwnd, TTM_ADDTOOLA, 0, (LPARAM)&toolinfoA);
+        ok(r, "failed to add a tool\n");
+
+        toolinfoA.hwnd = NULL;
+        toolinfoA.uId = 0x1233ABCD;
+        toolinfoA.lpszText = bufA;
+        SendMessageA(hwnd, TTM_GETTEXTA, 0, (LPARAM)&toolinfoA);
+        ok(strcmp(toolinfoA.lpszText, "abc") == 0, "lpszText should be an empty string\n");
+
+        toolinfoA.hinst = (HINSTANCE)0xdeadbee;
+        SendMessageA(hwnd, TTM_GETTOOLINFOA, 0, (LPARAM)&toolinfoA);
+        ok(toolinfoA.hinst == NULL, "expected NULL, got %p\n", toolinfoA.hinst);
+
+        SendMessageA(hwnd, TTM_DELTOOLA, 0, (LPARAM)&toolinfoA);
     }
     else
     {
@@ -817,6 +843,154 @@ static void test_track(void)
     DestroyWindow(parent);
 }
 
+static LRESULT CALLBACK info_wnd_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+    switch(msg) {
+
+    case WM_DESTROY:
+        PostQuitMessage(0);
+        break;
+
+    default:
+        return DefWindowProcA(hWnd, msg, wParam, lParam);
+    }
+    return 0;
+}
+
+static void test_setinfo(void)
+{
+   WNDCLASSA wc;
+   LRESULT   lResult;
+   HWND parent, parent2, hwndTip, hwndTip2;
+   TTTOOLINFOA toolInfo = { 0 };
+   TTTOOLINFOA toolInfo2 = { 0 };
+   WNDPROC wndProc;
+
+   /* Create a class to use the custom draw wndproc */
+   wc.style = CS_HREDRAW | CS_VREDRAW;
+   wc.cbClsExtra = 0;
+   wc.cbWndExtra = 0;
+   wc.hInstance = GetModuleHandleA(NULL);
+   wc.hIcon = NULL;
+   wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW);
+   wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
+   wc.lpszMenuName = NULL;
+   wc.lpszClassName = "SetInfoClass";
+   wc.lpfnWndProc = info_wnd_proc;
+   RegisterClassA(&wc);
+
+   /* Create a main window */
+   parent = CreateWindowExA(0, "SetInfoClass", NULL,
+                           WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
+                           WS_MAXIMIZEBOX | WS_VISIBLE,
+                           50, 50,
+                           300, 300,
+                           NULL, NULL, NULL, 0);
+   ok(parent != NULL, "Creation of main window failed\n");
+
+   parent2 = CreateWindowExA(0, "SetInfoClass", NULL,
+                           WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
+                           WS_MAXIMIZEBOX | WS_VISIBLE,
+                           50, 50,
+                           300, 300,
+                           NULL, NULL, NULL, 0);
+   ok(parent2 != NULL, "Creation of main window failed\n");
+
+   /* Make it show */
+   ShowWindow(parent2, SW_SHOWNORMAL);
+   flush_events(100);
+
+   /* Create Tooltip */
+   hwndTip = CreateWindowExA(WS_EX_TOPMOST, TOOLTIPS_CLASSA,
+                            NULL, TTS_NOPREFIX | TTS_ALWAYSTIP,
+                            CW_USEDEFAULT, CW_USEDEFAULT,
+                            CW_USEDEFAULT, CW_USEDEFAULT,
+                            parent, NULL, GetModuleHandleA(NULL), 0);
+   ok(hwndTip != NULL, "Creation of tooltip window failed\n");
+
+   hwndTip2 = CreateWindowExA(WS_EX_TOPMOST, TOOLTIPS_CLASSA,
+                            NULL, TTS_NOPREFIX | TTS_ALWAYSTIP,
+                            CW_USEDEFAULT, CW_USEDEFAULT,
+                            CW_USEDEFAULT, CW_USEDEFAULT,
+                            parent, NULL, GetModuleHandleA(NULL), 0);
+   ok(hwndTip2 != NULL, "Creation of tooltip window failed\n");
+
+
+   /* Make it topmost, as per the MSDN */
+   SetWindowPos(hwndTip, HWND_TOPMOST, 0, 0, 0, 0,
+         SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+
+   /* Create a tool */
+   toolInfo.cbSize = TTTOOLINFOA_V1_SIZE;
+   toolInfo.hwnd = parent;
+   toolInfo.hinst = GetModuleHandleA(NULL);
+   toolInfo.uFlags = TTF_SUBCLASS;
+   toolInfo.uId = 0x1234ABCD;
+   toolInfo.lpszText = (LPSTR)"This is a test tooltip";
+   toolInfo.lParam = 0xdeadbeef;
+   GetClientRect (parent, &toolInfo.rect);
+   lResult = SendMessageA(hwndTip, TTM_ADDTOOLA, 0, (LPARAM)&toolInfo);
+   ok(lResult, "Adding the tool to the tooltip failed\n");
+
+   toolInfo.cbSize = TTTOOLINFOA_V1_SIZE;
+   toolInfo.hwnd = parent2;
+   toolInfo.hinst = GetModuleHandleA(NULL);
+   toolInfo.uFlags = 0;
+   toolInfo.uId = 0x1234ABCE;
+   toolInfo.lpszText = (LPSTR)"This is a test tooltip";
+   toolInfo.lParam = 0xdeadbeef;
+   GetClientRect (parent, &toolInfo.rect);
+   lResult = SendMessageA(hwndTip, TTM_ADDTOOLA, 0, (LPARAM)&toolInfo);
+   ok(lResult, "Adding the tool to the tooltip failed\n");
+
+   /* Try to Remove Subclass */
+   toolInfo2.cbSize = TTTOOLINFOA_V1_SIZE;
+   toolInfo2.hwnd = parent;
+   toolInfo2.uId = 0x1234ABCD;
+   lResult = SendMessageA(hwndTip, TTM_GETTOOLINFOA, 0, (LPARAM)&toolInfo2);
+   ok(lResult, "GetToolInfo failed\n");
+   ok(toolInfo2.uFlags & TTF_SUBCLASS, "uFlags does not have subclass\n");
+   wndProc = (WNDPROC)GetWindowLongPtrA(parent, GWLP_WNDPROC);
+   ok (wndProc != info_wnd_proc, "Window Proc is wrong\n");
+
+   toolInfo2.uFlags &= ~TTF_SUBCLASS;
+   SendMessageA(hwndTip, TTM_SETTOOLINFOA, 0, (LPARAM)&toolInfo2);
+   lResult = SendMessageA(hwndTip, TTM_GETTOOLINFOA, 0, (LPARAM)&toolInfo2);
+   ok(lResult, "GetToolInfo failed\n");
+   ok(!(toolInfo2.uFlags & TTF_SUBCLASS), "uFlags has subclass\n");
+   wndProc = (WNDPROC)GetWindowLongPtrA(parent, GWLP_WNDPROC);
+   ok (wndProc != info_wnd_proc, "Window Proc is wrong\n");
+
+   /* Try to Add Subclass */
+
+   /* Make it topmost, as per the MSDN */
+   SetWindowPos(hwndTip2, HWND_TOPMOST, 0, 0, 0, 0,
+         SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+
+   toolInfo2.cbSize = TTTOOLINFOA_V1_SIZE;
+   toolInfo2.hwnd = parent2;
+   toolInfo2.uId = 0x1234ABCE;
+   lResult = SendMessageA(hwndTip, TTM_GETTOOLINFOA, 0, (LPARAM)&toolInfo2);
+   ok(lResult, "GetToolInfo failed\n");
+   ok(!(toolInfo2.uFlags & TTF_SUBCLASS), "uFlags has subclass\n");
+   wndProc = (WNDPROC)GetWindowLongPtrA(parent2, GWLP_WNDPROC);
+   ok (wndProc == info_wnd_proc, "Window Proc is wrong\n");
+
+   toolInfo2.uFlags |= TTF_SUBCLASS;
+   SendMessageA(hwndTip, TTM_SETTOOLINFOA, 0, (LPARAM)&toolInfo2);
+   lResult = SendMessageA(hwndTip, TTM_GETTOOLINFOA, 0, (LPARAM)&toolInfo2);
+   ok(lResult, "GetToolInfo failed\n");
+   ok(toolInfo2.uFlags & TTF_SUBCLASS, "uFlags does not have subclass\n");
+   wndProc = (WNDPROC)GetWindowLongPtrA(parent2, GWLP_WNDPROC);
+   ok (wndProc == info_wnd_proc, "Window Proc is wrong\n");
+
+   /* Clean up */
+   DestroyWindow(hwndTip);
+   DestroyWindow(hwndTip2);
+   DestroyWindow(parent);
+   DestroyWindow(parent2);
+}
+
 START_TEST(tooltips)
 {
     InitCommonControls();
@@ -828,4 +1002,5 @@ START_TEST(tooltips)
     test_longtextA();
     test_longtextW();
     test_track();
+    test_setinfo();
 }
index dc4e478..7068bc9 100644 (file)
@@ -19,6 +19,7 @@
  */
 
 #include <stdarg.h>
+#include <stdio.h>
 
 #include "windef.h"
 #include "winbase.h"
@@ -226,7 +227,8 @@ static const struct message parent_expand_empty_kb_seq[] = {
     { 0 }
 };
 
-static const struct message parent_singleexpand_seq[] = {
+static const struct message parent_singleexpand_seq0[] = {
+    /* alpha expands */
     { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
     { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
     { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
@@ -235,6 +237,84 @@ static const struct message parent_singleexpand_seq[] = {
     { 0 }
 };
 
+static const struct message parent_singleexpand_seq1[] = {
+    /* bravo expands */
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
+    { 0 }
+};
+
+static const struct message parent_singleexpand_seq2[] = {
+    /* delta expands, bravo collapses */
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
+    { 0 }
+};
+
+static const struct message parent_singleexpand_seq3[] = {
+    /* foxtrot expands, alpha and delta collapse */
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
+    { 0 }
+};
+
+static const struct message parent_singleexpand_seq4[] = {
+    /* alpha expands, foxtrot collapses */
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
+    { 0 }
+};
+
+static const struct message parent_singleexpand_seq5[] = {
+    /* foxtrot expands while golf is selected, then golf expands and alpha collapses */
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
+    { 0 }
+};
+
+static const struct message parent_singleexpand_seq6[] = {
+    /* hotel does not expand and india does not collapse because they have no children */
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
+    { 0 }
+};
+
+static const struct message parent_singleexpand_seq7[] = {
+    /* india does not expand and hotel does not collapse because they have no children */
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SINGLEEXPAND },
+    { 0 }
+};
+
 static const struct message parent_get_dispinfo_seq[] = {
     { WM_NOTIFY, sent|id, 0, 0, TVN_GETDISPINFOA },
     { 0 }
@@ -1637,21 +1717,95 @@ static void test_expandedimage(void)
 static void test_TVS_SINGLEEXPAND(void)
 {
     HWND hTree;
+    HTREEITEM alpha, bravo, charlie, delta, echo, foxtrot, golf, hotel, india, juliet;
+    TVINSERTSTRUCTA ins;
+    char foo[] = "foo";
+    char context[32];
+    int i;
     BOOL ret;
 
+    /* build a fairly complex tree
+     * - TVI_ROOT
+     *   - alpha
+     *     - bravo
+     *       - charlie
+     *     - delta
+     *       - echo
+     *   - foxtrot
+     *     - golf
+     *       - hotel
+     *       - india
+     *     - juliet
+     */
+    struct
+    {
+        HTREEITEM *handle;
+        HTREEITEM *parent;
+        UINT final_state;
+    }
+    items[] =
+    {
+        { &alpha,    NULL,      TVIS_EXPANDEDONCE               },
+        { &bravo,    &alpha,    TVIS_EXPANDEDONCE               },
+        { &charlie,  &bravo,    0                               },
+        { &delta,    &alpha,    TVIS_EXPANDEDONCE               },
+        { &echo,     &delta,    0                               },
+        { &foxtrot,  NULL,      TVIS_EXPANDEDONCE|TVIS_EXPANDED },
+        { &golf,     &foxtrot,  TVIS_EXPANDEDONCE|TVIS_EXPANDED },
+        { &hotel,    &golf,     0                               },
+        { &india,    &golf,     TVIS_SELECTED                   },
+        { &juliet,   &foxtrot,  0                               }
+    };
+
+    struct
+    {
+        HTREEITEM *select;
+        const struct message *sequence;
+    }
+    sequence_tests[] =
+    {
+        { &alpha,    parent_singleexpand_seq0 },
+        { &bravo,    parent_singleexpand_seq1 },
+        { &delta,    parent_singleexpand_seq2 },
+        { &foxtrot,  parent_singleexpand_seq3 },
+        { &alpha,    parent_singleexpand_seq4 },
+        { &golf,     parent_singleexpand_seq5 },
+        { &hotel,    parent_singleexpand_seq6 },
+        { &india,    parent_singleexpand_seq7 },
+        { &india,    empty_seq }
+    };
+
     hTree = create_treeview_control(0);
     SetWindowLongA(hTree, GWL_STYLE, GetWindowLongA(hTree, GWL_STYLE) | TVS_SINGLEEXPAND);
     /* to avoid painting related notifications */
     ShowWindow(hTree, SW_HIDE);
-    fill_tree(hTree);
+    for (i = 0; i < sizeof(items)/sizeof(items[0]); i++)
+    {
+        ins.hParent = items[i].parent ? *items[i].parent : TVI_ROOT;
+        ins.hInsertAfter = TVI_FIRST;
+        U(ins).item.mask = TVIF_TEXT;
+        U(ins).item.pszText = foo;
+        *items[i].handle = TreeView_InsertItemA(hTree, &ins);
+    }
 
-    flush_sequences(sequences, NUM_MSG_SEQUENCES);
-    ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
-    ok(ret, "got %d\n", ret);
-    ok_sequence(sequences, PARENT_SEQ_INDEX, parent_singleexpand_seq, "singleexpand notifications", FALSE);
+    for (i = 0; i < sizeof(sequence_tests)/sizeof(sequence_tests[0]); i++)
+    {
+        flush_sequences(sequences, NUM_MSG_SEQUENCES);
+        ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)(*sequence_tests[i].select));
+        ok(ret, "got %d\n", ret);
+        sprintf(context, "singleexpand notifications %d", i);
+        ok_sequence(sequences, PARENT_SEQ_INDEX, sequence_tests[i].sequence, context, FALSE);
+    }
+
+    for (i = 0; i < sizeof(items)/sizeof(items[0]); i++)
+    {
+        ret = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)(*items[i].handle), 0xFFFF);
+        ok(ret == items[i].final_state, "singleexpand items[%d]: expected state 0x%x got 0x%x\n",
+           i, items[i].final_state, ret);
+    }
 
     /* a workaround for NT4 that sends expand notifications when nothing is about to expand */
-    ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)hRoot);
+    ret = SendMessageA(hTree, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT);
     ok(ret, "got %d\n", ret);
     fill_tree(hTree);
     ret = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, 0);