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