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