82cd3c0445a156cb57dca62beef578970354b6f5
[reactos.git] / rostests / winetests / comctl32 / button.c
1 /* Unit test suite for Button control.
2 *
3 * Copyright 1999 Ove Kaaven
4 * Copyright 2003 Dimitrie O. Paun
5 * Copyright 2004, 2005 Dmitry Timoshkov
6 * Copyright 2014 Nikolay Sivov for CodeWeavers
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
23 //#include <windows.h>
24 #include <stdarg.h>
25 #include <windef.h>
26 #include <winbase.h>
27 #include <wingdi.h>
28 #include <winuser.h>
29 #include <commctrl.h>
30
31 #include "wine/test.h"
32 #include "v6util.h"
33 #include "msg.h"
34
35 #define IS_WNDPROC_HANDLE(x) (((ULONG_PTR)(x) >> 16) == (~0u >> 16))
36
37 static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
38 static BOOL (WINAPI *pRemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR);
39 static LRESULT (WINAPI *pDefSubclassProc)(HWND, UINT, WPARAM, LPARAM);
40
41 /****************** button message test *************************/
42 #define ID_BUTTON 0x000e
43
44 #define COMBINED_SEQ_INDEX 0
45 #define NUM_MSG_SEQUENCES 1
46
47 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
48
49 struct wndclass_redirect_data
50 {
51 ULONG size;
52 DWORD res;
53 ULONG name_len;
54 ULONG name_offset;
55 ULONG module_len;
56 ULONG module_offset;
57 };
58
59 /* returned pointer is valid as long as activation context is alive */
60 static WCHAR* get_versioned_classname(const WCHAR *name)
61 {
62 BOOL (WINAPI *pFindActCtxSectionStringW)(DWORD,const GUID *,ULONG,LPCWSTR,PACTCTX_SECTION_KEYED_DATA);
63 struct wndclass_redirect_data *wnddata;
64 ACTCTX_SECTION_KEYED_DATA data;
65 BOOL ret;
66
67 pFindActCtxSectionStringW = (void*)GetProcAddress(GetModuleHandleA("kernel32"), "FindActCtxSectionStringW");
68
69 memset(&data, 0, sizeof(data));
70 data.cbSize = sizeof(data);
71
72 ret = pFindActCtxSectionStringW(0, NULL,
73 ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION,
74 name, &data);
75 ok(ret, "got %d, error %u\n", ret, GetLastError());
76 wnddata = (struct wndclass_redirect_data*)data.lpData;
77 return (WCHAR*)((BYTE*)wnddata + wnddata->name_offset);
78 }
79
80 static void init_functions(void)
81 {
82 HMODULE hmod = GetModuleHandleA("comctl32.dll");
83 ok(hmod != NULL, "got %p\n", hmod);
84
85 #define MAKEFUNC_ORD(f, ord) (p##f = (void*)GetProcAddress(hmod, (LPSTR)(ord)))
86 MAKEFUNC_ORD(SetWindowSubclass, 410);
87 MAKEFUNC_ORD(RemoveWindowSubclass, 412);
88 MAKEFUNC_ORD(DefSubclassProc, 413);
89 #undef MAKEFUNC_ORD
90 }
91
92 /* try to make sure pending X events have been processed before continuing */
93 static void flush_events(void)
94 {
95 MSG msg;
96 int diff = 200;
97 int min_timeout = 100;
98 DWORD time = GetTickCount() + diff;
99
100 while (diff > 0)
101 {
102 if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min_timeout, QS_ALLINPUT ) == WAIT_TIMEOUT) break;
103 while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
104 diff = time - GetTickCount();
105 }
106 }
107
108 static BOOL ignore_message( UINT message )
109 {
110 /* these are always ignored */
111 return (message >= 0xc000 ||
112 message == WM_GETICON ||
113 message == WM_GETOBJECT ||
114 message == WM_TIMECHANGE ||
115 message == WM_DISPLAYCHANGE ||
116 message == WM_DEVICECHANGE ||
117 message == WM_DWMNCRENDERINGCHANGED ||
118 message == WM_GETTEXTLENGTH ||
119 message == WM_GETTEXT);
120 }
121
122 static LRESULT CALLBACK button_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR id, DWORD_PTR ref_data)
123 {
124 static LONG defwndproc_counter = 0;
125 LRESULT ret;
126 struct message msg;
127
128 if (ignore_message( message )) return pDefSubclassProc(hwnd, message, wParam, lParam);
129
130 switch (message)
131 {
132 case WM_SYNCPAINT:
133 break;
134 case BM_SETSTATE:
135 if (GetCapture())
136 ok(GetCapture() == hwnd, "GetCapture() = %p\n", GetCapture());
137 /* fall through */
138 default:
139 msg.message = message;
140 msg.flags = sent|wparam|lparam;
141 if (defwndproc_counter) msg.flags |= defwinproc;
142 msg.wParam = wParam;
143 msg.lParam = lParam;
144 add_message(sequences, COMBINED_SEQ_INDEX, &msg);
145 }
146
147 if (message == WM_NCDESTROY)
148 pRemoveWindowSubclass(hwnd, button_subclass_proc, 0);
149
150 defwndproc_counter++;
151 ret = pDefSubclassProc(hwnd, message, wParam, lParam);
152 defwndproc_counter--;
153
154 return ret;
155 }
156
157 static LRESULT WINAPI test_parent_wndproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
158 {
159 static LONG defwndproc_counter = 0;
160 static LONG beginpaint_counter = 0;
161 LRESULT ret;
162 struct message msg;
163
164 if (ignore_message( message )) return 0;
165
166 if (message == WM_PARENTNOTIFY || message == WM_CANCELMODE ||
167 message == WM_SETFOCUS || message == WM_KILLFOCUS ||
168 message == WM_ENABLE || message == WM_ENTERIDLE ||
169 message == WM_DRAWITEM || message == WM_COMMAND ||
170 message == WM_IME_SETCONTEXT)
171 {
172 switch (message)
173 {
174 /* ignore */
175 case WM_NCHITTEST:
176 return HTCLIENT;
177 case WM_SETCURSOR:
178 case WM_MOUSEMOVE:
179 case WM_NCMOUSEMOVE:
180 return 0;
181 }
182
183 msg.message = message;
184 msg.flags = sent|parent|wparam|lparam;
185 if (defwndproc_counter) msg.flags |= defwinproc;
186 if (beginpaint_counter) msg.flags |= beginpaint;
187 msg.wParam = wParam;
188 msg.lParam = lParam;
189 add_message(sequences, COMBINED_SEQ_INDEX, &msg);
190 }
191
192 if (message == WM_PAINT)
193 {
194 PAINTSTRUCT ps;
195 beginpaint_counter++;
196 BeginPaint( hwnd, &ps );
197 beginpaint_counter--;
198 EndPaint( hwnd, &ps );
199 return 0;
200 }
201
202 defwndproc_counter++;
203 ret = DefWindowProcA(hwnd, message, wParam, lParam);
204 defwndproc_counter--;
205
206 return ret;
207 }
208
209 static const struct message setfocus_seq[] =
210 {
211 { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
212 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
213 { WM_SETFOCUS, sent|wparam },
214 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) },
215 { WM_APP, sent|wparam|lparam },
216 { WM_PAINT, sent },
217 { 0 }
218 };
219
220 static const struct message killfocus_seq[] =
221 {
222 { WM_KILLFOCUS, sent|wparam, 0 },
223 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_KILLFOCUS) },
224 { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
225 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 1 },
226 { WM_APP, sent|wparam|lparam, 0, 0 },
227 { WM_PAINT, sent },
228 { 0 }
229 };
230
231 static const struct message setfocus_static_seq[] =
232 {
233 { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
234 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
235 { WM_SETFOCUS, sent|wparam, 0 },
236 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) },
237 { WM_COMMAND, sent|wparam|parent|optional, MAKEWPARAM(ID_BUTTON, BN_CLICKED) }, /* radio button */
238 { WM_APP, sent|wparam|lparam, 0, 0 },
239 { WM_PAINT, sent },
240 { 0 }
241 };
242
243 static const struct message setfocus_groupbox_seq[] =
244 {
245 { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
246 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
247 { WM_SETFOCUS, sent|wparam, 0 },
248 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) },
249 { WM_COMMAND, sent|wparam|parent|optional, MAKEWPARAM(ID_BUTTON, BN_CLICKED) }, /* radio button */
250 { WM_APP, sent|wparam|lparam, 0, 0 },
251 { WM_PAINT, sent },
252 { 0 }
253 };
254
255 static const struct message killfocus_static_seq[] =
256 {
257 { WM_KILLFOCUS, sent|wparam, 0 },
258 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_KILLFOCUS) },
259 { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
260 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 1 },
261 { WM_APP, sent|wparam|lparam, 0, 0 },
262 { WM_PAINT, sent },
263 { 0 }
264 };
265
266 static const struct message setfocus_ownerdraw_seq[] =
267 {
268 { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
269 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
270 { WM_SETFOCUS, sent|wparam, 0 },
271 { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
272 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) },
273 { WM_APP, sent|wparam|lparam, 0, 0 },
274 { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
275 { 0 }
276 };
277
278 static const struct message killfocus_ownerdraw_seq[] =
279 {
280 { WM_KILLFOCUS, sent|wparam, 0 },
281 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_KILLFOCUS) },
282 { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
283 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 1 },
284 { WM_APP, sent|wparam|lparam, 0, 0 },
285 { WM_PAINT, sent },
286 { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
287 { 0 }
288 };
289
290 static const struct message lbuttondown_seq[] =
291 {
292 { WM_LBUTTONDOWN, sent|wparam|lparam, 0, 0 },
293 { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
294 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
295 { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
296 { BM_SETSTATE, sent|wparam|defwinproc, TRUE },
297 { 0 }
298 };
299
300 static const struct message lbuttonup_seq[] =
301 {
302 { WM_LBUTTONUP, sent|wparam|lparam, 0, 0 },
303 { BM_SETSTATE, sent|wparam|defwinproc, FALSE },
304 { WM_CAPTURECHANGED, sent|wparam|defwinproc, 0 },
305 { WM_COMMAND, sent|wparam|defwinproc, 0 },
306 { 0 }
307 };
308
309 static const struct message setfont_seq[] =
310 {
311 { WM_SETFONT, sent },
312 { 0 }
313 };
314
315 static const struct message setstyle_seq[] =
316 {
317 { BM_SETSTYLE, sent },
318 { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
319 { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
320 { WM_APP, sent|wparam|lparam, 0, 0 },
321 { WM_PAINT, sent },
322 { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */
323 { WM_ERASEBKGND, sent|defwinproc|optional },
324 { 0 }
325 };
326
327 static const struct message setstyle_static_seq[] =
328 {
329 { BM_SETSTYLE, sent },
330 { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
331 { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
332 { WM_APP, sent|wparam|lparam, 0, 0 },
333 { WM_PAINT, sent },
334 { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */
335 { WM_ERASEBKGND, sent|defwinproc|optional },
336 { 0 }
337 };
338
339 static const struct message setstyle_user_seq[] =
340 {
341 { BM_SETSTYLE, sent },
342 { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
343 { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
344 { WM_APP, sent|wparam|lparam, 0, 0 },
345 { WM_PAINT, sent },
346 { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */
347 { WM_ERASEBKGND, sent|defwinproc|optional },
348 { 0 }
349 };
350
351 static const struct message setstyle_ownerdraw_seq[] =
352 {
353 { BM_SETSTYLE, sent },
354 { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
355 { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
356 { WM_APP, sent|wparam|lparam, 0, 0 },
357 { WM_PAINT, sent },
358 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
359 { WM_ERASEBKGND, sent|defwinproc|optional },
360 { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
361 { 0 }
362 };
363
364 static const struct message setstate_seq[] =
365 {
366 { BM_SETSTATE, sent },
367 { WM_APP, sent|wparam|lparam, 0, 0 },
368 { WM_PAINT, sent },
369 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
370 { WM_ERASEBKGND, sent|defwinproc|optional },
371 { 0 }
372 };
373
374 static const struct message setstate_static_seq[] =
375 {
376 { BM_SETSTATE, sent },
377 { WM_APP, sent|wparam|lparam, 0, 0 },
378 { WM_PAINT, sent },
379 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
380 { WM_ERASEBKGND, sent|defwinproc|optional },
381 { 0 }
382 };
383
384 static const struct message setstate_user_seq[] =
385 {
386 { BM_SETSTATE, sent },
387 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_HILITE) },
388 { WM_APP, sent|wparam|lparam, 0, 0 },
389 { WM_PAINT, sent },
390 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
391 { WM_ERASEBKGND, sent|defwinproc|optional },
392 { 0 }
393 };
394
395 static const struct message setstate_ownerdraw_seq[] =
396 {
397 { BM_SETSTATE, sent },
398 { WM_APP, sent|wparam|lparam, 0, 0 },
399 { WM_PAINT, sent },
400 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
401 { WM_ERASEBKGND, sent|defwinproc|optional },
402 { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
403 { 0 }
404 };
405
406 static const struct message clearstate_seq[] =
407 {
408 { BM_SETSTATE, sent },
409 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_UNHILITE) },
410 { WM_APP, sent|wparam|lparam, 0, 0 },
411 { WM_PAINT, sent },
412 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
413 { WM_ERASEBKGND, sent|defwinproc|optional },
414 { 0 }
415 };
416
417 static const struct message clearstate_ownerdraw_seq[] =
418 {
419 { BM_SETSTATE, sent },
420 { WM_APP, sent|wparam|lparam, 0, 0 },
421 { WM_PAINT, sent },
422 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
423 { WM_ERASEBKGND, sent|defwinproc|optional },
424 { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
425 { 0 }
426 };
427
428 static const struct message setcheck_ignored_seq[] =
429 {
430 { BM_SETCHECK, sent },
431 { WM_APP, sent|wparam|lparam, 0, 0 },
432 { 0 }
433 };
434
435 static const struct message setcheck_uncheck_seq[] =
436 {
437 { BM_SETCHECK, sent },
438 { WM_APP, sent|wparam|lparam, 0, 0 },
439 { 0 }
440 };
441
442 static const struct message setcheck_static_seq[] =
443 {
444 { BM_SETCHECK, sent },
445 { WM_APP, sent|wparam|lparam, 0, 0 },
446 { WM_PAINT, sent },
447 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
448 { WM_ERASEBKGND, sent|defwinproc|optional },
449 { 0 }
450 };
451
452 static const struct message setcheck_radio_seq[] =
453 {
454 { BM_SETCHECK, sent },
455 { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
456 { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
457 { WM_APP, sent|wparam|lparam, 0, 0 },
458 { 0 }
459 };
460
461 static const struct message setcheck_radio_redraw_seq[] =
462 {
463 { BM_SETCHECK, sent },
464 { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
465 { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
466 { WM_APP, sent|wparam|lparam, 0, 0 },
467 { WM_PAINT, sent },
468 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
469 { WM_ERASEBKGND, sent|defwinproc|optional },
470 { 0 }
471 };
472
473 static HWND create_button(DWORD style, HWND parent)
474 {
475 HMENU menuid = 0;
476 HWND hwnd;
477
478 if (parent)
479 {
480 style |= WS_CHILD|BS_NOTIFY;
481 menuid = (HMENU)ID_BUTTON;
482 }
483 hwnd = CreateWindowExA(0, "Button", "test", style, 0, 0, 50, 14, parent, menuid, 0, NULL);
484 ok(hwnd != NULL, "failed to create a button, 0x%08x, %p\n", style, parent);
485 pSetWindowSubclass(hwnd, button_subclass_proc, 0, 0);
486 return hwnd;
487 }
488
489 static void test_button_messages(void)
490 {
491 static const struct
492 {
493 DWORD style;
494 DWORD dlg_code;
495 const struct message *setfocus;
496 const struct message *killfocus;
497 const struct message *setstyle;
498 const struct message *setstate;
499 const struct message *clearstate;
500 const struct message *setcheck;
501 } button[] = {
502 { BS_PUSHBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
503 setfocus_seq, killfocus_seq, setstyle_seq,
504 setstate_seq, setstate_seq, setcheck_ignored_seq },
505 { BS_DEFPUSHBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON,
506 setfocus_seq, killfocus_seq, setstyle_seq,
507 setstate_seq, setstate_seq, setcheck_ignored_seq },
508 { BS_CHECKBOX, DLGC_BUTTON,
509 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
510 setstate_static_seq, setstate_static_seq, setcheck_static_seq },
511 { BS_AUTOCHECKBOX, DLGC_BUTTON,
512 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
513 setstate_static_seq, setstate_static_seq, setcheck_static_seq },
514 { BS_RADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
515 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
516 setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq },
517 { BS_3STATE, DLGC_BUTTON,
518 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
519 setstate_static_seq, setstate_static_seq, setcheck_static_seq },
520 { BS_AUTO3STATE, DLGC_BUTTON,
521 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
522 setstate_static_seq, setstate_static_seq, setcheck_static_seq },
523 { BS_GROUPBOX, DLGC_STATIC,
524 setfocus_groupbox_seq, killfocus_static_seq, setstyle_static_seq,
525 setstate_static_seq, setstate_static_seq, setcheck_ignored_seq },
526 { BS_USERBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
527 setfocus_seq, killfocus_seq, setstyle_user_seq,
528 setstate_user_seq, clearstate_seq, setcheck_ignored_seq },
529 { BS_AUTORADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
530 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
531 setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq },
532 { BS_OWNERDRAW, DLGC_BUTTON,
533 setfocus_ownerdraw_seq, killfocus_ownerdraw_seq, setstyle_ownerdraw_seq,
534 setstate_ownerdraw_seq, clearstate_ownerdraw_seq, setcheck_ignored_seq },
535 };
536 const struct message *seq;
537 unsigned int i;
538 HWND hwnd, parent;
539 DWORD dlg_code;
540 HFONT zfont;
541 BOOL todo;
542
543 /* selection with VK_SPACE should capture button window */
544 hwnd = create_button(BS_CHECKBOX | WS_VISIBLE | WS_POPUP, NULL);
545 ok(hwnd != 0, "Failed to create button window\n");
546 ReleaseCapture();
547 SetFocus(hwnd);
548 SendMessageA(hwnd, WM_KEYDOWN, VK_SPACE, 0);
549 ok(GetCapture() == hwnd, "Should be captured on VK_SPACE WM_KEYDOWN\n");
550 SendMessageA(hwnd, WM_KEYUP, VK_SPACE, 0);
551 DestroyWindow(hwnd);
552
553 parent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
554 100, 100, 200, 200, 0, 0, 0, NULL);
555 ok(parent != 0, "Failed to create parent window\n");
556
557 for (i = 0; i < sizeof(button)/sizeof(button[0]); i++)
558 {
559 MSG msg;
560 DWORD style, state;
561
562 trace("%d: button test sequence\n", i);
563 hwnd = create_button(button[i].style, parent);
564
565 style = GetWindowLongA(hwnd, GWL_STYLE);
566 style &= ~(WS_CHILD | BS_NOTIFY);
567 /* XP turns a BS_USERBUTTON into BS_PUSHBUTTON */
568 if (button[i].style == BS_USERBUTTON)
569 ok(style == BS_PUSHBUTTON, "expected style BS_PUSHBUTTON got %x\n", style);
570 else
571 ok(style == button[i].style, "expected style %x got %x\n", button[i].style, style);
572
573 dlg_code = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
574 ok(dlg_code == button[i].dlg_code, "%u: wrong dlg_code %08x\n", i, dlg_code);
575
576 ShowWindow(hwnd, SW_SHOW);
577 UpdateWindow(hwnd);
578 SetFocus(0);
579 flush_events();
580 SetFocus(0);
581 flush_sequences(sequences, NUM_MSG_SEQUENCES);
582
583 todo = button[i].style != BS_OWNERDRAW;
584 ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus());
585 SetFocus(hwnd);
586 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
587 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
588 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setfocus, "SetFocus(hwnd) on a button", todo);
589
590 todo = button[i].style == BS_OWNERDRAW;
591 SetFocus(0);
592 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
593 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
594 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].killfocus, "SetFocus(0) on a button", todo);
595 ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus());
596
597 SendMessageA(hwnd, BM_SETSTYLE, button[i].style | BS_BOTTOM, TRUE);
598 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
599 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
600 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setstyle, "BM_SETSTYLE on a button", TRUE);
601
602 style = GetWindowLongA(hwnd, GWL_STYLE);
603 style &= ~(WS_VISIBLE | WS_CHILD | BS_NOTIFY);
604 /* XP doesn't turn a BS_USERBUTTON into BS_PUSHBUTTON here! */
605 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
606
607 state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
608 ok(state == 0, "expected state 0, got %04x\n", state);
609
610 flush_sequences(sequences, NUM_MSG_SEQUENCES);
611
612 SendMessageA(hwnd, BM_SETSTATE, TRUE, 0);
613 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
614 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
615 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setstate, "BM_SETSTATE/TRUE on a button", TRUE);
616
617 state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
618 ok(state == BST_PUSHED, "expected state 0x0004, got %04x\n", state);
619
620 style = GetWindowLongA(hwnd, GWL_STYLE);
621 style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
622 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
623
624 flush_sequences(sequences, NUM_MSG_SEQUENCES);
625
626 SendMessageA(hwnd, BM_SETSTATE, FALSE, 0);
627 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
628 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
629 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].clearstate, "BM_SETSTATE/FALSE on a button", TRUE);
630
631 state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
632 ok(state == 0, "expected state 0, got %04x\n", state);
633
634 style = GetWindowLongA(hwnd, GWL_STYLE);
635 style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
636 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
637
638 state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
639 ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state);
640
641 flush_sequences(sequences, NUM_MSG_SEQUENCES);
642
643 if (button[i].style == BS_RADIOBUTTON ||
644 button[i].style == BS_AUTORADIOBUTTON)
645 {
646 seq = setcheck_radio_seq;
647 todo = TRUE;
648 }
649 else
650 {
651 seq = setcheck_ignored_seq;
652 todo = FALSE;
653 }
654 SendMessageA(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
655 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
656 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
657 ok_sequence(sequences, COMBINED_SEQ_INDEX, seq, "BM_SETCHECK on a button", todo);
658
659 state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
660 ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state);
661
662 style = GetWindowLongA(hwnd, GWL_STYLE);
663 style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
664 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
665
666 flush_sequences(sequences, NUM_MSG_SEQUENCES);
667
668 SendMessageA(hwnd, BM_SETCHECK, BST_CHECKED, 0);
669 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
670 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
671
672 if (button[i].style == BS_PUSHBUTTON ||
673 button[i].style == BS_DEFPUSHBUTTON ||
674 button[i].style == BS_GROUPBOX ||
675 button[i].style == BS_USERBUTTON ||
676 button[i].style == BS_OWNERDRAW)
677 {
678 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setcheck, "BM_SETCHECK on a button", FALSE);
679 state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
680 ok(state == BST_UNCHECKED, "expected check BST_UNCHECKED, got %04x\n", state);
681 }
682 else
683 {
684 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setcheck, "BM_SETCHECK on a button", TRUE);
685 state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
686 ok(state == BST_CHECKED, "expected check BST_CHECKED, got %04x\n", state);
687 }
688
689 style = GetWindowLongA(hwnd, GWL_STYLE);
690 style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
691 if (button[i].style == BS_RADIOBUTTON ||
692 button[i].style == BS_AUTORADIOBUTTON)
693 ok(style == (button[i].style | WS_TABSTOP), "expected style %04x | WS_TABSTOP got %04x\n", button[i].style, style);
694 else
695 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
696
697 DestroyWindow(hwnd);
698 }
699
700 DestroyWindow(parent);
701
702 hwnd = create_button(BS_PUSHBUTTON, NULL);
703
704 SetForegroundWindow(hwnd);
705 flush_events();
706
707 SetActiveWindow(hwnd);
708 SetFocus(0);
709 flush_sequences(sequences, NUM_MSG_SEQUENCES);
710
711 SendMessageA(hwnd, WM_LBUTTONDOWN, 0, 0);
712 ok_sequence(sequences, COMBINED_SEQ_INDEX, lbuttondown_seq, "WM_LBUTTONDOWN on a button", FALSE);
713
714 SendMessageA(hwnd, WM_LBUTTONUP, 0, 0);
715 ok_sequence(sequences, COMBINED_SEQ_INDEX, lbuttonup_seq, "WM_LBUTTONUP on a button", TRUE);
716
717 flush_sequences(sequences, NUM_MSG_SEQUENCES);
718 zfont = GetStockObject(SYSTEM_FONT);
719 SendMessageA(hwnd, WM_SETFONT, (WPARAM)zfont, TRUE);
720 UpdateWindow(hwnd);
721 ok_sequence(sequences, COMBINED_SEQ_INDEX, setfont_seq, "WM_SETFONT on a button", FALSE);
722
723 DestroyWindow(hwnd);
724 }
725
726 static void test_button_class(void)
727 {
728 static const WCHAR testW[] = {'t','e','s','t',0};
729 WNDCLASSEXW exW, ex2W;
730 WNDCLASSEXA exA;
731 char buffA[100];
732 WCHAR *nameW;
733 HWND hwnd;
734 BOOL ret;
735 int len;
736
737 ret = GetClassInfoExA(NULL, WC_BUTTONA, &exA);
738 ok(ret, "got %d\n", ret);
739 todo_wine
740 ok(IS_WNDPROC_HANDLE(exA.lpfnWndProc), "got %p\n", exA.lpfnWndProc);
741
742 ret = GetClassInfoExW(NULL, WC_BUTTONW, &exW);
743 ok(ret, "got %d\n", ret);
744 ok(!IS_WNDPROC_HANDLE(exW.lpfnWndProc), "got %p\n", exW.lpfnWndProc);
745
746 /* check that versioned class is also accessible */
747 nameW = get_versioned_classname(WC_BUTTONW);
748 ok(lstrcmpW(nameW, WC_BUTTONW), "got %s\n", wine_dbgstr_w(nameW));
749
750 ret = GetClassInfoExW(NULL, nameW, &ex2W);
751 todo_wine {
752 ok(ret, "got %d\n", ret);
753 ok(ex2W.lpfnWndProc == exW.lpfnWndProc, "got %p, %p\n", exW.lpfnWndProc, ex2W.lpfnWndProc);
754 }
755
756 /* Check reported class name */
757 hwnd = create_button(BS_CHECKBOX, NULL);
758 len = GetClassNameA(hwnd, buffA, sizeof(buffA));
759 ok(len == strlen(buffA), "got %d\n", len);
760 ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
761
762 len = RealGetWindowClassA(hwnd, buffA, sizeof(buffA));
763 ok(len == strlen(buffA), "got %d\n", len);
764 ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
765 DestroyWindow(hwnd);
766
767 /* explicitely create with versioned class name */
768 hwnd = CreateWindowExW(0, nameW, testW, BS_CHECKBOX, 0, 0, 50, 14, NULL, 0, 0, NULL);
769 todo_wine
770 ok(hwnd != NULL, "failed to create a window %s\n", wine_dbgstr_w(nameW));
771 if (hwnd)
772 {
773 len = GetClassNameA(hwnd, buffA, sizeof(buffA));
774 ok(len == strlen(buffA), "got %d\n", len);
775 ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
776
777 len = RealGetWindowClassA(hwnd, buffA, sizeof(buffA));
778 ok(len == strlen(buffA), "got %d\n", len);
779 ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
780
781 DestroyWindow(hwnd);
782 }
783 }
784
785 static void register_parent_class(void)
786 {
787 WNDCLASSA cls;
788
789 cls.style = 0;
790 cls.lpfnWndProc = test_parent_wndproc;
791 cls.cbClsExtra = 0;
792 cls.cbWndExtra = 0;
793 cls.hInstance = GetModuleHandleA(0);
794 cls.hIcon = 0;
795 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
796 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
797 cls.lpszMenuName = NULL;
798 cls.lpszClassName = "TestParentClass";
799 RegisterClassA(&cls);
800 }
801
802 START_TEST(button)
803 {
804 ULONG_PTR ctx_cookie;
805 HANDLE hCtx;
806
807 if (!load_v6_module(&ctx_cookie, &hCtx))
808 return;
809
810 register_parent_class();
811
812 init_functions();
813 init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
814
815 test_button_class();
816 test_button_messages();
817
818 unload_v6_module(ctx_cookie, hCtx);
819 }