fltlib: Add a stub dll.
[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 static void test_create_tooltip(void)
27 {
28     HWND parent, hwnd;
29     DWORD style, exp_style;
30
31     parent = CreateWindowEx(0, "static", NULL, WS_POPUP,
32                           0, 0, 0, 0,
33                           NULL, NULL, NULL, 0);
34     assert(parent);
35
36     hwnd = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, 0x7fffffff | WS_POPUP,
37                           10, 10, 300, 100,
38                           parent, NULL, NULL, 0);
39     assert(hwnd);
40
41     style = GetWindowLong(hwnd, GWL_STYLE);
42     trace("style = %08x\n", style);
43     exp_style = 0x7fffffff | WS_POPUP;
44     exp_style &= ~(WS_CHILD | WS_MAXIMIZE | WS_BORDER | WS_DLGFRAME);
45     ok(style == exp_style || broken(style == (exp_style | WS_BORDER)), /* nt4 */
46        "wrong style %08x/%08x\n", style, exp_style);
47
48     DestroyWindow(hwnd);
49
50     hwnd = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, 0,
51                           10, 10, 300, 100,
52                           parent, NULL, NULL, 0);
53     assert(hwnd);
54
55     style = GetWindowLong(hwnd, GWL_STYLE);
56     trace("style = %08x\n", style);
57     ok(style == (WS_POPUP | WS_CLIPSIBLINGS | WS_BORDER),
58        "wrong style %08x\n", style);
59
60     DestroyWindow(hwnd);
61
62     DestroyWindow(parent);
63 }
64
65 /* try to make sure pending X events have been processed before continuing */
66 static void flush_events(int waitTime)
67 {
68     MSG msg;
69     int diff = waitTime;
70     DWORD time = GetTickCount() + waitTime;
71
72     while (diff > 0)
73     {
74         if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min(100,diff), QS_ALLEVENTS) == WAIT_TIMEOUT) break;
75         while (PeekMessage( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessage( &msg );
76         diff = time - GetTickCount();
77     }
78 }
79
80 static int CD_Stages;
81 static LRESULT CD_Result;
82 static HWND g_hwnd;
83
84 #define TEST_CDDS_PREPAINT           0x00000001
85 #define TEST_CDDS_POSTPAINT          0x00000002
86 #define TEST_CDDS_PREERASE           0x00000004
87 #define TEST_CDDS_POSTERASE          0x00000008
88 #define TEST_CDDS_ITEMPREPAINT       0x00000010
89 #define TEST_CDDS_ITEMPOSTPAINT      0x00000020
90 #define TEST_CDDS_ITEMPREERASE       0x00000040
91 #define TEST_CDDS_ITEMPOSTERASE      0x00000080
92 #define TEST_CDDS_SUBITEM            0x00000100
93
94 static LRESULT CALLBACK CustomDrawWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
95 {
96     switch(msg) {
97
98     case WM_DESTROY:
99         PostQuitMessage(0);
100         break;
101
102     case WM_NOTIFY:
103         if (((NMHDR *)lParam)->code == NM_CUSTOMDRAW) {
104             NMTTCUSTOMDRAW *ttcd = (NMTTCUSTOMDRAW*) lParam;
105             ok(ttcd->nmcd.hdr.hwndFrom == g_hwnd, "Unexpected hwnd source %p (%p)\n",
106                  ttcd->nmcd.hdr.hwndFrom, g_hwnd);
107             ok(ttcd->nmcd.hdr.idFrom == 0x1234ABCD, "Unexpected id %x\n", (int)ttcd->nmcd.hdr.idFrom);
108
109             switch (ttcd->nmcd.dwDrawStage) {
110             case CDDS_PREPAINT     : CD_Stages |= TEST_CDDS_PREPAINT; break;
111             case CDDS_POSTPAINT    : CD_Stages |= TEST_CDDS_POSTPAINT; break;
112             case CDDS_PREERASE     : CD_Stages |= TEST_CDDS_PREERASE; break;
113             case CDDS_POSTERASE    : CD_Stages |= TEST_CDDS_POSTERASE; break;
114             case CDDS_ITEMPREPAINT : CD_Stages |= TEST_CDDS_ITEMPREPAINT; break;
115             case CDDS_ITEMPOSTPAINT: CD_Stages |= TEST_CDDS_ITEMPOSTPAINT; break;
116             case CDDS_ITEMPREERASE : CD_Stages |= TEST_CDDS_ITEMPREERASE; break;
117             case CDDS_ITEMPOSTERASE: CD_Stages |= TEST_CDDS_ITEMPOSTERASE; break;
118             case CDDS_SUBITEM      : CD_Stages |= TEST_CDDS_SUBITEM; break;
119             default: CD_Stages = -1;
120             }
121
122             if (ttcd->nmcd.dwDrawStage == CDDS_PREPAINT) return CD_Result;
123         }
124         /* drop through */
125
126     default:
127         return DefWindowProcA(hWnd, msg, wParam, lParam);
128     }
129
130     return 0L;
131 }
132
133 static void test_customdraw(void) {
134     static struct {
135         LRESULT FirstReturnValue;
136         int ExpectedCalls;
137     } expectedResults[] = {
138         /* Valid notification responses */
139         {CDRF_DODEFAULT, TEST_CDDS_PREPAINT},
140         {CDRF_SKIPDEFAULT, TEST_CDDS_PREPAINT},
141         {CDRF_NOTIFYPOSTPAINT, TEST_CDDS_PREPAINT | TEST_CDDS_POSTPAINT},
142
143         /* Invalid notification responses */
144         {CDRF_NOTIFYITEMDRAW, TEST_CDDS_PREPAINT},
145         {CDRF_NOTIFYPOSTERASE, TEST_CDDS_PREPAINT},
146         {CDRF_NEWFONT, TEST_CDDS_PREPAINT}
147     };
148
149    int       iterationNumber;
150    WNDCLASSA wc;
151    LRESULT   lResult;
152
153    /* Create a class to use the custom draw wndproc */
154    wc.style = CS_HREDRAW | CS_VREDRAW;
155    wc.cbClsExtra = 0;
156    wc.cbWndExtra = 0;
157    wc.hInstance = GetModuleHandleA(NULL);
158    wc.hIcon = NULL;
159    wc.hCursor = LoadCursorA(NULL, IDC_ARROW);
160    wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
161    wc.lpszMenuName = NULL;
162    wc.lpszClassName = "CustomDrawClass";
163    wc.lpfnWndProc = CustomDrawWndProc;
164    RegisterClass(&wc);
165
166    for (iterationNumber = 0;
167         iterationNumber < sizeof(expectedResults)/sizeof(expectedResults[0]);
168         iterationNumber++) {
169
170        HWND parent, hwndTip;
171        RECT rect;
172        TOOLINFO toolInfo = { 0 };
173
174        /* Create a main window */
175        parent = CreateWindowEx(0, "CustomDrawClass", NULL,
176                                WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
177                                WS_MAXIMIZEBOX | WS_VISIBLE,
178                                50, 50,
179                                300, 300,
180                                NULL, NULL, NULL, 0);
181        ok(parent != NULL, "Creation of main window failed\n");
182
183        /* Make it show */
184        ShowWindow(parent, SW_SHOWNORMAL);
185        flush_events(100);
186
187        /* Create Tooltip */
188        hwndTip = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS,
189                                 NULL, TTS_NOPREFIX | TTS_ALWAYSTIP,
190                                 CW_USEDEFAULT, CW_USEDEFAULT,
191                                 CW_USEDEFAULT, CW_USEDEFAULT,
192                                 parent, NULL, GetModuleHandleA(NULL), 0);
193        ok(hwndTip != NULL, "Creation of tooltip window failed\n");
194
195        /* Set up parms for the wndproc to handle */
196        CD_Stages = 0;
197        CD_Result = expectedResults[iterationNumber].FirstReturnValue;
198        g_hwnd    = hwndTip;
199
200        /* Make it topmost, as per the MSDN */
201        SetWindowPos(hwndTip, HWND_TOPMOST, 0, 0, 0, 0,
202              SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
203
204        /* Create a tool */
205        toolInfo.cbSize = TTTOOLINFO_V1_SIZE;
206        toolInfo.hwnd = parent;
207        toolInfo.hinst = GetModuleHandleA(NULL);
208        toolInfo.uFlags = TTF_SUBCLASS;
209        toolInfo.uId = 0x1234ABCD;
210        toolInfo.lpszText = (LPSTR)"This is a test tooltip";
211        toolInfo.lParam = 0xdeadbeef;
212        GetClientRect (parent, &toolInfo.rect);
213        lResult = SendMessage(hwndTip, TTM_ADDTOOL, 0, (LPARAM)&toolInfo);
214        ok(lResult, "Adding the tool to the tooltip failed\n");
215
216        /* Make tooltip appear quickly */
217        SendMessage(hwndTip, TTM_SETDELAYTIME, TTDT_INITIAL, MAKELPARAM(1,0));
218
219        /* Put cursor inside window, tooltip will appear immediately */
220        GetWindowRect( parent, &rect );
221        SetCursorPos( (rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2 );
222        flush_events(200);
223
224        if (CD_Stages)
225        {
226            /* Check CustomDraw results */
227            ok(CD_Stages == expectedResults[iterationNumber].ExpectedCalls ||
228               broken(CD_Stages == (expectedResults[iterationNumber].ExpectedCalls & ~TEST_CDDS_POSTPAINT)), /* nt4 */
229               "CustomDraw run %d stages %x, expected %x\n", iterationNumber, CD_Stages,
230               expectedResults[iterationNumber].ExpectedCalls);
231        }
232
233        /* Clean up */
234        DestroyWindow(hwndTip);
235        DestroyWindow(parent);
236    }
237
238
239 }
240
241 static const CHAR testcallbackA[]  = "callback";
242
243 static LRESULT WINAPI parent_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
244 {
245     if (message == WM_NOTIFY && lParam)
246     {
247         NMTTDISPINFOA *ttnmdi = (NMTTDISPINFOA*)lParam;
248
249         if (ttnmdi->hdr.code == TTN_GETDISPINFOA)
250             lstrcpy(ttnmdi->lpszText, testcallbackA);
251     }
252
253     return DefWindowProcA(hwnd, message, wParam, lParam);
254 }
255
256 static BOOL register_parent_wnd_class(void)
257 {
258     WNDCLASSA cls;
259
260     cls.style = 0;
261     cls.lpfnWndProc = parent_wnd_proc;
262     cls.cbClsExtra = 0;
263     cls.cbWndExtra = 0;
264     cls.hInstance = GetModuleHandleA(NULL);
265     cls.hIcon = 0;
266     cls.hCursor = LoadCursorA(0, IDC_ARROW);
267     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
268     cls.lpszMenuName = NULL;
269     cls.lpszClassName = "Tooltips test parent class";
270     return RegisterClassA(&cls);
271 }
272
273 static HWND create_parent_window(void)
274 {
275     if (!register_parent_wnd_class())
276         return NULL;
277
278     return CreateWindowEx(0, "Tooltips test parent class",
279                           "Tooltips test parent window",
280                           WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX |
281                           WS_MAXIMIZEBOX | WS_VISIBLE,
282                           0, 0, 100, 100,
283                           GetDesktopWindow(), NULL, GetModuleHandleA(NULL), NULL);
284 }
285
286 static void test_gettext(void)
287 {
288     HWND hwnd, notify;
289     TTTOOLINFOA toolinfoA;
290     TTTOOLINFOW toolinfoW;
291     LRESULT r;
292     CHAR bufA[10] = "";
293     WCHAR bufW[10] = { 0 };
294     static const CHAR testtipA[] = "testtip";
295
296     notify = create_parent_window();
297     ok(notify != NULL, "Expected notification window to be created\n");
298
299     /* For bug 14790 - lpszText is NULL */
300     hwnd = CreateWindowExA(0, TOOLTIPS_CLASSA, NULL, 0,
301                            10, 10, 300, 100,
302                            NULL, NULL, NULL, 0);
303     assert(hwnd);
304
305     /* use sizeof(TTTOOLINFOA) instead of TTTOOLINFOA_V1_SIZE so that adding it fails on Win9x */
306     /* otherwise it crashes on the NULL lpszText */
307     toolinfoA.cbSize = sizeof(TTTOOLINFOA);
308     toolinfoA.hwnd = NULL;
309     toolinfoA.hinst = GetModuleHandleA(NULL);
310     toolinfoA.uFlags = 0;
311     toolinfoA.uId = 0x1234ABCD;
312     toolinfoA.lpszText = NULL;
313     toolinfoA.lParam = 0xdeadbeef;
314     GetClientRect(hwnd, &toolinfoA.rect);
315     r = SendMessageA(hwnd, TTM_ADDTOOL, 0, (LPARAM)&toolinfoA);
316     if (r)
317     {
318         toolinfoA.hwnd = NULL;
319         toolinfoA.uId = 0x1234ABCD;
320         toolinfoA.lpszText = bufA;
321         SendMessageA(hwnd, TTM_GETTEXTA, 0, (LPARAM)&toolinfoA);
322         ok(strcmp(toolinfoA.lpszText, "") == 0, "lpszText should be an empty string\n");
323     }
324     else
325     {
326         win_skip( "Old comctl32, not testing NULL text\n" );
327         DestroyWindow( hwnd );
328         return;
329     }
330
331     /* add another tool with text */
332     toolinfoA.cbSize = sizeof(TTTOOLINFOA);
333     toolinfoA.hwnd = NULL;
334     toolinfoA.hinst = GetModuleHandleA(NULL);
335     toolinfoA.uFlags = 0;
336     toolinfoA.uId = 0x1235ABCD;
337     strcpy(bufA, testtipA);
338     toolinfoA.lpszText = bufA;
339     toolinfoA.lParam = 0xdeadbeef;
340     GetClientRect(hwnd, &toolinfoA.rect);
341     r = SendMessageA(hwnd, TTM_ADDTOOL, 0, (LPARAM)&toolinfoA);
342     ok(r, "Adding the tool to the tooltip failed\n");
343     if (r)
344     {
345         DWORD length;
346
347         length = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0);
348         ok(length == 0, "Expected 0, got %d\n", length);
349
350         toolinfoA.hwnd = NULL;
351         toolinfoA.uId = 0x1235ABCD;
352         toolinfoA.lpszText = bufA;
353         SendMessageA(hwnd, TTM_GETTEXTA, 0, (LPARAM)&toolinfoA);
354         ok(strcmp(toolinfoA.lpszText, testtipA) == 0, "lpszText should be an empty string\n");
355
356         length = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, 0);
357         ok(length == 0, "Expected 0, got %d\n", length);
358     }
359
360     /* add another with callback text */
361     toolinfoA.cbSize = sizeof(TTTOOLINFOA);
362     toolinfoA.hwnd = notify;
363     toolinfoA.hinst = GetModuleHandleA(NULL);
364     toolinfoA.uFlags = 0;
365     toolinfoA.uId = 0x1236ABCD;
366     toolinfoA.lpszText = LPSTR_TEXTCALLBACKA;
367     toolinfoA.lParam = 0xdeadbeef;
368     GetClientRect(hwnd, &toolinfoA.rect);
369     r = SendMessageA(hwnd, TTM_ADDTOOL, 0, (LPARAM)&toolinfoA);
370     ok(r, "Adding the tool to the tooltip failed\n");
371     if (r)
372     {
373         toolinfoA.hwnd = notify;
374         toolinfoA.uId = 0x1236ABCD;
375         toolinfoA.lpszText = bufA;
376         SendMessageA(hwnd, TTM_GETTEXTA, 0, (LPARAM)&toolinfoA);
377         ok(strcmp(toolinfoA.lpszText, testcallbackA) == 0,
378            "lpszText should be an (%s) string\n", testcallbackA);
379     }
380
381     DestroyWindow(hwnd);
382     DestroyWindow(notify);
383
384     SetLastError(0xdeadbeef);
385     hwnd = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, 0,
386                            10, 10, 300, 100,
387                            NULL, NULL, NULL, 0);
388
389     if (!hwnd && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
390         win_skip("CreateWindowExW is not implemented\n");
391         return;
392     }
393
394     assert(hwnd);
395
396     toolinfoW.cbSize = sizeof(TTTOOLINFOW);
397     toolinfoW.hwnd = NULL;
398     toolinfoW.hinst = GetModuleHandleA(NULL);
399     toolinfoW.uFlags = 0;
400     toolinfoW.uId = 0x1234ABCD;
401     toolinfoW.lpszText = NULL;
402     toolinfoW.lParam = 0xdeadbeef;
403     GetClientRect(hwnd, &toolinfoW.rect);
404     r = SendMessageW(hwnd, TTM_ADDTOOL, 0, (LPARAM)&toolinfoW);
405     ok(r, "Adding the tool to the tooltip failed\n");
406
407     if (0)  /* crashes on NT4 */
408     {
409         toolinfoW.hwnd = NULL;
410         toolinfoW.uId = 0x1234ABCD;
411         toolinfoW.lpszText = bufW;
412         SendMessageW(hwnd, TTM_GETTEXTW, 0, (LPARAM)&toolinfoW);
413         ok(toolinfoW.lpszText[0] == 0, "lpszText should be an empty string\n");
414     }
415
416     DestroyWindow(hwnd);
417 }
418
419 START_TEST(tooltips)
420 {
421     InitCommonControls();
422
423     test_create_tooltip();
424     test_customdraw();
425     test_gettext();
426 }