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