edfa447baba83034dc4e120de59806998ea7b094
[reactos.git] / modules / 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 #ifdef __REACTOS__
24 #undef USE_WINE_TODOS
25 #endif
26
27 #include <windows.h>
28 #include <commctrl.h>
29
30 #include "wine/test.h"
31 #include "v6util.h"
32 #include "msg.h"
33
34 #ifdef __REACTOS__
35 #define BS_PUSHBOX 0x0000000AL
36 #endif
37
38 #define IS_WNDPROC_HANDLE(x) (((ULONG_PTR)(x) >> 16) == (~0u >> 16))
39
40 static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
41 static BOOL (WINAPI *pRemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR);
42 static LRESULT (WINAPI *pDefSubclassProc)(HWND, UINT, WPARAM, LPARAM);
43 static HIMAGELIST (WINAPI *pImageList_Create)(int, int, UINT, int, int);
44 static int (WINAPI *pImageList_Add)(HIMAGELIST, HBITMAP, HBITMAP);
45 static BOOL (WINAPI *pImageList_Destroy)(HIMAGELIST);
46
47 /****************** button message test *************************/
48 #define ID_BUTTON 0x000e
49
50 #define COMBINED_SEQ_INDEX 0
51 #define NUM_MSG_SEQUENCES 1
52
53 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
54
55 struct wndclass_redirect_data
56 {
57 ULONG size;
58 DWORD res;
59 ULONG name_len;
60 ULONG name_offset;
61 ULONG module_len;
62 ULONG module_offset;
63 };
64
65 /* returned pointer is valid as long as activation context is alive */
66 static WCHAR* get_versioned_classname(const WCHAR *name)
67 {
68 struct wndclass_redirect_data *wnddata;
69 ACTCTX_SECTION_KEYED_DATA data;
70 BOOL ret;
71
72 memset(&data, 0, sizeof(data));
73 data.cbSize = sizeof(data);
74 ret = FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION, name, &data);
75 ok(ret, "Failed to find class redirection section, error %u\n", 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 #define X(f) p##f = (void *)GetProcAddress(hmod, #f);
92 X(ImageList_Create);
93 X(ImageList_Add);
94 X(ImageList_Destroy);
95 #undef X
96 }
97
98 /* try to make sure pending X events have been processed before continuing */
99 static void flush_events(void)
100 {
101 MSG msg;
102 int diff = 200;
103 int min_timeout = 100;
104 DWORD time = GetTickCount() + diff;
105
106 while (diff > 0)
107 {
108 if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min_timeout, QS_ALLINPUT ) == WAIT_TIMEOUT) break;
109 while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
110 diff = time - GetTickCount();
111 }
112 }
113
114 static BOOL ignore_message( UINT message )
115 {
116 /* these are always ignored */
117 return (message >= 0xc000 ||
118 message == WM_GETICON ||
119 message == WM_GETOBJECT ||
120 message == WM_TIMECHANGE ||
121 message == WM_DISPLAYCHANGE ||
122 message == WM_DEVICECHANGE ||
123 message == WM_DWMNCRENDERINGCHANGED ||
124 message == WM_GETTEXTLENGTH ||
125 message == WM_GETTEXT);
126 }
127
128 static LRESULT CALLBACK button_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR id, DWORD_PTR ref_data)
129 {
130 static LONG defwndproc_counter = 0;
131 struct message msg = { 0 };
132 LRESULT ret;
133
134 if (ignore_message( message )) return pDefSubclassProc(hwnd, message, wParam, lParam);
135
136 switch (message)
137 {
138 case WM_SYNCPAINT:
139 break;
140 case BM_SETSTATE:
141 if (GetCapture())
142 ok(GetCapture() == hwnd, "GetCapture() = %p\n", GetCapture());
143 /* fall through */
144 default:
145 msg.message = message;
146 msg.flags = sent|wparam|lparam;
147 if (defwndproc_counter) msg.flags |= defwinproc;
148 msg.wParam = wParam;
149 msg.lParam = lParam;
150 add_message(sequences, COMBINED_SEQ_INDEX, &msg);
151 }
152
153 if (message == WM_NCDESTROY)
154 pRemoveWindowSubclass(hwnd, button_subclass_proc, 0);
155
156 defwndproc_counter++;
157 ret = pDefSubclassProc(hwnd, message, wParam, lParam);
158 defwndproc_counter--;
159
160 return ret;
161 }
162
163 static LRESULT WINAPI test_parent_wndproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
164 {
165 static LONG defwndproc_counter = 0;
166 static LONG beginpaint_counter = 0;
167 struct message msg = { 0 };
168 LRESULT ret;
169
170 if (ignore_message( message )) return 0;
171
172 if (message == WM_PARENTNOTIFY || message == WM_CANCELMODE ||
173 message == WM_SETFOCUS || message == WM_KILLFOCUS ||
174 message == WM_ENABLE || message == WM_ENTERIDLE ||
175 message == WM_DRAWITEM || message == WM_COMMAND ||
176 message == WM_IME_SETCONTEXT)
177 {
178 msg.message = message;
179 msg.flags = sent|parent|wparam|lparam;
180 if (defwndproc_counter) msg.flags |= defwinproc;
181 if (beginpaint_counter) msg.flags |= beginpaint;
182 msg.wParam = wParam;
183 msg.lParam = lParam;
184 add_message(sequences, COMBINED_SEQ_INDEX, &msg);
185 }
186
187 if (message == WM_PAINT)
188 {
189 PAINTSTRUCT ps;
190 beginpaint_counter++;
191 BeginPaint( hwnd, &ps );
192 beginpaint_counter--;
193 EndPaint( hwnd, &ps );
194 return 0;
195 }
196
197 defwndproc_counter++;
198 ret = DefWindowProcA(hwnd, message, wParam, lParam);
199 defwndproc_counter--;
200
201 return ret;
202 }
203
204 static const struct message setfocus_seq[] =
205 {
206 { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
207 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
208 { BM_GETSTATE, sent|optional }, /* when touchscreen is present */
209 { WM_SETFOCUS, sent|wparam },
210 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) },
211 { WM_APP, sent|wparam|lparam },
212 { WM_PAINT, sent },
213 { 0 }
214 };
215
216 static const struct message killfocus_seq[] =
217 {
218 { WM_KILLFOCUS, sent|wparam, 0 },
219 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_KILLFOCUS) },
220 { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
221 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 1 },
222 { WM_APP, sent|wparam|lparam, 0, 0 },
223 { WM_PAINT, sent },
224 { 0 }
225 };
226
227 static const struct message setfocus_static_seq[] =
228 {
229 { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
230 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
231 { BM_GETSTATE, sent|optional }, /* when touchscreen is present */
232 { WM_SETFOCUS, sent|wparam, 0 },
233 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) },
234 { WM_COMMAND, sent|wparam|parent|optional, MAKEWPARAM(ID_BUTTON, BN_CLICKED) }, /* radio button */
235 { WM_APP, sent|wparam|lparam, 0, 0 },
236 { WM_PAINT, sent },
237 { 0 }
238 };
239
240 static const struct message setfocus_groupbox_seq[] =
241 {
242 { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
243 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
244 { BM_GETSTATE, sent|optional }, /* when touchscreen is present */
245 { WM_SETFOCUS, sent|wparam, 0 },
246 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) },
247 { WM_COMMAND, sent|wparam|parent|optional, MAKEWPARAM(ID_BUTTON, BN_CLICKED) }, /* radio button */
248 { WM_APP, sent|wparam|lparam, 0, 0 },
249 { WM_PAINT, sent },
250 { 0 }
251 };
252
253 static const struct message killfocus_static_seq[] =
254 {
255 { WM_KILLFOCUS, sent|wparam, 0 },
256 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_KILLFOCUS) },
257 { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
258 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 1 },
259 { WM_APP, sent|wparam|lparam, 0, 0 },
260 { WM_PAINT, sent },
261 { 0 }
262 };
263
264 static const struct message setfocus_ownerdraw_seq[] =
265 {
266 { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
267 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
268 { BM_GETSTATE, sent|optional }, /* when touchscreen is present */
269 { WM_SETFOCUS, sent|wparam, 0 },
270 { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
271 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) },
272 { WM_APP, sent|wparam|lparam, 0, 0 },
273 { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
274 { 0 }
275 };
276
277 static const struct message killfocus_ownerdraw_seq[] =
278 {
279 { WM_KILLFOCUS, sent|wparam, 0 },
280 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_KILLFOCUS) },
281 { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
282 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 1 },
283 { WM_APP, sent|wparam|lparam, 0, 0 },
284 { WM_PAINT, sent },
285 { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
286 { 0 }
287 };
288
289 static const struct message lbuttondown_seq[] =
290 {
291 { WM_LBUTTONDOWN, sent|wparam|lparam, 0, 0 },
292 { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
293 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
294 { BM_GETSTATE, sent|defwinproc|optional }, /* when touchscreen is present */
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_PAINT, sent|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 { 0 }
391 };
392
393 static const struct message setstate_ownerdraw_seq[] =
394 {
395 { BM_SETSTATE, sent },
396 { WM_APP, sent|wparam|lparam, 0, 0 },
397 { WM_PAINT, sent },
398 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
399 { WM_ERASEBKGND, sent|defwinproc|optional },
400 { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
401 { 0 }
402 };
403
404 static const struct message clearstate_seq[] =
405 {
406 { BM_SETSTATE, sent },
407 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_UNHILITE) },
408 { WM_APP, sent|wparam|lparam, 0, 0 },
409 { WM_PAINT, sent },
410 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
411 { WM_ERASEBKGND, sent|defwinproc|optional },
412 { 0 }
413 };
414
415 static const struct message clearstate_ownerdraw_seq[] =
416 {
417 { BM_SETSTATE, sent },
418 { WM_APP, sent|wparam|lparam, 0, 0 },
419 { WM_PAINT, sent },
420 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
421 { WM_ERASEBKGND, sent|defwinproc|optional },
422 { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
423 { 0 }
424 };
425
426 static const struct message setcheck_ignored_seq[] =
427 {
428 { BM_SETCHECK, sent },
429 { WM_APP, sent|wparam|lparam, 0, 0 },
430 { WM_PAINT, sent|optional },
431 { 0 }
432 };
433
434 static const struct message setcheck_static_seq[] =
435 {
436 { BM_SETCHECK, sent },
437 { WM_APP, sent|wparam|lparam, 0, 0 },
438 { WM_PAINT, sent },
439 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
440 { WM_ERASEBKGND, sent|defwinproc|optional },
441 { 0 }
442 };
443
444 static const struct message setcheck_radio_seq[] =
445 {
446 { BM_SETCHECK, sent },
447 { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
448 { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
449 { WM_APP, sent|wparam|lparam, 0, 0 },
450 { 0 }
451 };
452
453 static const struct message setcheck_radio_redraw_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 { WM_PAINT, sent },
460 { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */
461 { 0 }
462 };
463
464 static HWND create_button(DWORD style, HWND parent)
465 {
466 HMENU menuid = 0;
467 HWND hwnd;
468
469 if (parent)
470 {
471 style |= WS_CHILD|BS_NOTIFY;
472 menuid = (HMENU)ID_BUTTON;
473 }
474 hwnd = CreateWindowExA(0, WC_BUTTONA, "test", style, 0, 0, 50, 14, parent, menuid, 0, NULL);
475 ok(hwnd != NULL, "failed to create a button, 0x%08x, %p\n", style, parent);
476 pSetWindowSubclass(hwnd, button_subclass_proc, 0, 0);
477 return hwnd;
478 }
479
480 static void test_button_messages(void)
481 {
482 static const struct
483 {
484 DWORD style;
485 DWORD dlg_code;
486 const struct message *setfocus;
487 const struct message *killfocus;
488 const struct message *setstyle;
489 const struct message *setstate;
490 const struct message *clearstate;
491 const struct message *setcheck;
492 } button[] = {
493 { BS_PUSHBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
494 setfocus_seq, killfocus_seq, setstyle_seq,
495 setstate_seq, setstate_seq, setcheck_ignored_seq },
496 { BS_DEFPUSHBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON,
497 setfocus_seq, killfocus_seq, setstyle_seq,
498 setstate_seq, setstate_seq, setcheck_ignored_seq },
499 { BS_CHECKBOX, DLGC_BUTTON,
500 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
501 setstate_static_seq, setstate_static_seq, setcheck_static_seq },
502 { BS_AUTOCHECKBOX, DLGC_BUTTON,
503 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
504 setstate_static_seq, setstate_static_seq, setcheck_static_seq },
505 { BS_RADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
506 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
507 setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq },
508 { BS_3STATE, DLGC_BUTTON,
509 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
510 setstate_static_seq, setstate_static_seq, setcheck_static_seq },
511 { BS_AUTO3STATE, DLGC_BUTTON,
512 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
513 setstate_static_seq, setstate_static_seq, setcheck_static_seq },
514 { BS_GROUPBOX, DLGC_STATIC,
515 setfocus_groupbox_seq, killfocus_static_seq, setstyle_static_seq,
516 setstate_static_seq, setstate_static_seq, setcheck_ignored_seq },
517 { BS_USERBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
518 setfocus_seq, killfocus_seq, setstyle_user_seq,
519 setstate_user_seq, clearstate_seq, setcheck_ignored_seq },
520 { BS_AUTORADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
521 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
522 setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq },
523 { BS_OWNERDRAW, DLGC_BUTTON,
524 setfocus_ownerdraw_seq, killfocus_ownerdraw_seq, setstyle_ownerdraw_seq,
525 setstate_ownerdraw_seq, clearstate_ownerdraw_seq, setcheck_ignored_seq },
526 { BS_SPLITBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON | DLGC_WANTARROWS,
527 setfocus_seq, killfocus_seq, setstyle_seq,
528 setstate_seq, setstate_seq, setcheck_ignored_seq },
529 { BS_DEFSPLITBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON | DLGC_WANTARROWS,
530 setfocus_seq, killfocus_seq, setstyle_seq,
531 setstate_seq, setstate_seq, setcheck_ignored_seq },
532 { BS_COMMANDLINK, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
533 setfocus_seq, killfocus_seq, setstyle_seq,
534 setstate_seq, setstate_seq, setcheck_ignored_seq },
535 { BS_DEFCOMMANDLINK, DLGC_BUTTON | DLGC_DEFPUSHBUTTON,
536 setfocus_seq, killfocus_seq, setstyle_seq,
537 setstate_seq, setstate_seq, setcheck_ignored_seq },
538 };
539 LOGFONTA logfont = { 0 };
540 const struct message *seq;
541 HFONT zfont, hfont2;
542 unsigned int i;
543 HWND hwnd, parent;
544 DWORD dlg_code;
545 BOOL todo;
546
547 /* selection with VK_SPACE should capture button window */
548 hwnd = create_button(BS_CHECKBOX | WS_VISIBLE | WS_POPUP, NULL);
549 ok(hwnd != 0, "Failed to create button window\n");
550 ReleaseCapture();
551 SetFocus(hwnd);
552 SendMessageA(hwnd, WM_KEYDOWN, VK_SPACE, 0);
553 ok(GetCapture() == hwnd, "Should be captured on VK_SPACE WM_KEYDOWN\n");
554 SendMessageA(hwnd, WM_KEYUP, VK_SPACE, 0);
555 DestroyWindow(hwnd);
556
557 parent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
558 100, 100, 200, 200, 0, 0, 0, NULL);
559 ok(parent != 0, "Failed to create parent window\n");
560
561 logfont.lfHeight = -12;
562 logfont.lfWeight = FW_NORMAL;
563 strcpy(logfont.lfFaceName, "Tahoma");
564
565 hfont2 = CreateFontIndirectA(&logfont);
566 ok(hfont2 != NULL, "Failed to create Tahoma font\n");
567
568 for (i = 0; i < ARRAY_SIZE(button); i++)
569 {
570 HFONT prevfont, hfont;
571 MSG msg;
572 DWORD style, state;
573 HDC hdc;
574
575 hwnd = create_button(button[i].style, parent);
576 ok(hwnd != NULL, "Failed to create a button.\n");
577
578 style = GetWindowLongA(hwnd, GWL_STYLE);
579 style &= ~(WS_CHILD | BS_NOTIFY);
580 /* XP turns a BS_USERBUTTON into BS_PUSHBUTTON */
581 if (button[i].style == BS_USERBUTTON)
582 ok(style == BS_PUSHBUTTON, "expected style BS_PUSHBUTTON got %x\n", style);
583 else
584 ok(style == button[i].style, "expected style %x got %x\n", button[i].style, style);
585
586 dlg_code = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
587 if (button[i].style == BS_SPLITBUTTON ||
588 button[i].style == BS_DEFSPLITBUTTON ||
589 button[i].style == BS_COMMANDLINK ||
590 button[i].style == BS_DEFCOMMANDLINK)
591 {
592 ok(dlg_code == button[i].dlg_code || broken(dlg_code == DLGC_BUTTON) /* WinXP */, "%u: wrong dlg_code %08x\n", i, dlg_code);
593 }
594 else
595 ok(dlg_code == button[i].dlg_code, "%u: wrong dlg_code %08x\n", i, dlg_code);
596
597 ShowWindow(hwnd, SW_SHOW);
598 UpdateWindow(hwnd);
599 SetFocus(0);
600 flush_events();
601 SetFocus(0);
602 flush_sequences(sequences, NUM_MSG_SEQUENCES);
603
604 todo = button[i].style != BS_OWNERDRAW;
605 ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus());
606 SetFocus(hwnd);
607 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
608 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
609 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setfocus, "SetFocus(hwnd) on a button", todo);
610
611 todo = button[i].style == BS_OWNERDRAW;
612 SetFocus(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].killfocus, "SetFocus(0) on a button", todo);
616 ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus());
617
618 SendMessageA(hwnd, BM_SETSTYLE, button[i].style | BS_BOTTOM, TRUE);
619 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
620 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
621 todo = button[i].style == BS_OWNERDRAW;
622 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setstyle, "BM_SETSTYLE on a button", todo);
623
624 style = GetWindowLongA(hwnd, GWL_STYLE);
625 style &= ~(WS_VISIBLE | WS_CHILD | BS_NOTIFY);
626 /* XP doesn't turn a BS_USERBUTTON into BS_PUSHBUTTON here! */
627 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
628
629 state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
630 ok(state == 0, "expected state 0, got %04x\n", state);
631
632 flush_sequences(sequences, NUM_MSG_SEQUENCES);
633
634 SendMessageA(hwnd, BM_SETSTATE, TRUE, 0);
635 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
636 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
637 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setstate, "BM_SETSTATE/TRUE on a button", FALSE);
638
639 state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
640 ok(state == BST_PUSHED, "expected state 0x0004, got %04x\n", state);
641
642 style = GetWindowLongA(hwnd, GWL_STYLE);
643 style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
644 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
645
646 flush_sequences(sequences, NUM_MSG_SEQUENCES);
647
648 SendMessageA(hwnd, BM_SETSTATE, FALSE, 0);
649 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
650 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
651 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].clearstate, "BM_SETSTATE/FALSE on a button", FALSE);
652
653 state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
654 ok(state == 0, "expected state 0, got %04x\n", state);
655
656 style = GetWindowLongA(hwnd, GWL_STYLE);
657 style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
658 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
659
660 state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
661 ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state);
662
663 flush_sequences(sequences, NUM_MSG_SEQUENCES);
664
665 if (button[i].style == BS_RADIOBUTTON ||
666 button[i].style == BS_AUTORADIOBUTTON)
667 {
668 seq = setcheck_radio_seq;
669 }
670 else
671 seq = setcheck_ignored_seq;
672
673 SendMessageA(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
674 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
675 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
676 ok_sequence(sequences, COMBINED_SEQ_INDEX, seq, "BM_SETCHECK on a button", FALSE);
677
678 state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
679 ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state);
680
681 style = GetWindowLongA(hwnd, GWL_STYLE);
682 style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
683 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
684
685 flush_sequences(sequences, NUM_MSG_SEQUENCES);
686
687 SendMessageA(hwnd, BM_SETCHECK, BST_CHECKED, 0);
688 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
689 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
690 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setcheck, "BM_SETCHECK on a button", FALSE);
691
692 state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
693 if (button[i].style == BS_PUSHBUTTON ||
694 button[i].style == BS_DEFPUSHBUTTON ||
695 button[i].style == BS_GROUPBOX ||
696 button[i].style == BS_USERBUTTON ||
697 button[i].style == BS_OWNERDRAW ||
698 button[i].style == BS_SPLITBUTTON ||
699 button[i].style == BS_DEFSPLITBUTTON ||
700 button[i].style == BS_COMMANDLINK ||
701 button[i].style == BS_DEFCOMMANDLINK)
702 {
703 ok(state == BST_UNCHECKED, "expected check BST_UNCHECKED, got %04x\n", state);
704 }
705 else
706 ok(state == BST_CHECKED, "expected check BST_CHECKED, got %04x\n", state);
707
708 style = GetWindowLongA(hwnd, GWL_STYLE);
709 style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
710 if (button[i].style == BS_RADIOBUTTON ||
711 button[i].style == BS_AUTORADIOBUTTON)
712 ok(style == (button[i].style | WS_TABSTOP), "expected style %04x | WS_TABSTOP got %04x\n", button[i].style, style);
713 else
714 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
715
716 /* Test that original font is not selected back after painting */
717 hfont = (HFONT)SendMessageA(hwnd, WM_GETFONT, 0, 0);
718 ok(hfont == NULL, "Unexpected control font.\n");
719
720 SendMessageA(hwnd, WM_SETFONT, (WPARAM)GetStockObject(SYSTEM_FONT), 0);
721
722 hdc = CreateCompatibleDC(0);
723
724 prevfont = SelectObject(hdc, hfont2);
725 SendMessageA(hwnd, WM_PRINTCLIENT, (WPARAM)hdc, 0);
726 ok(hfont2 != GetCurrentObject(hdc, OBJ_FONT) || broken(hfont2 == GetCurrentObject(hdc, OBJ_FONT)) /* WinXP */,
727 "button[%u]: unexpected font selected after WM_PRINTCLIENT\n", i);
728 SelectObject(hdc, prevfont);
729
730 prevfont = SelectObject(hdc, hfont2);
731 SendMessageA(hwnd, WM_PAINT, (WPARAM)hdc, 0);
732 ok(hfont2 != GetCurrentObject(hdc, OBJ_FONT) || broken(hfont2 == GetCurrentObject(hdc, OBJ_FONT)) /* WinXP */,
733 "button[%u]: unexpected font selected after WM_PAINT\n", i);
734 SelectObject(hdc, prevfont);
735
736 DeleteDC(hdc);
737
738 DestroyWindow(hwnd);
739 }
740
741 DeleteObject(hfont2);
742 DestroyWindow(parent);
743
744 hwnd = create_button(BS_PUSHBUTTON, NULL);
745
746 SetForegroundWindow(hwnd);
747 flush_events();
748
749 SetActiveWindow(hwnd);
750 SetFocus(0);
751 flush_sequences(sequences, NUM_MSG_SEQUENCES);
752
753 SendMessageA(hwnd, WM_LBUTTONDOWN, 0, 0);
754 ok_sequence(sequences, COMBINED_SEQ_INDEX, lbuttondown_seq, "WM_LBUTTONDOWN on a button", FALSE);
755
756 SendMessageA(hwnd, WM_LBUTTONUP, 0, 0);
757 ok_sequence(sequences, COMBINED_SEQ_INDEX, lbuttonup_seq, "WM_LBUTTONUP on a button", TRUE);
758
759 flush_sequences(sequences, NUM_MSG_SEQUENCES);
760 zfont = GetStockObject(SYSTEM_FONT);
761 SendMessageA(hwnd, WM_SETFONT, (WPARAM)zfont, TRUE);
762 UpdateWindow(hwnd);
763 ok_sequence(sequences, COMBINED_SEQ_INDEX, setfont_seq, "WM_SETFONT on a button", FALSE);
764
765 DestroyWindow(hwnd);
766 }
767
768 static void test_button_class(void)
769 {
770 static const WCHAR testW[] = {'t','e','s','t',0};
771 WNDCLASSEXW exW, ex2W;
772 WNDCLASSEXA exA;
773 char buffA[100];
774 WCHAR *nameW;
775 HWND hwnd;
776 BOOL ret;
777 int len;
778
779 ret = GetClassInfoExA(NULL, WC_BUTTONA, &exA);
780 ok(ret, "got %d\n", ret);
781 ok(IS_WNDPROC_HANDLE(exA.lpfnWndProc), "got %p\n", exA.lpfnWndProc);
782 ok(exA.cbClsExtra == 0, "Unexpected class bytes %d.\n", exA.cbClsExtra);
783 ok(exA.cbWndExtra == sizeof(void *), "Unexpected window bytes %d.\n", exA.cbWndExtra);
784
785 ret = GetClassInfoExW(NULL, WC_BUTTONW, &exW);
786 ok(ret, "got %d\n", ret);
787 ok(!IS_WNDPROC_HANDLE(exW.lpfnWndProc), "got %p\n", exW.lpfnWndProc);
788 ok(exW.cbClsExtra == 0, "Unexpected class bytes %d.\n", exW.cbClsExtra);
789 ok(exW.cbWndExtra == sizeof(void *), "Unexpected window bytes %d.\n", exW.cbWndExtra);
790
791 /* check that versioned class is also accessible */
792 nameW = get_versioned_classname(WC_BUTTONW);
793 ok(lstrcmpW(nameW, WC_BUTTONW), "got %s\n", wine_dbgstr_w(nameW));
794
795 ret = GetClassInfoExW(NULL, nameW, &ex2W);
796 ok(ret, "got %d\n", ret);
797 ok(ex2W.lpfnWndProc == exW.lpfnWndProc, "got %p, %p\n", exW.lpfnWndProc, ex2W.lpfnWndProc);
798
799 /* Check reported class name */
800 hwnd = create_button(BS_CHECKBOX, NULL);
801 len = GetClassNameA(hwnd, buffA, sizeof(buffA));
802 ok(len == strlen(buffA), "got %d\n", len);
803 ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
804
805 len = RealGetWindowClassA(hwnd, buffA, sizeof(buffA));
806 ok(len == strlen(buffA), "got %d\n", len);
807 ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
808 DestroyWindow(hwnd);
809
810 /* explicitly create with versioned class name */
811 hwnd = CreateWindowExW(0, nameW, testW, BS_CHECKBOX, 0, 0, 50, 14, NULL, 0, 0, NULL);
812 ok(hwnd != NULL, "failed to create a window %s\n", wine_dbgstr_w(nameW));
813
814 len = GetClassNameA(hwnd, buffA, sizeof(buffA));
815 ok(len == strlen(buffA), "got %d\n", len);
816 ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
817
818 len = RealGetWindowClassA(hwnd, buffA, sizeof(buffA));
819 ok(len == strlen(buffA), "got %d\n", len);
820 ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
821
822 DestroyWindow(hwnd);
823 }
824
825 static void test_note(void)
826 {
827 HWND hwnd;
828 BOOL ret;
829 WCHAR test_w[] = {'t', 'e', 's', 't', 0};
830 WCHAR tes_w[] = {'t', 'e', 's', 0};
831 WCHAR deadbeef_w[] = {'d', 'e', 'a', 'd', 'b', 'e', 'e', 'f', 0};
832 WCHAR buffer_w[10];
833 DWORD size;
834 DWORD error;
835 INT type;
836
837 hwnd = create_button(BS_COMMANDLINK, NULL);
838 ok(hwnd != NULL, "Expect hwnd not null\n");
839 SetLastError(0xdeadbeef);
840 size = ARRAY_SIZE(buffer_w);
841 ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
842 error = GetLastError();
843 if (!ret && error == 0xdeadbeef)
844 {
845 win_skip("BCM_GETNOTE message is unavailable. Skipping note tests\n"); /* xp or 2003 */
846 DestroyWindow(hwnd);
847 return;
848 }
849 DestroyWindow(hwnd);
850
851 for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++)
852 {
853 if (type == BS_DEFCOMMANDLINK || type == BS_COMMANDLINK)
854 {
855 hwnd = create_button(type, NULL);
856 ok(hwnd != NULL, "Expect hwnd not null\n");
857
858 /* Get note when note hasn't been not set yet */
859 SetLastError(0xdeadbeef);
860 lstrcpyW(buffer_w, deadbeef_w);
861 size = ARRAY_SIZE(buffer_w);
862 ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
863 error = GetLastError();
864 ok(!ret, "Expect BCM_GETNOTE return false\n");
865 ok(!lstrcmpW(buffer_w, deadbeef_w), "Expect note: %s, got: %s\n",
866 wine_dbgstr_w(deadbeef_w), wine_dbgstr_w(buffer_w));
867 ok(size == ARRAY_SIZE(buffer_w), "Got: %d\n", size);
868 ok(error == ERROR_INVALID_PARAMETER, "Expect last error: 0x%08x, got: 0x%08x\n",
869 ERROR_INVALID_PARAMETER, error);
870
871 /* Get note length when note is not set */
872 ret = SendMessageA(hwnd, BCM_GETNOTELENGTH, 0, 0);
873 ok(ret == 0, "Expect note length: %d, got: %d\n", 0, ret);
874
875 /* Successful set note, get note and get note length */
876 SetLastError(0xdeadbeef);
877 ret = SendMessageA(hwnd, BCM_SETNOTE, 0, (LPARAM)test_w);
878 ok(ret, "Expect BCM_SETNOTE return true\n");
879 error = GetLastError();
880 ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error);
881
882 SetLastError(0xdeadbeef);
883 lstrcpyW(buffer_w, deadbeef_w);
884 size = ARRAY_SIZE(buffer_w);
885 ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
886 ok(ret, "Expect BCM_GETNOTE return true\n");
887 ok(!lstrcmpW(buffer_w, test_w), "Expect note: %s, got: %s\n", wine_dbgstr_w(test_w),
888 wine_dbgstr_w(buffer_w));
889 ok(size == ARRAY_SIZE(buffer_w), "Got: %d\n", size);
890 error = GetLastError();
891 ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error);
892
893 ret = SendMessageA(hwnd, BCM_GETNOTELENGTH, 0, 0);
894 ok(ret == ARRAY_SIZE(test_w) - 1, "Got: %d\n", ret);
895
896 /* Insufficient buffer, return partial string */
897 SetLastError(0xdeadbeef);
898 lstrcpyW(buffer_w, deadbeef_w);
899 size = ARRAY_SIZE(test_w) - 1;
900 ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
901 ok(!ret, "Expect BCM_GETNOTE return false\n");
902 ok(!lstrcmpW(buffer_w, tes_w), "Expect note: %s, got: %s\n", wine_dbgstr_w(tes_w),
903 wine_dbgstr_w(buffer_w));
904 ok(size == ARRAY_SIZE(test_w), "Got: %d\n", size);
905 error = GetLastError();
906 ok(error == ERROR_INSUFFICIENT_BUFFER, "Expect last error: 0x%08x, got: 0x%08x\n",
907 ERROR_INSUFFICIENT_BUFFER, error);
908
909 /* Set note with NULL buffer */
910 SetLastError(0xdeadbeef);
911 ret = SendMessageA(hwnd, BCM_SETNOTE, 0, 0);
912 ok(ret, "Expect BCM_SETNOTE return false\n");
913 error = GetLastError();
914 ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error);
915
916 /* Check that set note with NULL buffer make note empty */
917 SetLastError(0xdeadbeef);
918 lstrcpyW(buffer_w, deadbeef_w);
919 size = ARRAY_SIZE(buffer_w);
920 ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
921 ok(ret, "Expect BCM_GETNOTE return true\n");
922 ok(lstrlenW(buffer_w) == 0, "Expect note length 0\n");
923 ok(size == ARRAY_SIZE(buffer_w), "Got: %d\n", size);
924 error = GetLastError();
925 ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error);
926 ret = SendMessageA(hwnd, BCM_GETNOTELENGTH, 0, 0);
927 ok(ret == 0, "Expect note length: %d, got: %d\n", 0, ret);
928
929 /* Get note with NULL buffer */
930 SetLastError(0xdeadbeef);
931 size = ARRAY_SIZE(buffer_w);
932 ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, 0);
933 ok(!ret, "Expect BCM_SETNOTE return false\n");
934 ok(size == ARRAY_SIZE(buffer_w), "Got: %d\n", size);
935 error = GetLastError();
936 ok(error == ERROR_INVALID_PARAMETER, "Expect last error: 0x%08x, got: 0x%08x\n",
937 ERROR_INVALID_PARAMETER, error);
938
939 /* Get note with NULL size */
940 SetLastError(0xdeadbeef);
941 lstrcpyW(buffer_w, deadbeef_w);
942 ret = SendMessageA(hwnd, BCM_GETNOTE, 0, (LPARAM)buffer_w);
943 ok(!ret, "Expect BCM_SETNOTE return false\n");
944 ok(!lstrcmpW(buffer_w, deadbeef_w), "Expect note: %s, got: %s\n",
945 wine_dbgstr_w(deadbeef_w), wine_dbgstr_w(buffer_w));
946 error = GetLastError();
947 ok(error == ERROR_INVALID_PARAMETER, "Expect last error: 0x%08x, got: 0x%08x\n",
948 ERROR_INVALID_PARAMETER, error);
949
950 /* Get note with zero size */
951 SetLastError(0xdeadbeef);
952 size = 0;
953 lstrcpyW(buffer_w, deadbeef_w);
954 ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
955 ok(!ret, "Expect BCM_GETNOTE return false\n");
956 ok(!lstrcmpW(buffer_w, deadbeef_w), "Expect note: %s, got: %s\n",
957 wine_dbgstr_w(deadbeef_w), wine_dbgstr_w(buffer_w));
958 ok(size == 1, "Got: %d\n", size);
959 error = GetLastError();
960 ok(error == ERROR_INSUFFICIENT_BUFFER, "Expect last error: 0x%08x, got: 0x%08x\n",
961 ERROR_INSUFFICIENT_BUFFER, error);
962
963 DestroyWindow(hwnd);
964 }
965 else
966 {
967 hwnd = create_button(type, NULL);
968 ok(hwnd != NULL, "Expect hwnd not null\n");
969 SetLastError(0xdeadbeef);
970 size = ARRAY_SIZE(buffer_w);
971 ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
972 ok(!ret, "Expect BCM_GETNOTE return false\n");
973 error = GetLastError();
974 ok(error == ERROR_NOT_SUPPORTED, "Expect last error: 0x%08x, got: 0x%08x\n",
975 ERROR_NOT_SUPPORTED, error);
976 DestroyWindow(hwnd);
977 }
978 }
979 }
980
981 static void test_bm_get_set_image(void)
982 {
983 HWND hwnd;
984 HDC hdc;
985 HBITMAP hbmp1x1;
986 HBITMAP hbmp2x2;
987 HBITMAP hmask2x2;
988 ICONINFO icon_info2x2;
989 HICON hicon2x2;
990 HBITMAP hbmp;
991 HICON hicon;
992 ICONINFO icon_info;
993 BITMAP bm;
994 static const DWORD default_style = BS_PUSHBUTTON | WS_TABSTOP | WS_POPUP | WS_VISIBLE;
995
996 hdc = GetDC(0);
997 hbmp1x1 = CreateCompatibleBitmap(hdc, 1, 1);
998 hbmp2x2 = CreateCompatibleBitmap(hdc, 2, 2);
999 ZeroMemory(&bm, sizeof(bm));
1000 ok(GetObjectW(hbmp1x1, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1001 ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1,
1002 bm.bmWidth, bm.bmHeight);
1003 ZeroMemory(&bm, sizeof(bm));
1004 ok(GetObjectW(hbmp2x2, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1005 ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
1006 bm.bmWidth, bm.bmHeight);
1007
1008 hmask2x2 = CreateCompatibleBitmap(hdc, 2, 2);
1009 ZeroMemory(&icon_info2x2, sizeof(icon_info2x2));
1010 icon_info2x2.fIcon = TRUE;
1011 icon_info2x2.hbmMask = hmask2x2;
1012 icon_info2x2.hbmColor = hbmp2x2;
1013 hicon2x2 = CreateIconIndirect(&icon_info2x2);
1014 ok(hicon2x2 !=NULL, "Expect CreateIconIndirect() success\n");
1015
1016 ZeroMemory(&icon_info, sizeof(icon_info));
1017 ok(GetIconInfo(hicon2x2, &icon_info), "Expect GetIconInfo() success\n");
1018 ZeroMemory(&bm, sizeof(bm));
1019 ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1020 ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
1021 bm.bmWidth, bm.bmHeight);
1022 DeleteObject(icon_info.hbmColor);
1023 DeleteObject(icon_info.hbmMask);
1024
1025 hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0,
1026 0, 0);
1027 ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1028 /* Get image when image is not set */
1029 hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0);
1030 ok(hbmp == 0, "Expect hbmp == 0\n");
1031 /* Set image */
1032 hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp1x1);
1033 ok(hbmp == 0, "Expect hbmp == 0\n");
1034 hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0);
1035 ok(hbmp != 0, "Expect hbmp != 0\n");
1036 /* Set null resets image */
1037 hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, 0);
1038 ok(hbmp != 0, "Expect hbmp != 0\n");
1039 hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0);
1040 ok(hbmp == 0, "Expect hbmp == 0\n");
1041 DestroyWindow(hwnd);
1042
1043 /* Set bitmap with BS_BITMAP */
1044 hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0,
1045 0, 0);
1046 ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1047 hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp1x1);
1048 ok(hbmp == 0, "Expect hbmp == 0\n");
1049 hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0);
1050 ok(hbmp != 0, "Expect hbmp != 0\n");
1051 ZeroMemory(&bm, sizeof(bm));
1052 ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1053 ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1,
1054 bm.bmWidth, bm.bmHeight);
1055 DestroyWindow(hwnd);
1056
1057 /* Set bitmap without BS_BITMAP */
1058 hwnd = CreateWindowA(WC_BUTTONA, "test", default_style, 0, 0, 100, 100, 0, 0, 0, 0);
1059 ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1060 hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp1x1);
1061 ok(hbmp == 0, "Expect hbmp == 0\n");
1062 hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0);
1063 if (hbmp == 0)
1064 {
1065 /* on xp or 2003*/
1066 win_skip("Show both image and text is not supported. Skip following tests.\n");
1067 DestroyWindow(hwnd);
1068 goto done;
1069 }
1070 ok(hbmp != 0, "Expect hbmp != 0\n");
1071 ZeroMemory(&bm, sizeof(bm));
1072 ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1073 ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1,
1074 bm.bmWidth, bm.bmHeight);
1075 DestroyWindow(hwnd);
1076
1077 /* Set icon with BS_ICON */
1078 hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0,
1079 0);
1080 ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1081 hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon2x2);
1082 ok(hicon == 0, "Expect hicon == 0\n");
1083 hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0);
1084 ok(hicon != 0, "Expect hicon != 0\n");
1085 ZeroMemory(&icon_info, sizeof(icon_info));
1086 ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n");
1087 ZeroMemory(&bm, sizeof(bm));
1088 ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1089 ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
1090 bm.bmWidth, bm.bmHeight);
1091 DeleteObject(icon_info.hbmColor);
1092 DeleteObject(icon_info.hbmMask);
1093 DestroyWindow(hwnd);
1094
1095 /* Set icon without BS_ICON */
1096 hwnd = CreateWindowA(WC_BUTTONA, "test", default_style, 0, 0, 100, 100, 0, 0, 0, 0);
1097 ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1098 hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon2x2);
1099 ok(hicon == 0, "Expect hicon == 0\n");
1100 hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0);
1101 ok(hicon != 0, "Expect hicon != 0\n");
1102 ZeroMemory(&icon_info, sizeof(icon_info));
1103 ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n");
1104 ZeroMemory(&bm, sizeof(bm));
1105 ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1106 ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
1107 bm.bmWidth, bm.bmHeight);
1108 DeleteObject(icon_info.hbmColor);
1109 DeleteObject(icon_info.hbmMask);
1110 DestroyWindow(hwnd);
1111
1112 /* Set icon with BS_BITMAP */
1113 hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0,
1114 0, 0);
1115 ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1116 hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon2x2);
1117 ok(hicon == 0, "Expect hicon == 0\n");
1118 hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0);
1119 ok(hicon != 0, "Expect hicon != 0\n");
1120 ZeroMemory(&icon_info, sizeof(icon_info));
1121 ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n");
1122 ZeroMemory(&bm, sizeof(bm));
1123 ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1124 ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
1125 bm.bmWidth, bm.bmHeight);
1126 DeleteObject(icon_info.hbmColor);
1127 DeleteObject(icon_info.hbmMask);
1128 DestroyWindow(hwnd);
1129
1130 /* Set bitmap with BS_ICON */
1131 hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0,
1132 0);
1133 ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1134 hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp1x1);
1135 ok(hbmp == 0, "Expect hbmp == 0\n");
1136 hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0);
1137 ok(hbmp != 0, "Expect hbmp != 0\n");
1138 ZeroMemory(&bm, sizeof(bm));
1139 ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1140 ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1,
1141 bm.bmWidth, bm.bmHeight);
1142 DestroyWindow(hwnd);
1143
1144 /* Set bitmap with BS_BITMAP and IMAGE_ICON*/
1145 hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0,
1146 0, 0);
1147 ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1148 hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hbmp1x1);
1149 ok(hbmp == 0, "Expect hbmp == 0\n");
1150 hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0);
1151 ok(hbmp != 0, "Expect hbmp != 0\n");
1152 ZeroMemory(&bm, sizeof(bm));
1153 ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1154 ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1,
1155 bm.bmWidth, bm.bmHeight);
1156 DestroyWindow(hwnd);
1157
1158 /* Set icon with BS_ICON and IMAGE_BITMAP */
1159 hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0,
1160 0);
1161 ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1162 hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hicon2x2);
1163 ok(hicon == 0, "Expect hicon == 0\n");
1164 hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0);
1165 ok(hicon != 0, "Expect hicon != 0\n");
1166 ZeroMemory(&icon_info, sizeof(icon_info));
1167 ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n");
1168 ZeroMemory(&bm, sizeof(bm));
1169 ok(GetObjectW(icon_info.hbmColor, sizeof(BITMAP), &bm), "Expect GetObjectW() success\n");
1170 ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
1171 bm.bmWidth, bm.bmHeight);
1172 DeleteObject(icon_info.hbmColor);
1173 DeleteObject(icon_info.hbmMask);
1174 DestroyWindow(hwnd);
1175
1176 /* Set bitmap with BS_ICON and IMAGE_ICON */
1177 hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0, 0);
1178 ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1179 hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hbmp1x1);
1180 ok(hbmp == 0, "Expect hbmp == 0\n");
1181 hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0);
1182 ok(hbmp != 0, "Expect hbmp != 0\n");
1183 ZeroMemory(&bm, sizeof(bm));
1184 ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1185 ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1,
1186 bm.bmWidth, bm.bmHeight);
1187 DestroyWindow(hwnd);
1188
1189 /* Set icon with BS_BITMAP and IMAGE_BITMAP */
1190 hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0, 0, 0);
1191 ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1192 hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hicon2x2);
1193 ok(hicon == 0, "Expect hicon == 0\n");
1194 hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0);
1195 ok(hicon != 0, "Expect hicon != 0\n");
1196 ZeroMemory(&icon_info, sizeof(icon_info));
1197 ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n");
1198 ZeroMemory(&bm, sizeof(bm));
1199 ok(GetObjectW(icon_info.hbmColor, sizeof(BITMAP), &bm), "Expect GetObjectW() success\n");
1200 ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
1201 bm.bmWidth, bm.bmHeight);
1202 DeleteObject(icon_info.hbmColor);
1203 DeleteObject(icon_info.hbmMask);
1204 DestroyWindow(hwnd);
1205
1206 done:
1207 DestroyIcon(hicon2x2);
1208 DeleteObject(hmask2x2);
1209 DeleteObject(hbmp2x2);
1210 DeleteObject(hbmp1x1);
1211 ReleaseDC(0, hdc);
1212 }
1213
1214 static void register_parent_class(void)
1215 {
1216 WNDCLASSA cls;
1217
1218 cls.style = 0;
1219 cls.lpfnWndProc = test_parent_wndproc;
1220 cls.cbClsExtra = 0;
1221 cls.cbWndExtra = 0;
1222 cls.hInstance = GetModuleHandleA(0);
1223 cls.hIcon = 0;
1224 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
1225 cls.hbrBackground = GetStockObject(WHITE_BRUSH);
1226 cls.lpszMenuName = NULL;
1227 cls.lpszClassName = "TestParentClass";
1228 RegisterClassA(&cls);
1229 }
1230
1231 static void test_button_data(void)
1232 {
1233 static const DWORD styles[] =
1234 {
1235 BS_PUSHBUTTON,
1236 BS_DEFPUSHBUTTON,
1237 BS_CHECKBOX,
1238 BS_AUTOCHECKBOX,
1239 BS_RADIOBUTTON,
1240 BS_3STATE,
1241 BS_AUTO3STATE,
1242 BS_GROUPBOX,
1243 BS_USERBUTTON,
1244 BS_AUTORADIOBUTTON,
1245 BS_OWNERDRAW,
1246 BS_SPLITBUTTON,
1247 BS_DEFSPLITBUTTON,
1248 BS_COMMANDLINK,
1249 BS_DEFCOMMANDLINK,
1250 };
1251
1252 struct button_desc
1253 {
1254 HWND self;
1255 HWND parent;
1256 LONG style;
1257 };
1258 unsigned int i;
1259 HWND parent;
1260
1261 parent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
1262 100, 100, 200, 200, 0, 0, 0, NULL);
1263 ok(parent != 0, "Failed to create parent window\n");
1264
1265 for (i = 0; i < ARRAY_SIZE(styles); i++)
1266 {
1267 struct button_desc *desc;
1268 HWND hwnd;
1269
1270 hwnd = create_button(styles[i], parent);
1271 ok(hwnd != NULL, "Failed to create a button.\n");
1272
1273 desc = (void *)GetWindowLongPtrA(hwnd, 0);
1274 ok(desc != NULL, "Expected window data.\n");
1275
1276 if (desc)
1277 {
1278 ok(desc->self == hwnd, "Unexpected 'self' field.\n");
1279 ok(desc->parent == parent, "Unexpected 'parent' field.\n");
1280 ok(desc->style == (WS_CHILD | BS_NOTIFY | styles[i]), "Unexpected 'style' field.\n");
1281 }
1282
1283 DestroyWindow(hwnd);
1284 }
1285
1286 DestroyWindow(parent);
1287 }
1288
1289 static void test_get_set_imagelist(void)
1290 {
1291 HWND hwnd;
1292 HIMAGELIST himl;
1293 BUTTON_IMAGELIST biml = {0};
1294 HDC hdc;
1295 HBITMAP hbmp;
1296 INT width = 16;
1297 INT height = 16;
1298 INT index;
1299 DWORD type;
1300 BOOL ret;
1301
1302 hdc = GetDC(0);
1303 hbmp = CreateCompatibleBitmap(hdc, width, height);
1304 ok(hbmp != NULL, "Expect hbmp not null\n");
1305
1306 himl = pImageList_Create(width, height, ILC_COLOR, 1, 0);
1307 ok(himl != NULL, "Expect himl not null\n");
1308 index = pImageList_Add(himl, hbmp, NULL);
1309 ok(index == 0, "Expect index == 0\n");
1310 DeleteObject(hbmp);
1311 ReleaseDC(0, hdc);
1312
1313 for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++)
1314 {
1315 hwnd = create_button(type, NULL);
1316 ok(hwnd != NULL, "Expect hwnd not null\n");
1317
1318 /* Get imagelist when imagelist is unset yet */
1319 ret = SendMessageA(hwnd, BCM_GETIMAGELIST, 0, (LPARAM)&biml);
1320 ok(ret, "Expect BCM_GETIMAGELIST return true\n");
1321 ok(biml.himl == 0 && IsRectEmpty(&biml.margin) && biml.uAlign == 0,
1322 "Expect BUTTON_IMAGELIST is empty\n");
1323
1324 /* Set imagelist with himl null */
1325 biml.himl = 0;
1326 biml.uAlign = BUTTON_IMAGELIST_ALIGN_CENTER;
1327 ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml);
1328 ok(ret || broken(!ret), /* xp or 2003 */
1329 "Expect BCM_SETIMAGELIST return true\n");
1330
1331 /* Set imagelist with uAlign invalid */
1332 biml.himl = himl;
1333 biml.uAlign = -1;
1334 ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml);
1335 ok(ret, "Expect BCM_SETIMAGELIST return true\n");
1336
1337 /* Successful get and set imagelist */
1338 biml.himl = himl;
1339 biml.uAlign = BUTTON_IMAGELIST_ALIGN_CENTER;
1340 ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml);
1341 ok(ret, "Expect BCM_SETIMAGELIST return true\n");
1342 ret = SendMessageA(hwnd, BCM_GETIMAGELIST, 0, (LPARAM)&biml);
1343 ok(ret, "Expect BCM_GETIMAGELIST return true\n");
1344 ok(biml.himl == himl, "Expect himl to be same\n");
1345 ok(biml.uAlign == BUTTON_IMAGELIST_ALIGN_CENTER, "Expect uAlign to be %x\n",
1346 BUTTON_IMAGELIST_ALIGN_CENTER);
1347
1348 /* BCM_SETIMAGELIST null pointer handling */
1349 ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, 0);
1350 ok(!ret, "Expect BCM_SETIMAGELIST return false\n");
1351 ret = SendMessageA(hwnd, BCM_GETIMAGELIST, 0, (LPARAM)&biml);
1352 ok(ret, "Expect BCM_GETIMAGELIST return true\n");
1353 ok(biml.himl == himl, "Expect himl to be same\n");
1354
1355 /* BCM_GETIMAGELIST null pointer handling */
1356 biml.himl = himl;
1357 biml.uAlign = BUTTON_IMAGELIST_ALIGN_CENTER;
1358 ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml);
1359 ok(ret, "Expect BCM_SETIMAGELIST return true\n");
1360 ret = SendMessageA(hwnd, BCM_GETIMAGELIST, 0, 0);
1361 ok(!ret, "Expect BCM_GETIMAGELIST return false\n");
1362
1363 DestroyWindow(hwnd);
1364 }
1365
1366 pImageList_Destroy(himl);
1367 }
1368
1369 static void test_get_set_textmargin(void)
1370 {
1371 HWND hwnd;
1372 RECT margin_in;
1373 RECT margin_out;
1374 BOOL ret;
1375 DWORD type;
1376
1377 SetRect(&margin_in, 2, 1, 3, 4);
1378 for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++)
1379 {
1380 hwnd = create_button(type, NULL);
1381 ok(hwnd != NULL, "Expect hwnd not null\n");
1382
1383 /* Get text margin when it is unset */
1384 ret = SendMessageA(hwnd, BCM_GETTEXTMARGIN, 0, (LPARAM)&margin_out);
1385 ok(ret, "Expect ret to be true\n");
1386 ok(IsRectEmpty(&margin_out), "Expect margin empty\n");
1387
1388 /* Successful get and set text margin */
1389 ret = SendMessageA(hwnd, BCM_SETTEXTMARGIN, 0, (LPARAM)&margin_in);
1390 ok(ret, "Expect ret to be true\n");
1391 SetRectEmpty(&margin_out);
1392 ret = SendMessageA(hwnd, BCM_GETTEXTMARGIN, 0, (LPARAM)&margin_out);
1393 ok(ret, "Expect ret to be true\n");
1394 ok(EqualRect(&margin_in, &margin_out), "Expect margins to be equal\n");
1395
1396 /* BCM_SETTEXTMARGIN null pointer handling */
1397 ret = SendMessageA(hwnd, BCM_SETTEXTMARGIN, 0, 0);
1398 ok(!ret, "Expect ret to be false\n");
1399 SetRectEmpty(&margin_out);
1400 ret = SendMessageA(hwnd, BCM_GETTEXTMARGIN, 0, (LPARAM)&margin_out);
1401 ok(ret, "Expect ret to be true\n");
1402 ok(EqualRect(&margin_in, &margin_out), "Expect margins to be equal\n");
1403
1404 /* BCM_GETTEXTMARGIN null pointer handling */
1405 ret = SendMessageA(hwnd, BCM_SETTEXTMARGIN, 0, (LPARAM)&margin_in);
1406 ok(ret, "Expect ret to be true\n");
1407 ret = SendMessageA(hwnd, BCM_GETTEXTMARGIN, 0, 0);
1408 ok(!ret, "Expect ret to be true\n");
1409
1410 DestroyWindow(hwnd);
1411 }
1412 }
1413
1414 static void test_state(void)
1415 {
1416 HWND hwnd;
1417 DWORD type;
1418 LONG state;
1419
1420 /* Initial button state */
1421 for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++)
1422 {
1423 hwnd = create_button(type, NULL);
1424 state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
1425 ok(state == BST_UNCHECKED, "Expect state 0x%08x, got 0x%08x\n", BST_UNCHECKED, state);
1426 DestroyWindow(hwnd);
1427 }
1428 }
1429
1430 static void test_bcm_get_ideal_size(void)
1431 {
1432 static const char *button_text2 = "WWWW\nWWWW";
1433 static const char *button_text = "WWWW";
1434 static const DWORD imagelist_aligns[] = {BUTTON_IMAGELIST_ALIGN_LEFT, BUTTON_IMAGELIST_ALIGN_RIGHT,
1435 BUTTON_IMAGELIST_ALIGN_TOP, BUTTON_IMAGELIST_ALIGN_BOTTOM,
1436 BUTTON_IMAGELIST_ALIGN_CENTER};
1437 static const DWORD aligns[] = {0, BS_TOP, BS_LEFT, BS_RIGHT, BS_BOTTOM,
1438 BS_CENTER, BS_VCENTER, BS_RIGHTBUTTON, WS_EX_RIGHT};
1439 DWORD default_style = WS_TABSTOP | WS_POPUP | WS_VISIBLE;
1440 const LONG client_width = 400, client_height = 200;
1441 LONG image_width, height, line_count, text_width;
1442 HFONT hfont, prev_font;
1443 DWORD style, type;
1444 BOOL ret;
1445 HWND hwnd;
1446 HDC hdc;
1447 LOGFONTA lf;
1448 TEXTMETRICA tm;
1449 SIZE size;
1450 HBITMAP hmask, hbmp;
1451 ICONINFO icon_info;
1452 HICON hicon;
1453 HIMAGELIST himl;
1454 BUTTON_IMAGELIST biml = {0};
1455 RECT rect;
1456 INT i, j;
1457
1458 /* Check for NULL pointer handling */
1459 hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_PUSHBUTTON | default_style, 0, 0, client_width, client_height,
1460 NULL, NULL, 0, NULL);
1461 ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, 0);
1462 ok(!ret, "Expect BCM_GETIDEALSIZE message to return false.\n");
1463
1464 /* Set font so that the test is consistent on Wine and Windows */
1465 ZeroMemory(&lf, sizeof(lf));
1466 lf.lfWeight = FW_NORMAL;
1467 lf.lfHeight = 20;
1468 lstrcpyA(lf.lfFaceName, "Tahoma");
1469 hfont = CreateFontIndirectA(&lf);
1470 ok(hfont != NULL, "Failed to create test font.\n");
1471
1472 /* Get tmHeight */
1473 hdc = GetDC(hwnd);
1474 prev_font = SelectObject(hdc, hfont);
1475 GetTextMetricsA(hdc, &tm);
1476 SelectObject(hdc, prev_font);
1477 DrawTextA(hdc, button_text, -1, &rect, DT_CALCRECT);
1478 text_width = rect.right - rect.left;
1479 ReleaseDC(hwnd, hdc);
1480 DestroyWindow(hwnd);
1481
1482 /* XP and 2003 doesn't support command links, getting ideal size with button having only text returns client size on these platforms. */
1483 hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFCOMMANDLINK | default_style, 0, 0, client_width, client_height, NULL,
1484 NULL, 0, NULL);
1485 ok(hwnd != NULL, "Expect hwnd not NULL\n");
1486 SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
1487 ZeroMemory(&size, sizeof(size));
1488 ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
1489 ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
1490 if (size.cx == client_width && size.cy == client_height)
1491 {
1492 /* on XP and 2003, buttons with image are not supported */
1493 win_skip("Skipping further tests on XP and 2003\n");
1494 return;
1495 }
1496
1497 /* Tests for image placements */
1498 /* Prepare bitmap */
1499 image_width = 48;
1500 height = 48;
1501 hdc = GetDC(0);
1502 hmask = CreateCompatibleBitmap(hdc, image_width, height);
1503 hbmp = CreateCompatibleBitmap(hdc, image_width, height);
1504
1505 /* Only bitmap for push button, ideal size should be enough for image and text */
1506 hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFPUSHBUTTON | BS_BITMAP | default_style, 0, 0, client_width,
1507 client_height, NULL, NULL, 0, NULL);
1508 ok(hwnd != NULL, "Expect hwnd not NULL\n");
1509 SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp);
1510 SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
1511 ZeroMemory(&size, sizeof(size));
1512 ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
1513 ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
1514 /* Ideal size contains text rect even show bitmap only */
1515 ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)),
1516 "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width,
1517 size.cy, max(height, tm.tmHeight));
1518 DestroyWindow(hwnd);
1519
1520 /* Image alignments when button has bitmap and text*/
1521 for (i = 0; i < ARRAY_SIZE(aligns); i++)
1522 for (j = 0; j < ARRAY_SIZE(aligns); j++)
1523 {
1524 style = BS_DEFPUSHBUTTON | default_style | aligns[i] | aligns[j];
1525 hwnd = CreateWindowA(WC_BUTTONA, button_text, style, 0, 0, client_width, client_height, NULL, NULL, 0, NULL);
1526 ok(hwnd != NULL, "Expect hwnd not NULL\n");
1527 SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp);
1528 SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
1529 ZeroMemory(&size, sizeof(size));
1530 ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
1531 ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
1532 if (!(style & (BS_CENTER | BS_VCENTER)) || ((style & BS_CENTER) && (style & BS_CENTER) != BS_CENTER)
1533 || !(style & BS_VCENTER) || (style & BS_VCENTER) == BS_VCENTER)
1534 ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)),
1535 "Style: 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", style, size.cx,
1536 image_width + text_width, size.cy, max(height, tm.tmHeight));
1537 else
1538 ok((size.cx >= max(text_width, height) && size.cy >= height + tm.tmHeight),
1539 "Style: 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", style, size.cx,
1540 max(text_width, height), size.cy, height + tm.tmHeight);
1541 DestroyWindow(hwnd);
1542 }
1543
1544 /* Image list alignments */
1545 himl = pImageList_Create(image_width, height, ILC_COLOR, 1, 1);
1546 pImageList_Add(himl, hbmp, 0);
1547 biml.himl = himl;
1548 for (i = 0; i < ARRAY_SIZE(imagelist_aligns); i++)
1549 {
1550 biml.uAlign = imagelist_aligns[i];
1551 hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFPUSHBUTTON | default_style, 0, 0, client_width,
1552 client_height, NULL, NULL, 0, NULL);
1553 ok(hwnd != NULL, "Expect hwnd not NULL\n");
1554 SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
1555 SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml);
1556 ZeroMemory(&size, sizeof(size));
1557 ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
1558 ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
1559 if (biml.uAlign == BUTTON_IMAGELIST_ALIGN_TOP || biml.uAlign == BUTTON_IMAGELIST_ALIGN_BOTTOM)
1560 ok((size.cx >= max(text_width, height) && size.cy >= height + tm.tmHeight),
1561 "Align:%d expect ideal cx %d >= %d and ideal cy %d >= %d\n", biml.uAlign, size.cx,
1562 max(text_width, height), size.cy, height + tm.tmHeight);
1563 else if (biml.uAlign == BUTTON_IMAGELIST_ALIGN_LEFT || biml.uAlign == BUTTON_IMAGELIST_ALIGN_RIGHT)
1564 ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)),
1565 "Align:%d expect ideal cx %d >= %d and ideal cy %d >= %d\n", biml.uAlign, size.cx,
1566 image_width + text_width, size.cy, max(height, tm.tmHeight));
1567 else
1568 ok(size.cx >= image_width && size.cy >= height, "Align:%d expect ideal cx %d >= %d and ideal cy %d >= %d\n",
1569 biml.uAlign, size.cx, image_width, size.cy, height);
1570 DestroyWindow(hwnd);
1571 }
1572
1573 /* Icon as image */
1574 /* Create icon from bitmap */
1575 ZeroMemory(&icon_info, sizeof(icon_info));
1576 icon_info.fIcon = TRUE;
1577 icon_info.hbmMask = hmask;
1578 icon_info.hbmColor = hbmp;
1579 hicon = CreateIconIndirect(&icon_info);
1580
1581 /* Only icon, ideal size should be enough for image and text */
1582 hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFPUSHBUTTON | BS_ICON | default_style, 0, 0, client_width,
1583 client_height, NULL, NULL, 0, NULL);
1584 ok(hwnd != NULL, "Expect hwnd not NULL\n");
1585 SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hicon);
1586 SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
1587 ZeroMemory(&size, sizeof(size));
1588 ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
1589 ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
1590 /* Ideal size contains text rect even show icons only */
1591 ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)),
1592 "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width, size.cy,
1593 max(height, tm.tmHeight));
1594 DestroyWindow(hwnd);
1595
1596 /* Show icon and text */
1597 hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFPUSHBUTTON | default_style, 0, 0, client_width,
1598 client_height, NULL, NULL, 0, NULL);
1599 ok(hwnd != NULL, "Expect hwnd not NULL\n");
1600 SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hicon);
1601 SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
1602 ZeroMemory(&size, sizeof(size));
1603 ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
1604 ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
1605 ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)),
1606 "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width, size.cy,
1607 max(height, tm.tmHeight));
1608 DestroyWindow(hwnd);
1609
1610 /* Checkbox */
1611 /* Both bitmap and text for checkbox, ideal size is only enough for text because it doesn't support image(but not image list)*/
1612 hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_AUTOCHECKBOX | default_style, 0, 0, client_width, client_height,
1613 NULL, NULL, 0, NULL);
1614 ok(hwnd != NULL, "Expect hwnd not NULL\n");
1615 SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp);
1616 SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
1617 ZeroMemory(&size, sizeof(size));
1618 ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
1619 ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
1620 ok((size.cx <= image_width + text_width && size.cx >= text_width && size.cy <= max(height, tm.tmHeight)
1621 && size.cy >= tm.tmHeight),
1622 "Expect ideal cx %d within range (%d, %d ) and ideal cy %d within range (%d, %d )\n", size.cx,
1623 text_width, image_width + text_width, size.cy, tm.tmHeight, max(height, tm.tmHeight));
1624 DestroyWindow(hwnd);
1625
1626 /* Both image list and text for checkbox, ideal size should have enough for image list and text */
1627 biml.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT;
1628 hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_AUTOCHECKBOX | BS_BITMAP | default_style, 0, 0, client_width,
1629 client_height, NULL, NULL, 0, NULL);
1630 ok(hwnd != NULL, "Expect hwnd not NULL\n");
1631 SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml);
1632 SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
1633 ZeroMemory(&size, sizeof(size));
1634 ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
1635 ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
1636 ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)),
1637 "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width, size.cy,
1638 max(height, tm.tmHeight));
1639 DestroyWindow(hwnd);
1640
1641 /* Only bitmap for checkbox, ideal size should have enough for image and text */
1642 hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_AUTOCHECKBOX | BS_BITMAP | default_style, 0, 0, client_width,
1643 client_height, NULL, NULL, 0, NULL);
1644 ok(hwnd != NULL, "Expect hwnd not NULL\n");
1645 SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp);
1646 SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
1647 ZeroMemory(&size, sizeof(size));
1648 ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
1649 ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
1650 ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)),
1651 "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width, size.cy,
1652 max(height, tm.tmHeight));
1653 DestroyWindow(hwnd);
1654
1655 /* Test button with only text */
1656 /* No text */
1657 for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++)
1658 {
1659 style = type | default_style;
1660 hwnd = CreateWindowA(WC_BUTTONA, "", style, 0, 0, client_width, client_height, NULL, NULL, 0, NULL);
1661 ok(hwnd != NULL, "Expect hwnd not NULL\n");
1662 SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
1663
1664 ZeroMemory(&size, sizeof(size));
1665 ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
1666 ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
1667
1668 if (type == BS_COMMANDLINK || type == BS_DEFCOMMANDLINK)
1669 {
1670 todo_wine ok((size.cx == 0 && size.cy > 0), "Style 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n",
1671 style, size.cx, 0, size.cy, 0);
1672 }
1673 else
1674 {
1675 ok(size.cx == client_width && size.cy == client_height,
1676 "Style 0x%08x expect size.cx == %d and size.cy == %d, got size.cx: %d size.cy: %d\n", style,
1677 client_width, client_height, size.cx, size.cy);
1678 }
1679 DestroyWindow(hwnd);
1680 }
1681
1682 /* Single line and multiple lines text */
1683 for (line_count = 1; line_count <= 2; line_count++)
1684 {
1685 for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++)
1686 {
1687 style = line_count > 1 ? type | BS_MULTILINE : type;
1688 style |= default_style;
1689
1690 hwnd = CreateWindowA(WC_BUTTONA, (line_count == 2 ? button_text2 : button_text), style, 0, 0, client_width,
1691 client_height, NULL, NULL, 0, NULL);
1692 ok(hwnd != NULL, "Expect hwnd not NULL\n");
1693 SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
1694 ZeroMemory(&size, sizeof(size));
1695 ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
1696 ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
1697
1698 if (type == BS_3STATE || type == BS_AUTO3STATE || type == BS_GROUPBOX || type == BS_PUSHBOX
1699 || type == BS_OWNERDRAW)
1700 {
1701 ok(size.cx == client_width && size.cy == client_height,
1702 "Style 0x%08x expect ideal size (%d,%d), got (%d,%d)\n", style, client_width, client_height, size.cx,
1703 size.cy);
1704 }
1705 else if (type == BS_COMMANDLINK || type == BS_DEFCOMMANDLINK)
1706 {
1707 todo_wine ok((size.cx == 0 && size.cy > 0),
1708 "Style 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", style, size.cx, 0,
1709 size.cy, 0);
1710 }
1711 else
1712 {
1713 height = line_count == 2 ? 2 * tm.tmHeight : tm.tmHeight;
1714 ok(size.cx >= 0 && size.cy >= height, "Style 0x%08x expect ideal cx %d >= 0 and ideal cy %d >= %d\n",
1715 style, size.cx, size.cy, height);
1716 }
1717 DestroyWindow(hwnd);
1718 }
1719 }
1720
1721 pImageList_Destroy(himl);
1722 DestroyIcon(hicon);
1723 DeleteObject(hbmp);
1724 DeleteObject(hmask);
1725 ReleaseDC(0, hdc);
1726 DeleteObject(hfont);
1727 }
1728
1729 START_TEST(button)
1730 {
1731 ULONG_PTR ctx_cookie;
1732 HANDLE hCtx;
1733
1734 if (!load_v6_module(&ctx_cookie, &hCtx))
1735 return;
1736
1737 register_parent_class();
1738
1739 init_functions();
1740 init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
1741
1742 test_button_class();
1743 test_button_messages();
1744 test_note();
1745 test_button_data();
1746 test_bm_get_set_image();
1747 test_get_set_imagelist();
1748 test_get_set_textmargin();
1749 test_state();
1750 test_bcm_get_ideal_size();
1751
1752 unload_v6_module(ctx_cookie, hCtx);
1753 }