f368bfcb650a5a96c80e609c509736b181e29974
[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_static_seq[] =
436 {
437 { BM_SETCHECK, sent },
438 { WM_APP, sent|wparam|lparam, 0, 0 },
439 { WM_PAINT, sent },
440 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
441 { WM_ERASEBKGND, sent|defwinproc|optional },
442 { 0 }
443 };
444
445 static const struct message setcheck_radio_seq[] =
446 {
447 { BM_SETCHECK, sent },
448 { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
449 { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
450 { WM_APP, sent|wparam|lparam, 0, 0 },
451 { 0 }
452 };
453
454 static const struct message setcheck_radio_redraw_seq[] =
455 {
456 { BM_SETCHECK, sent },
457 { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
458 { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
459 { WM_APP, sent|wparam|lparam, 0, 0 },
460 { WM_PAINT, sent },
461 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
462 { WM_ERASEBKGND, sent|defwinproc|optional },
463 { 0 }
464 };
465
466 static HWND create_button(DWORD style, HWND parent)
467 {
468 HMENU menuid = 0;
469 HWND hwnd;
470
471 if (parent)
472 {
473 style |= WS_CHILD|BS_NOTIFY;
474 menuid = (HMENU)ID_BUTTON;
475 }
476 hwnd = CreateWindowExA(0, "Button", "test", style, 0, 0, 50, 14, parent, menuid, 0, NULL);
477 ok(hwnd != NULL, "failed to create a button, 0x%08x, %p\n", style, parent);
478 pSetWindowSubclass(hwnd, button_subclass_proc, 0, 0);
479 return hwnd;
480 }
481
482 static void test_button_messages(void)
483 {
484 static const struct
485 {
486 DWORD style;
487 DWORD dlg_code;
488 const struct message *setfocus;
489 const struct message *killfocus;
490 const struct message *setstyle;
491 const struct message *setstate;
492 const struct message *clearstate;
493 const struct message *setcheck;
494 } button[] = {
495 { BS_PUSHBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
496 setfocus_seq, killfocus_seq, setstyle_seq,
497 setstate_seq, setstate_seq, setcheck_ignored_seq },
498 { BS_DEFPUSHBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON,
499 setfocus_seq, killfocus_seq, setstyle_seq,
500 setstate_seq, setstate_seq, setcheck_ignored_seq },
501 { BS_CHECKBOX, DLGC_BUTTON,
502 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
503 setstate_static_seq, setstate_static_seq, setcheck_static_seq },
504 { BS_AUTOCHECKBOX, DLGC_BUTTON,
505 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
506 setstate_static_seq, setstate_static_seq, setcheck_static_seq },
507 { BS_RADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
508 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
509 setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq },
510 { BS_3STATE, DLGC_BUTTON,
511 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
512 setstate_static_seq, setstate_static_seq, setcheck_static_seq },
513 { BS_AUTO3STATE, DLGC_BUTTON,
514 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
515 setstate_static_seq, setstate_static_seq, setcheck_static_seq },
516 { BS_GROUPBOX, DLGC_STATIC,
517 setfocus_groupbox_seq, killfocus_static_seq, setstyle_static_seq,
518 setstate_static_seq, setstate_static_seq, setcheck_ignored_seq },
519 { BS_USERBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
520 setfocus_seq, killfocus_seq, setstyle_user_seq,
521 setstate_user_seq, clearstate_seq, setcheck_ignored_seq },
522 { BS_AUTORADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
523 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
524 setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq },
525 { BS_OWNERDRAW, DLGC_BUTTON,
526 setfocus_ownerdraw_seq, killfocus_ownerdraw_seq, setstyle_ownerdraw_seq,
527 setstate_ownerdraw_seq, clearstate_ownerdraw_seq, setcheck_ignored_seq },
528 };
529 const struct message *seq;
530 unsigned int i;
531 HWND hwnd, parent;
532 DWORD dlg_code;
533 HFONT zfont;
534 BOOL todo;
535
536 /* selection with VK_SPACE should capture button window */
537 hwnd = create_button(BS_CHECKBOX | WS_VISIBLE | WS_POPUP, NULL);
538 ok(hwnd != 0, "Failed to create button window\n");
539 ReleaseCapture();
540 SetFocus(hwnd);
541 SendMessageA(hwnd, WM_KEYDOWN, VK_SPACE, 0);
542 ok(GetCapture() == hwnd, "Should be captured on VK_SPACE WM_KEYDOWN\n");
543 SendMessageA(hwnd, WM_KEYUP, VK_SPACE, 0);
544 DestroyWindow(hwnd);
545
546 parent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
547 100, 100, 200, 200, 0, 0, 0, NULL);
548 ok(parent != 0, "Failed to create parent window\n");
549
550 for (i = 0; i < sizeof(button)/sizeof(button[0]); i++)
551 {
552 MSG msg;
553 DWORD style, state;
554
555 trace("%d: button test sequence\n", i);
556 hwnd = create_button(button[i].style, parent);
557
558 style = GetWindowLongA(hwnd, GWL_STYLE);
559 style &= ~(WS_CHILD | BS_NOTIFY);
560 /* XP turns a BS_USERBUTTON into BS_PUSHBUTTON */
561 if (button[i].style == BS_USERBUTTON)
562 ok(style == BS_PUSHBUTTON, "expected style BS_PUSHBUTTON got %x\n", style);
563 else
564 ok(style == button[i].style, "expected style %x got %x\n", button[i].style, style);
565
566 dlg_code = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
567 ok(dlg_code == button[i].dlg_code, "%u: wrong dlg_code %08x\n", i, dlg_code);
568
569 ShowWindow(hwnd, SW_SHOW);
570 UpdateWindow(hwnd);
571 SetFocus(0);
572 flush_events();
573 SetFocus(0);
574 flush_sequences(sequences, NUM_MSG_SEQUENCES);
575
576 todo = button[i].style != BS_OWNERDRAW;
577 ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus());
578 SetFocus(hwnd);
579 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
580 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
581 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setfocus, "SetFocus(hwnd) on a button", todo);
582
583 todo = button[i].style == BS_OWNERDRAW;
584 SetFocus(0);
585 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
586 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
587 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].killfocus, "SetFocus(0) on a button", todo);
588 ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus());
589
590 SendMessageA(hwnd, BM_SETSTYLE, button[i].style | BS_BOTTOM, TRUE);
591 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
592 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
593 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setstyle, "BM_SETSTYLE on a button", TRUE);
594
595 style = GetWindowLongA(hwnd, GWL_STYLE);
596 style &= ~(WS_VISIBLE | WS_CHILD | BS_NOTIFY);
597 /* XP doesn't turn a BS_USERBUTTON into BS_PUSHBUTTON here! */
598 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
599
600 state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
601 ok(state == 0, "expected state 0, got %04x\n", state);
602
603 flush_sequences(sequences, NUM_MSG_SEQUENCES);
604
605 SendMessageA(hwnd, BM_SETSTATE, TRUE, 0);
606 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
607 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
608 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setstate, "BM_SETSTATE/TRUE on a button", TRUE);
609
610 state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
611 ok(state == BST_PUSHED, "expected state 0x0004, got %04x\n", state);
612
613 style = GetWindowLongA(hwnd, GWL_STYLE);
614 style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
615 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
616
617 flush_sequences(sequences, NUM_MSG_SEQUENCES);
618
619 SendMessageA(hwnd, BM_SETSTATE, FALSE, 0);
620 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
621 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
622 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].clearstate, "BM_SETSTATE/FALSE on a button", TRUE);
623
624 state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
625 ok(state == 0, "expected state 0, got %04x\n", state);
626
627 style = GetWindowLongA(hwnd, GWL_STYLE);
628 style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
629 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
630
631 state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
632 ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state);
633
634 flush_sequences(sequences, NUM_MSG_SEQUENCES);
635
636 if (button[i].style == BS_RADIOBUTTON ||
637 button[i].style == BS_AUTORADIOBUTTON)
638 {
639 seq = setcheck_radio_seq;
640 todo = TRUE;
641 }
642 else
643 {
644 seq = setcheck_ignored_seq;
645 todo = FALSE;
646 }
647 SendMessageA(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
648 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
649 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
650 ok_sequence(sequences, COMBINED_SEQ_INDEX, seq, "BM_SETCHECK on a button", todo);
651
652 state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
653 ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state);
654
655 style = GetWindowLongA(hwnd, GWL_STYLE);
656 style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
657 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
658
659 flush_sequences(sequences, NUM_MSG_SEQUENCES);
660
661 SendMessageA(hwnd, BM_SETCHECK, BST_CHECKED, 0);
662 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
663 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
664
665 if (button[i].style == BS_PUSHBUTTON ||
666 button[i].style == BS_DEFPUSHBUTTON ||
667 button[i].style == BS_GROUPBOX ||
668 button[i].style == BS_USERBUTTON ||
669 button[i].style == BS_OWNERDRAW)
670 {
671 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setcheck, "BM_SETCHECK on a button", FALSE);
672 state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
673 ok(state == BST_UNCHECKED, "expected check BST_UNCHECKED, got %04x\n", state);
674 }
675 else
676 {
677 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setcheck, "BM_SETCHECK on a button", TRUE);
678 state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
679 ok(state == BST_CHECKED, "expected check BST_CHECKED, got %04x\n", state);
680 }
681
682 style = GetWindowLongA(hwnd, GWL_STYLE);
683 style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
684 if (button[i].style == BS_RADIOBUTTON ||
685 button[i].style == BS_AUTORADIOBUTTON)
686 ok(style == (button[i].style | WS_TABSTOP), "expected style %04x | WS_TABSTOP got %04x\n", button[i].style, style);
687 else
688 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
689
690 DestroyWindow(hwnd);
691 }
692
693 DestroyWindow(parent);
694
695 hwnd = create_button(BS_PUSHBUTTON, NULL);
696
697 SetForegroundWindow(hwnd);
698 flush_events();
699
700 SetActiveWindow(hwnd);
701 SetFocus(0);
702 flush_sequences(sequences, NUM_MSG_SEQUENCES);
703
704 SendMessageA(hwnd, WM_LBUTTONDOWN, 0, 0);
705 ok_sequence(sequences, COMBINED_SEQ_INDEX, lbuttondown_seq, "WM_LBUTTONDOWN on a button", FALSE);
706
707 SendMessageA(hwnd, WM_LBUTTONUP, 0, 0);
708 ok_sequence(sequences, COMBINED_SEQ_INDEX, lbuttonup_seq, "WM_LBUTTONUP on a button", TRUE);
709
710 flush_sequences(sequences, NUM_MSG_SEQUENCES);
711 zfont = GetStockObject(SYSTEM_FONT);
712 SendMessageA(hwnd, WM_SETFONT, (WPARAM)zfont, TRUE);
713 UpdateWindow(hwnd);
714 ok_sequence(sequences, COMBINED_SEQ_INDEX, setfont_seq, "WM_SETFONT on a button", FALSE);
715
716 DestroyWindow(hwnd);
717 }
718
719 static void test_button_class(void)
720 {
721 static const WCHAR testW[] = {'t','e','s','t',0};
722 WNDCLASSEXW exW, ex2W;
723 WNDCLASSEXA exA;
724 char buffA[100];
725 WCHAR *nameW;
726 HWND hwnd;
727 BOOL ret;
728 int len;
729
730 ret = GetClassInfoExA(NULL, WC_BUTTONA, &exA);
731 ok(ret, "got %d\n", ret);
732 todo_wine
733 ok(IS_WNDPROC_HANDLE(exA.lpfnWndProc), "got %p\n", exA.lpfnWndProc);
734
735 ret = GetClassInfoExW(NULL, WC_BUTTONW, &exW);
736 ok(ret, "got %d\n", ret);
737 ok(!IS_WNDPROC_HANDLE(exW.lpfnWndProc), "got %p\n", exW.lpfnWndProc);
738
739 /* check that versioned class is also accessible */
740 nameW = get_versioned_classname(WC_BUTTONW);
741 ok(lstrcmpW(nameW, WC_BUTTONW), "got %s\n", wine_dbgstr_w(nameW));
742
743 ret = GetClassInfoExW(NULL, nameW, &ex2W);
744 todo_wine
745 ok(ret, "got %d\n", ret);
746 if (ret) /* TODO: remove once Wine is fixed */
747 ok(ex2W.lpfnWndProc == exW.lpfnWndProc, "got %p, %p\n", exW.lpfnWndProc, ex2W.lpfnWndProc);
748
749 /* Check reported class name */
750 hwnd = create_button(BS_CHECKBOX, NULL);
751 len = GetClassNameA(hwnd, buffA, sizeof(buffA));
752 ok(len == strlen(buffA), "got %d\n", len);
753 ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
754
755 len = RealGetWindowClassA(hwnd, buffA, sizeof(buffA));
756 ok(len == strlen(buffA), "got %d\n", len);
757 ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
758 DestroyWindow(hwnd);
759
760 /* explicitly create with versioned class name */
761 hwnd = CreateWindowExW(0, nameW, testW, BS_CHECKBOX, 0, 0, 50, 14, NULL, 0, 0, NULL);
762 todo_wine
763 ok(hwnd != NULL, "failed to create a window %s\n", wine_dbgstr_w(nameW));
764 if (hwnd)
765 {
766 len = GetClassNameA(hwnd, buffA, sizeof(buffA));
767 ok(len == strlen(buffA), "got %d\n", len);
768 ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
769
770 len = RealGetWindowClassA(hwnd, buffA, sizeof(buffA));
771 ok(len == strlen(buffA), "got %d\n", len);
772 ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
773
774 DestroyWindow(hwnd);
775 }
776 }
777
778 static void register_parent_class(void)
779 {
780 WNDCLASSA cls;
781
782 cls.style = 0;
783 cls.lpfnWndProc = test_parent_wndproc;
784 cls.cbClsExtra = 0;
785 cls.cbWndExtra = 0;
786 cls.hInstance = GetModuleHandleA(0);
787 cls.hIcon = 0;
788 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
789 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
790 cls.lpszMenuName = NULL;
791 cls.lpszClassName = "TestParentClass";
792 RegisterClassA(&cls);
793 }
794
795 START_TEST(button)
796 {
797 ULONG_PTR ctx_cookie;
798 HANDLE hCtx;
799
800 if (!load_v6_module(&ctx_cookie, &hCtx))
801 return;
802
803 register_parent_class();
804
805 init_functions();
806 init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
807
808 test_button_class();
809 test_button_messages();
810
811 unload_v6_module(ctx_cookie, hCtx);
812 }