comctl32/listview: Always send LVN_ITEMCHANGING notification even if nothing changed.
[wine] / dlls / comctl32 / tests / tooltips.c
1 /*
2  * Copyright 2005 Dmitry Timoshkov
3  * Copyright 2008 Jason Edmeades
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19
20 #include <assert.h>
21 #include <windows.h>
22 #include <commctrl.h>
23
24 #include "wine/test.h"
25
26 #define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
27
28 static void test_create_tooltip(void)
29 {
30     HWND parent, hwnd;
31     DWORD style, exp_style;
32
33     parent = CreateWindowEx(0, "static", NULL, WS_POPUP,
34                           0, 0, 0, 0,
35                           NULL, NULL, NULL, 0);
36     assert(parent);
37
38     hwnd = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, 0x7fffffff | WS_POPUP,
39                           10, 10, 300, 100,
40                           parent, NULL, NULL, 0);
41     assert(hwnd);
42
43     style = GetWindowLong(hwnd, GWL_STYLE);
44     trace("style = %08x\n", style);
45     exp_style = 0x7fffffff | WS_POPUP;
46     exp_style &= ~(WS_CHILD | WS_MAXIMIZE | WS_BORDER | WS_DLGFRAME);
47     ok(style == exp_style || broken(style == (exp_style | WS_BORDER)), /* nt4 */
48        "wrong style %08x/%08x\n", style, exp_style);
49
50     DestroyWindow(hwnd);
51
52     hwnd = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, 0,
53                           10, 10, 300, 100,
54                           parent, NULL, NULL, 0);
55     assert(hwnd);
56
57     style = GetWindowLong(hwnd, GWL_STYLE);
58     trace("style = %08x\n", style);
59     ok(style == (WS_POPUP | WS_CLIPSIBLINGS | WS_BORDER),
60        "wrong style %08x\n", style);
61
62     DestroyWindow(hwnd);
63
64     DestroyWindow(parent);
65 }
66
67 /* try to make sure pending X events have been processed before continuing */
68 static void flush_events(int waitTime)
69 {
70     MSG msg;
71     int diff = waitTime;
72     DWORD time = GetTickCount() + waitTime;
73
74     while (diff > 0)
75     {
76         if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min(100,diff), QS_ALLEVENTS) == WAIT_TIMEOUT) break;
77         while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
78         diff = time - GetTickCount();
79     }
80 }
81
82 static int CD_Stages;
83 static LRESULT CD_Result;
84 static HWND g_hwnd;
85
86 #define TEST_CDDS_PREPAINT           0x00000001
87 #define TEST_CDDS_POSTPAINT          0x00000002
88 #define TEST_CDDS_PREERASE           0x00000004
89 #define TEST_CDDS_POSTERASE          0x00000008
90 #define TEST_CDDS_ITEMPREPAINT       0x00000010
91 #define TEST_CDDS_ITEMPOSTPAINT      0x00000020
92 #define TEST_CDDS_ITEMPREERASE       0x00000040
93 #define TEST_CDDS_ITEMPOSTERASE      0x00000080
94 #define TEST_CDDS_SUBITEM            0x00000100
95
96 static LRESULT CALLBACK CustomDrawWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
97 {
98     switch(msg) {
99
100     case WM_DESTROY:
101         PostQuitMessage(0);
102         break;
103
104     case WM_NOTIFY:
105         if (((NMHDR *)lParam)->code == NM_CUSTOMDRAW) {
106             NMTTCUSTOMDRAW *ttcd = (NMTTCUSTOMDRAW*) lParam;
107             ok(ttcd->nmcd.hdr.hwndFrom == g_hwnd, "Unexpected hwnd source %p (%p)\n",
108                  ttcd->nmcd.hdr.hwndFrom, g_hwnd);
109             ok(ttcd->nmcd.hdr.idFrom == 0x1234ABCD, "Unexpected id %x\n", (int)ttcd->nmcd.hdr.idFrom);
110
111             switch (ttcd->nmcd.dwDrawStage) {
112             case CDDS_PREPAINT     : CD_Stages |= TEST_CDDS_PREPAINT; break;
113             case CDDS_POSTPAINT    : CD_Stages |= TEST_CDDS_POSTPAINT; break;
114             case CDDS_PREERASE     : CD_Stages |= TEST_CDDS_PREERASE; break;
115             case CDDS_POSTERASE    : CD_Stages |= TEST_CDDS_POSTERASE; break;
116             case CDDS_ITEMPREPAINT : CD_Stages |= TEST_CDDS_ITEMPREPAINT; break;
117             case CDDS_ITEMPOSTPAINT: CD_Stages |= TEST_CDDS_ITEMPOSTPAINT; break;
118             case CDDS_ITEMPREERASE : CD_Stages |= TEST_CDDS_ITEMPREERASE; break;
119             case CDDS_ITEMPOSTERASE: CD_Stages |= TEST_CDDS_ITEMPOSTERASE; break;
120             case CDDS_SUBITEM      : CD_Stages |= TEST_CDDS_SUBITEM; break;
121             default: CD_Stages = -1;
122             }
123
124             if (ttcd->nmcd.dwDrawStage == CDDS_PREPAINT) return CD_Result;
125         }
126         /* drop through */
127
128     default:
129         return DefWindowProcA(hWnd, msg, wParam, lParam);
130     }
131
132     return 0L;
133 }
134
135 static void test_customdraw(void) {
136     static struct {
137         LRESULT FirstReturnValue;
138         int ExpectedCalls;
139     } expectedResults[] = {
140         /* Valid notification responses */
141         {CDRF_DODEFAULT, TEST_CDDS_PREPAINT},
142         {CDRF_SKIPDEFAULT, TEST_CDDS_PREPAINT},
143         {CDRF_NOTIFYPOSTPAINT, TEST_CDDS_PREPAINT | TEST_CDDS_POSTPAINT},
144
145         /* Invalid notification responses */
146         {CDRF_NOTIFYITEMDRAW, TEST_CDDS_PREPAINT},
147         {CDRF_NOTIFYPOSTERASE, TEST_CDDS_PREPAINT},
148         {CDRF_NEWFONT, TEST_CDDS_PREPAINT}
149     };
150
151    DWORD       iterationNumber;
152    WNDCLASSA wc;
153    LRESULT   lResult;
154
155    /* Create a class to use the custom draw wndproc */
156    wc.style = CS_HREDRAW | CS_VREDRAW;
157    wc.cbClsExtra = 0;
158    wc.cbWndExtra = 0;
159    wc.hInstance = GetModuleHandleA(NULL);
160    wc.hIcon = NULL;
161    wc.hCursor = LoadCursorA(NULL, IDC_ARROW);
162    wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
163    wc.lpszMenuName = NULL;
164    wc.lpszClassName = "CustomDrawClass";
165    wc.lpfnWndProc = CustomDrawWndProc;
166    RegisterClass(&wc);
167
168    for (iterationNumber = 0;
169         iterationNumber < sizeof(expectedResults)/sizeof(expectedResults[0]);
170         iterationNumber++) {
171
172        HWND parent, hwndTip;
173        RECT rect;
174        TOOLINFO toolInfo = { 0 };
175
176        /* Create a main window */
177        parent = CreateWindowEx(0, "CustomDrawClass", NULL,
178                                WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
179                                WS_MAXIMIZEBOX | WS_VISIBLE,
180                                50, 50,
181                                300, 300,
182                                NULL, NULL, NULL, 0);
183        ok(parent != NULL, "Creation of main window failed\n");
184
185        /* Make it show */
186        ShowWindow(parent, SW_SHOWNORMAL);
187        flush_events(100);
188
189        /* Create Tooltip */
190        hwndTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS,
191                                 NULL, TTS_NOPREFIX | TTS_ALWAYSTIP,
192                                 CW_USEDEFAULT, CW_USEDEFAULT,
193                                 CW_USEDEFAULT, CW_USEDEFAULT,
194                                 parent, NULL, GetModuleHandleA(NULL), 0);
195        ok(hwndTip != NULL, "Creation of tooltip window failed\n");
196
197        /* Set up parms for the wndproc to handle */
198        CD_Stages = 0;
199        CD_Result = expectedResults[iterationNumber].FirstReturnValue;
200        g_hwnd    = hwndTip;
201
202        /* Make it topmost, as per the MSDN */
203        SetWindowPos(hwndTip, HWND_TOPMOST, 0, 0, 0, 0,
204              SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
205
206        /* Create a tool */
207        toolInfo.cbSize = TTTOOLINFO_V1_SIZE;
208        toolInfo.hwnd = parent;
209        toolInfo.hinst = GetModuleHandleA(NULL);
210        toolInfo.uFlags = TTF_SUBCLASS;
211        toolInfo.uId = 0x1234ABCD;
212        toolInfo.lpszText = (LPSTR)"This is a test tooltip";
213        toolInfo.lParam = 0xdeadbeef;
214        GetClientRect (parent, &toolInfo.rect);
215        lResult = SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);
216        ok(lResult, "Adding the tool to the tooltip failed\n");
217
218        /* Make tooltip appear quickly */
219        SendMessage(hwndTip, TTM_SETDELAYTIME, TTDT_INITIAL, MAKELPARAM(1,0));
220
221        /* Put cursor inside window, tooltip will appear immediately */
222        GetWindowRect( parent, &rect );
223        SetCursorPos( (rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2 );
224        flush_events(200);
225
226        if (CD_Stages)
227        {
228            /* Check CustomDraw results */
229            ok(CD_Stages == expectedResults[iterationNumber].ExpectedCalls ||
230               broken(CD_Stages == (expectedResults[iterationNumber].ExpectedCalls & ~TEST_CDDS_POSTPAINT)), /* nt4 */
231               "CustomDraw run %d stages %x, expected %x\n", iterationNumber, CD_Stages,
232               expectedResults[iterationNumber].ExpectedCalls);
233        }
234
235        /* Clean up */
236        DestroyWindow(hwndTip);
237        DestroyWindow(parent);
238    }
239
240
241 }
242
243 static const CHAR testcallbackA[]  = "callback";
244
245 static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
246 {
247     if (message == WM_NOTIFY && lParam)
248     {
249         NMTTDISPINFOA *ttnmdi = (NMTTDISPINFOA*)lParam;
250
251         if (ttnmdi->hdr.code == TTN_GETDISPINFOA)
252             lstrcpy(ttnmdi->lpszText, testcallbackA);
253     }
254
255     return DefWindowProcA(hwnd, message, wParam, lParam);
256 }
257
258 static BOOL register_parent_wnd_class(void)
259 {
260     WNDCLASSA cls;
261
262     cls.style = 0;
263     cls.lpfnWndProc = parent_wnd_proc;
264     cls.cbClsExtra = 0;
265     cls.cbWndExtra = 0;
266     cls.hInstance = GetModuleHandleA(NULL);
267     cls.hIcon = 0;
268     cls.hCursor = LoadCursorA(0, IDC_ARROW);
269     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
270     cls.lpszMenuName = NULL;
271     cls.lpszClassName = "Tooltips test parent class";
272     return RegisterClassA(&cls);
273 }
274
275 static HWND create_parent_window(void)
276 {
277     if (!register_parent_wnd_class())
278         return NULL;
279
280     return CreateWindowEx(0, "Tooltips test parent class",
281                           "Tooltips test parent window",
282                           WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
283                           WS_MAXIMIZEBOX | WS_VISIBLE,
284                           0, 0, 100, 100,
285                           GetDesktopWindow(), NULL, GetModuleHandleA(NULL), NULL);
286 }
287
288 static void test_gettext(void)
289 {
290     HWND hwnd, notify;
291     TTTOOLINFOA toolinfoA;
292     TTTOOLINFOW toolinfoW;
293     LRESULT r;
294     CHAR bufA[10] = "";
295     WCHAR bufW[10] = { 0 };
296     static const CHAR testtipA[] = "testtip";
297
298     notify = create_parent_window();
299     ok(notify != NULL, "Expected notification window to be created\n");
300
301     /* For bug 14790 - lpszText is NULL */
302     hwnd = CreateWindowExA(0, TOOLTIPS_CLASSA, NULL, 0,
303                            10, 10, 300, 100,
304                            NULL, NULL, NULL, 0);
305     assert(hwnd);
306
307     /* use sizeof(TTTOOLINFOA) instead of TTTOOLINFOA_V1_SIZE so that adding it fails on Win9x */
308     /* otherwise it crashes on the NULL lpszText */
309     toolinfoA.cbSize = sizeof(TTTOOLINFOA);
310     toolinfoA.hwnd = NULL;
311     toolinfoA.hinst = GetModuleHandleA(NULL);
312     toolinfoA.uFlags = 0;
313     toolinfoA.uId = 0x1234ABCD;
314     toolinfoA.lpszText = NULL;
315     toolinfoA.lParam = 0xdeadbeef;
316     GetClientRect(hwnd, &toolinfoA.rect);
317     r = SendMessageA(hwnd, TTM_ADDTOOL, 0, (LPARAM)&toolinfoA);
318     if (r)
319     {
320         toolinfoA.hwnd = NULL;
321         toolinfoA.uId = 0x1234ABCD;
322         toolinfoA.lpszText = bufA;
323         SendMessageA(hwnd, TTM_GETTEXTA, 0, (LPARAM)&toolinfoA);
324         ok(strcmp(toolinfoA.lpszText, "") == 0, "lpszText should be an empty string\n");
325
326         toolinfoA.lpszText = bufA;
327         SendMessageA(hwnd, TTM_GETTOOLINFOA, 0, (LPARAM)&toolinfoA);
328         ok(toolinfoA.lpszText == NULL,
329            "expected NULL, got %p\n", toolinfoA.lpszText);
330     }
331     else
332     {
333         win_skip( "Old comctl32, not testing NULL text\n" );
334         DestroyWindow( hwnd );
335         return;
336     }
337
338     /* add another tool with text */
339     toolinfoA.cbSize = sizeof(TTTOOLINFOA);
340     toolinfoA.hwnd = NULL;
341     toolinfoA.hinst = GetModuleHandleA(NULL);
342     toolinfoA.uFlags = 0;
343     toolinfoA.uId = 0x1235ABCD;
344     strcpy(bufA, testtipA);
345     toolinfoA.lpszText = bufA;
346     toolinfoA.lParam = 0xdeadbeef;
347     GetClientRect(hwnd, &toolinfoA.rect);
348     r = SendMessageA(hwnd, TTM_ADDTOOL, 0, (LPARAM)&toolinfoA);
349     ok(r, "Adding the tool to the tooltip failed\n");
350     if (r)
351     {
352         DWORD length;
353
354         length = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0);
355         ok(length == 0, "Expected 0, got %d\n", length);
356
357         toolinfoA.hwnd = NULL;
358         toolinfoA.uId = 0x1235ABCD;
359         toolinfoA.lpszText = bufA;
360         SendMessageA(hwnd, TTM_GETTEXTA, 0, (LPARAM)&toolinfoA);
361         ok(strcmp(toolinfoA.lpszText, testtipA) == 0, "lpszText should be an empty string\n");
362
363         memset(bufA, 0x1f, sizeof(bufA));
364         toolinfoA.lpszText = bufA;
365         SendMessageA(hwnd, TTM_GETTOOLINFOA, 0, (LPARAM)&toolinfoA);
366         ok(strcmp(toolinfoA.lpszText, testtipA) == 0,
367            "expected %s, got %p\n", testtipA, toolinfoA.lpszText);
368
369         length = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0);
370         ok(length == 0, "Expected 0, got %d\n", length);
371     }
372
373     /* add another with callback text */
374     toolinfoA.cbSize = sizeof(TTTOOLINFOA);
375     toolinfoA.hwnd = notify;
376     toolinfoA.hinst = GetModuleHandleA(NULL);
377     toolinfoA.uFlags = 0;
378     toolinfoA.uId = 0x1236ABCD;
379     toolinfoA.lpszText = LPSTR_TEXTCALLBACKA;
380     toolinfoA.lParam = 0xdeadbeef;
381     GetClientRect(hwnd, &toolinfoA.rect);
382     r = SendMessageA(hwnd, TTM_ADDTOOL, 0, (LPARAM)&toolinfoA);
383     ok(r, "Adding the tool to the tooltip failed\n");
384     if (r)
385     {
386         toolinfoA.hwnd = notify;
387         toolinfoA.uId = 0x1236ABCD;
388         toolinfoA.lpszText = bufA;
389         SendMessageA(hwnd, TTM_GETTEXTA, 0, (LPARAM)&toolinfoA);
390         ok(strcmp(toolinfoA.lpszText, testcallbackA) == 0,
391            "lpszText should be an (%s) string\n", testcallbackA);
392
393         toolinfoA.lpszText = bufA;
394         SendMessageA(hwnd, TTM_GETTOOLINFOA, 0, (LPARAM)&toolinfoA);
395         ok(toolinfoA.lpszText == LPSTR_TEXTCALLBACKA,
396            "expected LPSTR_TEXTCALLBACKA, got %p\n", toolinfoA.lpszText);
397     }
398
399     DestroyWindow(hwnd);
400     DestroyWindow(notify);
401
402     SetLastError(0xdeadbeef);
403     hwnd = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, 0,
404                            10, 10, 300, 100,
405                            NULL, NULL, NULL, 0);
406
407     if (!hwnd && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
408         win_skip("CreateWindowExW is not implemented\n");
409         return;
410     }
411
412     assert(hwnd);
413
414     toolinfoW.cbSize = sizeof(TTTOOLINFOW);
415     toolinfoW.hwnd = NULL;
416     toolinfoW.hinst = GetModuleHandleA(NULL);
417     toolinfoW.uFlags = 0;
418     toolinfoW.uId = 0x1234ABCD;
419     toolinfoW.lpszText = NULL;
420     toolinfoW.lParam = 0xdeadbeef;
421     GetClientRect(hwnd, &toolinfoW.rect);
422     r = SendMessageW(hwnd, TTM_ADDTOOL, 0, (LPARAM)&toolinfoW);
423     ok(r, "Adding the tool to the tooltip failed\n");
424
425     if (0)  /* crashes on NT4 */
426     {
427         toolinfoW.hwnd = NULL;
428         toolinfoW.uId = 0x1234ABCD;
429         toolinfoW.lpszText = bufW;
430         SendMessageW(hwnd, TTM_GETTEXTW, 0, (LPARAM)&toolinfoW);
431         ok(toolinfoW.lpszText[0] == 0, "lpszText should be an empty string\n");
432     }
433
434     DestroyWindow(hwnd);
435 }
436
437 static void test_ttm_gettoolinfo(void)
438 {
439     TTTOOLINFOA ti;
440     TTTOOLINFOW tiW;
441     HWND hwnd;
442     DWORD r;
443
444     hwnd = CreateWindowExA(0, TOOLTIPS_CLASSA, NULL, 0,
445                            10, 10, 300, 100,
446                            NULL, NULL, NULL, 0);
447
448     ti.cbSize = TTTOOLINFOA_V2_SIZE;
449     ti.hwnd = NULL;
450     ti.hinst = GetModuleHandleA(NULL);
451     ti.uFlags = 0;
452     ti.uId = 0x1234ABCD;
453     ti.lpszText = NULL;
454     ti.lParam = 0x1abe11ed;
455     GetClientRect(hwnd, &ti.rect);
456     r = SendMessageA(hwnd, TTM_ADDTOOLA, 0, (LPARAM)&ti);
457     ok(r, "Adding the tool to the tooltip failed\n");
458
459     ti.cbSize = TTTOOLINFOA_V2_SIZE;
460     ti.lParam = 0xaaaaaaaa;
461     r = SendMessageA(hwnd, TTM_GETTOOLINFOA, 0, (LPARAM)&ti);
462     ok(r, "Getting tooltip info failed\n");
463     ok(0x1abe11ed == ti.lParam ||
464        broken(0x1abe11ed != ti.lParam), /* comctl32 < 5.81 */
465        "Expected 0x1abe11ed, got %lx\n", ti.lParam);
466
467     tiW.cbSize = TTTOOLINFOW_V2_SIZE;
468     tiW.hwnd = NULL;
469     tiW.uId = 0x1234ABCD;
470     tiW.lParam = 0xaaaaaaaa;
471     r = SendMessageA(hwnd, TTM_GETTOOLINFOW, 0, (LPARAM)&tiW);
472     ok(r, "Getting tooltip info failed\n");
473     ok(0x1abe11ed == tiW.lParam ||
474        broken(0x1abe11ed != tiW.lParam), /* comctl32 < 5.81 */
475        "Expected 0x1abe11ed, got %lx\n", tiW.lParam);
476
477     ti.cbSize = TTTOOLINFOA_V2_SIZE;
478     ti.uId = 0x1234ABCD;
479     ti.lParam = 0x55555555;
480     SendMessageA(hwnd, TTM_SETTOOLINFOA, 0, (LPARAM)&ti);
481
482     ti.cbSize = TTTOOLINFOA_V2_SIZE;
483     ti.lParam = 0xdeadbeef;
484     r = SendMessageA(hwnd, TTM_GETTOOLINFOA, 0, (LPARAM)&ti);
485     ok(r, "Getting tooltip info failed\n");
486     ok(0x55555555 == ti.lParam ||
487        broken(0x55555555 != ti.lParam), /* comctl32 < 5.81 */
488        "Expected 0x55555555, got %lx\n", ti.lParam);
489
490     DestroyWindow(hwnd);
491
492     /* 1. test size parameter validation rules (ansi messages) */
493     hwnd = CreateWindowExA(0, TOOLTIPS_CLASSA, NULL, 0,
494                            10, 10, 300, 100,
495                            NULL, NULL, NULL, 0);
496
497     ti.cbSize = TTTOOLINFOA_V1_SIZE - 1;
498     ti.hwnd = NULL;
499     ti.hinst = GetModuleHandleA(NULL);
500     ti.uFlags = 0;
501     ti.uId = 0x1234ABCD;
502     ti.lpszText = NULL;
503     ti.lParam = 0xdeadbeef;
504     GetClientRect(hwnd, &ti.rect);
505     r = SendMessage(hwnd, TTM_ADDTOOLA, 0, (LPARAM)&ti);
506     ok(r, "Adding the tool to the tooltip failed\n");
507     r = SendMessage(hwnd, TTM_GETTOOLCOUNT, 0, 0);
508     expect(1, r);
509
510     ti.cbSize = TTTOOLINFOA_V1_SIZE - 1;
511     ti.hwnd = NULL;
512     ti.uId = 0x1234ABCD;
513     SendMessage(hwnd, TTM_DELTOOLA, 0, (LPARAM)&ti);
514     r = SendMessage(hwnd, TTM_GETTOOLCOUNT, 0, 0);
515     expect(0, r);
516
517     ti.cbSize = TTTOOLINFOA_V2_SIZE - 1;
518     ti.hwnd = NULL;
519     ti.hinst = GetModuleHandleA(NULL);
520     ti.uFlags = 0;
521     ti.uId = 0x1234ABCD;
522     ti.lpszText = NULL;
523     ti.lParam = 0xdeadbeef;
524     GetClientRect(hwnd, &ti.rect);
525     r = SendMessage(hwnd, TTM_ADDTOOLA, 0, (LPARAM)&ti);
526     ok(r, "Adding the tool to the tooltip failed\n");
527     r = SendMessage(hwnd, TTM_GETTOOLCOUNT, 0, 0);
528     expect(1, r);
529
530     ti.cbSize = TTTOOLINFOA_V2_SIZE - 1;
531     ti.hwnd = NULL;
532     ti.uId = 0x1234ABCD;
533     SendMessage(hwnd, TTM_DELTOOLA, 0, (LPARAM)&ti);
534     r = SendMessage(hwnd, TTM_GETTOOLCOUNT, 0, 0);
535     expect(0, r);
536
537     ti.cbSize = TTTOOLINFOA_V2_SIZE + 1;
538     ti.hwnd = NULL;
539     ti.hinst = GetModuleHandleA(NULL);
540     ti.uFlags = 0;
541     ti.uId = 0x1234ABCD;
542     ti.lpszText = NULL;
543     ti.lParam = 0xdeadbeef;
544     GetClientRect(hwnd, &ti.rect);
545     r = SendMessage(hwnd, TTM_ADDTOOLA, 0, (LPARAM)&ti);
546     ok(r, "Adding the tool to the tooltip failed\n");
547     r = SendMessage(hwnd, TTM_GETTOOLCOUNT, 0, 0);
548     expect(1, r);
549
550     ti.cbSize = TTTOOLINFOA_V2_SIZE + 1;
551     ti.hwnd = NULL;
552     ti.uId = 0x1234ABCD;
553     SendMessage(hwnd, TTM_DELTOOLA, 0, (LPARAM)&ti);
554     r = SendMessage(hwnd, TTM_GETTOOLCOUNT, 0, 0);
555     expect(0, r);
556
557     DestroyWindow(hwnd);
558
559     /* 2. test size parameter validation rules (w-messages) */
560     hwnd = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, 0,
561                            10, 10, 300, 100,
562                            NULL, NULL, NULL, 0);
563     if(!hwnd)
564     {
565         win_skip("CreateWindowExW() not supported. Skipping.\n");
566         return;
567     }
568
569     tiW.cbSize = TTTOOLINFOW_V1_SIZE - 1;
570     tiW.hwnd = NULL;
571     tiW.hinst = GetModuleHandleA(NULL);
572     tiW.uFlags = 0;
573     tiW.uId = 0x1234ABCD;
574     tiW.lpszText = NULL;
575     tiW.lParam = 0xdeadbeef;
576     GetClientRect(hwnd, &tiW.rect);
577     r = SendMessageW(hwnd, TTM_ADDTOOLW, 0, (LPARAM)&tiW);
578     ok(r, "Adding the tool to the tooltip failed\n");
579     r = SendMessageW(hwnd, TTM_GETTOOLCOUNT, 0, 0);
580     expect(1, r);
581
582     tiW.cbSize = TTTOOLINFOW_V1_SIZE - 1;
583     tiW.hwnd = NULL;
584     tiW.uId = 0x1234ABCD;
585     SendMessageW(hwnd, TTM_DELTOOLW, 0, (LPARAM)&tiW);
586     r = SendMessageW(hwnd, TTM_GETTOOLCOUNT, 0, 0);
587     expect(0, r);
588
589     tiW.cbSize = TTTOOLINFOW_V2_SIZE - 1;
590     tiW.hwnd = NULL;
591     tiW.hinst = GetModuleHandleA(NULL);
592     tiW.uFlags = 0;
593     tiW.uId = 0x1234ABCD;
594     tiW.lpszText = NULL;
595     tiW.lParam = 0xdeadbeef;
596     GetClientRect(hwnd, &tiW.rect);
597     r = SendMessageW(hwnd, TTM_ADDTOOLW, 0, (LPARAM)&tiW);
598     ok(r, "Adding the tool to the tooltip failed\n");
599     r = SendMessageW(hwnd, TTM_GETTOOLCOUNT, 0, 0);
600     expect(1, r);
601
602     tiW.cbSize = TTTOOLINFOW_V2_SIZE - 1;
603     tiW.hwnd = NULL;
604     tiW.uId = 0x1234ABCD;
605     SendMessageW(hwnd, TTM_DELTOOLW, 0, (LPARAM)&tiW);
606     r = SendMessageW(hwnd, TTM_GETTOOLCOUNT, 0, 0);
607     expect(0, r);
608
609     tiW.cbSize = TTTOOLINFOW_V2_SIZE + 1;
610     tiW.hwnd = NULL;
611     tiW.hinst = GetModuleHandleA(NULL);
612     tiW.uFlags = 0;
613     tiW.uId = 0x1234ABCD;
614     tiW.lpszText = NULL;
615     tiW.lParam = 0xdeadbeef;
616     GetClientRect(hwnd, &tiW.rect);
617     r = SendMessageW(hwnd, TTM_ADDTOOLA, 0, (LPARAM)&tiW);
618     ok(r, "Adding the tool to the tooltip failed\n");
619     r = SendMessageW(hwnd, TTM_GETTOOLCOUNT, 0, 0);
620     expect(1, r);
621     /* looks like TTM_DELTOOLW doesn't work with invalid size */
622     tiW.cbSize = TTTOOLINFOW_V2_SIZE + 1;
623     tiW.hwnd = NULL;
624     tiW.uId = 0x1234ABCD;
625     SendMessageW(hwnd, TTM_DELTOOLW, 0, (LPARAM)&tiW);
626     r = SendMessageW(hwnd, TTM_GETTOOLCOUNT, 0, 0);
627     expect(1, r);
628
629     tiW.cbSize = TTTOOLINFOW_V2_SIZE;
630     tiW.hwnd = NULL;
631     tiW.uId = 0x1234ABCD;
632     SendMessageW(hwnd, TTM_DELTOOLW, 0, (LPARAM)&tiW);
633     r = SendMessageW(hwnd, TTM_GETTOOLCOUNT, 0, 0);
634     expect(0, r);
635
636     DestroyWindow(hwnd);
637 }
638
639 static void test_longtextA(void)
640 {
641     static const char longtextA[] =
642         "According to MSDN, TTM_ENUMTOOLS claims that TOOLINFO's lpszText is maximum "
643         "80 chars including null. In fact, the buffer is not null-terminated.";
644     HWND hwnd;
645     TTTOOLINFOA toolinfoA = { 0 };
646     CHAR bufA[256];
647     LRESULT r;
648
649     hwnd = CreateWindowExA(0, TOOLTIPS_CLASSA, NULL, 0,
650                            10, 10, 300, 100,
651                            NULL, NULL, NULL, 0);
652     toolinfoA.cbSize = sizeof(TTTOOLINFOA);
653     toolinfoA.hwnd = NULL;
654     toolinfoA.hinst = GetModuleHandleA(NULL);
655     toolinfoA.uFlags = 0;
656     toolinfoA.uId = 0x1234ABCD;
657     strcpy(bufA, longtextA);
658     toolinfoA.lpszText = bufA;
659     toolinfoA.lParam = 0xdeadbeef;
660     GetClientRect(hwnd, &toolinfoA.rect);
661     r = SendMessageA(hwnd, TTM_ADDTOOLA, 0, (LPARAM)&toolinfoA);
662     if (r)
663     {
664         int textlen;
665         memset(bufA, 0, sizeof(bufA));
666         toolinfoA.hwnd = NULL;
667         toolinfoA.uId = 0xABCD1234;
668         toolinfoA.lpszText = bufA;
669         SendMessageA(hwnd, TTM_ENUMTOOLSA, 0, (LPARAM)&toolinfoA);
670         textlen = lstrlenA(toolinfoA.lpszText);
671         ok(textlen == 80, "lpszText has %d chars\n", textlen);
672         ok(toolinfoA.uId == 0x1234ABCD,
673            "uId should be retrieved, got %p\n", (void*)toolinfoA.uId);
674
675         memset(bufA, 0, sizeof(bufA));
676         toolinfoA.hwnd = NULL;
677         toolinfoA.uId = 0x1234ABCD;
678         toolinfoA.lpszText = bufA;
679         SendMessageA(hwnd, TTM_GETTOOLINFOA, 0, (LPARAM)&toolinfoA);
680         textlen = lstrlenA(toolinfoA.lpszText);
681         ok(textlen == 80, "lpszText has %d chars\n", textlen);
682
683         memset(bufA, 0, sizeof(bufA));
684         toolinfoA.hwnd = NULL;
685         toolinfoA.uId = 0x1234ABCD;
686         toolinfoA.lpszText = bufA;
687         SendMessageA(hwnd, TTM_GETTEXTA, 0, (LPARAM)&toolinfoA);
688         textlen = lstrlenA(toolinfoA.lpszText);
689         ok(textlen == 80, "lpszText has %d chars\n", textlen);
690     }
691
692     DestroyWindow(hwnd);
693 }
694
695 static void test_longtextW(void)
696 {
697     static const char longtextA[] =
698         "According to MSDN, TTM_ENUMTOOLS claims that TOOLINFO's lpszText is maximum "
699         "80 chars including null. Actually, this is not applied for wide version.";
700     HWND hwnd;
701     TTTOOLINFOW toolinfoW = { 0 };
702     WCHAR bufW[256];
703     LRESULT r;
704     int lenW;
705
706     hwnd = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, 0,
707                            10, 10, 300, 100,
708                            NULL, NULL, NULL, 0);
709     if(!hwnd)
710     {
711         win_skip("CreateWindowExW() not supported. Skipping.\n");
712         return;
713     }
714
715     toolinfoW.cbSize = TTTOOLINFOW_V2_SIZE;
716     toolinfoW.hwnd = NULL;
717     toolinfoW.hinst = GetModuleHandleW(NULL);
718     toolinfoW.uFlags = 0;
719     toolinfoW.uId = 0x1234ABCD;
720     MultiByteToWideChar(CP_ACP, 0, longtextA, -1, bufW, sizeof(bufW)/sizeof(bufW[0]));
721     lenW = lstrlenW(bufW);
722     toolinfoW.lpszText = bufW;
723     toolinfoW.lParam = 0xdeadbeef;
724     GetClientRect(hwnd, &toolinfoW.rect);
725     r = SendMessageW(hwnd, TTM_ADDTOOLW, 0, (LPARAM)&toolinfoW);
726     if (r)
727     {
728         int textlen;
729         memset(bufW, 0, sizeof(bufW));
730         toolinfoW.hwnd = NULL;
731         toolinfoW.uId = 0xABCD1234;
732         toolinfoW.lpszText = bufW;
733         SendMessageW(hwnd, TTM_ENUMTOOLSW, 0, (LPARAM)&toolinfoW);
734         textlen = lstrlenW(toolinfoW.lpszText);
735         ok(textlen == lenW, "lpszText has %d chars\n", textlen);
736         ok(toolinfoW.uId == 0x1234ABCD,
737            "uId should be retrieved, got %p\n", (void*)toolinfoW.uId);
738
739         memset(bufW, 0, sizeof(bufW));
740         toolinfoW.hwnd = NULL;
741         toolinfoW.uId = 0x1234ABCD;
742         toolinfoW.lpszText = bufW;
743         SendMessageW(hwnd, TTM_GETTOOLINFOW, 0, (LPARAM)&toolinfoW);
744         textlen = lstrlenW(toolinfoW.lpszText);
745         ok(textlen == lenW, "lpszText has %d chars\n", textlen);
746
747         memset(bufW, 0, sizeof(bufW));
748         toolinfoW.hwnd = NULL;
749         toolinfoW.uId = 0x1234ABCD;
750         toolinfoW.lpszText = bufW;
751         SendMessageW(hwnd, TTM_GETTEXTW, 0, (LPARAM)&toolinfoW);
752         textlen = lstrlenW(toolinfoW.lpszText);
753         ok(textlen == lenW ||
754            broken(textlen == 0 && toolinfoW.lpszText == NULL), /* nt4, kb186177 */
755            "lpszText has %d chars\n", textlen);
756     }
757
758     DestroyWindow(hwnd);
759 }
760
761 START_TEST(tooltips)
762 {
763     InitCommonControls();
764
765     test_create_tooltip();
766     test_customdraw();
767     test_gettext();
768     test_ttm_gettoolinfo();
769     test_longtextA();
770     test_longtextW();
771 }