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 PARENT_CD_SEQ_INDEX 1
52 #define NUM_MSG_SEQUENCES   2
53 
54 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
55 
56 struct wndclass_redirect_data
57 {
58     ULONG size;
59     DWORD res;
60     ULONG name_len;
61     ULONG name_offset;
62     ULONG module_len;
63     ULONG module_offset;
64 };
65 
66 /* returned pointer is valid as long as activation context is alive */
67 static WCHAR* get_versioned_classname(const WCHAR *name)
68 {
69     struct wndclass_redirect_data *wnddata;
70     ACTCTX_SECTION_KEYED_DATA data;
71     BOOL ret;
72 
73     memset(&data, 0, sizeof(data));
74     data.cbSize = sizeof(data);
75     ret = FindActCtxSectionStringW(0, NULL, ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION, name, &data);
76     ok(ret, "Failed to find class redirection section, error %u\n", GetLastError());
77     wnddata = (struct wndclass_redirect_data*)data.lpData;
78     return (WCHAR*)((BYTE*)wnddata + wnddata->name_offset);
79 }
80 
81 static void init_functions(void)
82 {
83     HMODULE hmod = GetModuleHandleA("comctl32.dll");
84     ok(hmod != NULL, "got %p\n", hmod);
85 
86 #define MAKEFUNC_ORD(f, ord) (p##f = (void*)GetProcAddress(hmod, (LPSTR)(ord)))
87     MAKEFUNC_ORD(SetWindowSubclass, 410);
88     MAKEFUNC_ORD(RemoveWindowSubclass, 412);
89     MAKEFUNC_ORD(DefSubclassProc, 413);
90 #undef MAKEFUNC_ORD
91 
92 #define X(f) p##f = (void *)GetProcAddress(hmod, #f);
93     X(ImageList_Create);
94     X(ImageList_Add);
95     X(ImageList_Destroy);
96 #undef X
97 }
98 
99 /* try to make sure pending X events have been processed before continuing */
100 static void flush_events(void)
101 {
102     MSG msg;
103     int diff = 200;
104     int min_timeout = 100;
105     DWORD time = GetTickCount() + diff;
106 
107     while (diff > 0)
108     {
109         if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min_timeout, QS_ALLINPUT ) == WAIT_TIMEOUT) break;
110         while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg );
111         diff = time - GetTickCount();
112     }
113 }
114 
115 static BOOL ignore_message( UINT message )
116 {
117     /* these are always ignored */
118     return (message >= 0xc000 ||
119             message == WM_GETICON ||
120             message == WM_GETOBJECT ||
121             message == WM_TIMECHANGE ||
122             message == WM_DISPLAYCHANGE ||
123             message == WM_DEVICECHANGE ||
124             message == WM_DWMNCRENDERINGCHANGED ||
125             message == WM_GETTEXTLENGTH ||
126             message == WM_GETTEXT);
127 }
128 
129 static LRESULT CALLBACK button_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR id, DWORD_PTR ref_data)
130 {
131     static LONG defwndproc_counter = 0;
132     struct message msg = { 0 };
133     LRESULT ret;
134 
135     if (ignore_message( message )) return pDefSubclassProc(hwnd, message, wParam, lParam);
136 
137     switch (message)
138     {
139     case WM_SYNCPAINT:
140         break;
141     case BM_SETSTATE:
142         if (GetCapture())
143             ok(GetCapture() == hwnd, "GetCapture() = %p\n", GetCapture());
144         /* fall through */
145     default:
146         msg.message = message;
147         msg.flags = sent|wparam|lparam;
148         if (defwndproc_counter) msg.flags |= defwinproc;
149         msg.wParam = wParam;
150         msg.lParam = lParam;
151         add_message(sequences, COMBINED_SEQ_INDEX, &msg);
152     }
153 
154     if (message == WM_NCDESTROY)
155         pRemoveWindowSubclass(hwnd, button_subclass_proc, 0);
156 
157     defwndproc_counter++;
158     ret = pDefSubclassProc(hwnd, message, wParam, lParam);
159     defwndproc_counter--;
160 
161     return ret;
162 }
163 
164 static struct
165 {
166     DWORD button;
167     UINT line;
168     UINT state;
169     DWORD ret;
170     BOOL empty;
171 } test_cd;
172 
173 #define set_test_cd_state(s) do { \
174     test_cd.state = (s); \
175     test_cd.empty = TRUE; \
176     test_cd.line = __LINE__; \
177 } while (0)
178 
179 #define set_test_cd_ret(r) do { \
180     test_cd.ret = (r); \
181     test_cd.empty = TRUE; \
182     test_cd.line = __LINE__; \
183 } while (0)
184 
185 static void disable_test_cd(void)
186 {
187     test_cd.line = 0;
188 }
189 
190 static LRESULT WINAPI test_parent_wndproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
191 {
192     static LONG defwndproc_counter = 0;
193     static LONG beginpaint_counter = 0;
194     static HDC cd_first_hdc;
195     struct message msg = { 0 };
196     NMCUSTOMDRAW *cd = (NMCUSTOMDRAW*)lParam;
197     NMBCDROPDOWN *bcd = (NMBCDROPDOWN*)lParam;
198     LRESULT ret;
199 
200     if (ignore_message( message )) return 0;
201 
202     if (message == WM_PARENTNOTIFY || message == WM_CANCELMODE ||
203         message == WM_SETFOCUS || message == WM_KILLFOCUS ||
204         message == WM_ENABLE || message == WM_ENTERIDLE ||
205         message == WM_DRAWITEM || message == WM_COMMAND ||
206         message == WM_IME_SETCONTEXT)
207     {
208         msg.message = message;
209         msg.flags = sent|parent|wparam|lparam;
210         if (defwndproc_counter) msg.flags |= defwinproc;
211         if (beginpaint_counter) msg.flags |= beginpaint;
212         msg.wParam = wParam;
213         msg.lParam = lParam;
214         add_message(sequences, COMBINED_SEQ_INDEX, &msg);
215     }
216 
217     if (message == WM_NOTIFY && cd->hdr.code == NM_CUSTOMDRAW && test_cd.line)
218     {
219         /* Ignore an inconsistency across Windows versions */
220         UINT state = cd->uItemState & ~CDIS_SHOWKEYBOARDCUES;
221 
222         /* Some Windows configurations paint twice with different DC */
223         if (test_cd.empty)
224         {
225             cd_first_hdc = cd->hdc;
226             test_cd.empty = FALSE;
227         }
228 
229         ok_(__FILE__,test_cd.line)(!(cd->dwDrawStage & CDDS_ITEM),
230             "[%u] CDDS_ITEM is set\n", test_cd.button);
231 
232         ok_(__FILE__,test_cd.line)(state == test_cd.state,
233             "[%u] expected uItemState %u, got %u\n", test_cd.button,
234             test_cd.state, state);
235 
236         msg.message = message;
237         msg.flags = sent|parent|wparam|lparam|id|custdraw;
238         msg.wParam = wParam;
239         msg.lParam = lParam;
240         msg.id = NM_CUSTOMDRAW;
241         msg.stage = cd->dwDrawStage;
242         if (cd->hdc == cd_first_hdc)
243             add_message(sequences, PARENT_CD_SEQ_INDEX, &msg);
244 
245         ret = test_cd.ret;
246         switch (msg.stage)
247         {
248             case CDDS_PREERASE:
249                 ret &= ~CDRF_NOTIFYPOSTPAINT;
250                 cd->dwItemSpec = 0xdeadbeef;
251                 break;
252             case CDDS_PREPAINT:
253                 ret &= ~CDRF_NOTIFYPOSTERASE;
254                 break;
255             case CDDS_POSTERASE:
256             case CDDS_POSTPAINT:
257                 ok_(__FILE__,test_cd.line)(cd->dwItemSpec == 0xdeadbeef,
258                     "[%u] NMCUSTOMDRAW was not shared, stage %u\n", test_cd.button, msg.stage);
259                 break;
260         }
261         return ret;
262     }
263 
264     if (message == WM_NOTIFY && bcd->hdr.code == BCN_DROPDOWN)
265     {
266         UINT button = GetWindowLongW(bcd->hdr.hwndFrom, GWL_STYLE) & BS_TYPEMASK;
267         RECT rc;
268 
269         GetClientRect(bcd->hdr.hwndFrom, &rc);
270 
271         ok(bcd->hdr.hwndFrom != NULL, "Received BCN_DROPDOWN with no hwnd attached, wParam %lu id %lu\n",
272            wParam, bcd->hdr.idFrom);
273         ok(bcd->hdr.idFrom == wParam, "[%u] Mismatch between wParam (%lu) and idFrom (%lu)\n",
274            button, wParam, bcd->hdr.idFrom);
275         ok(EqualRect(&rc, &bcd->rcButton), "[%u] Wrong rcButton, expected %s got %s\n",
276            button, wine_dbgstr_rect(&rc), wine_dbgstr_rect(&bcd->rcButton));
277 
278         msg.message = message;
279         msg.flags = sent|parent|wparam|lparam|id;
280         msg.wParam = wParam;
281         msg.lParam = lParam;
282         msg.id = BCN_DROPDOWN;
283         add_message(sequences, COMBINED_SEQ_INDEX, &msg);
284         return 0;
285     }
286 
287     if (message == WM_PAINT)
288     {
289         PAINTSTRUCT ps;
290         beginpaint_counter++;
291         BeginPaint( hwnd, &ps );
292         beginpaint_counter--;
293         EndPaint( hwnd, &ps );
294         return 0;
295     }
296 
297     defwndproc_counter++;
298     ret = DefWindowProcA(hwnd, message, wParam, lParam);
299     defwndproc_counter--;
300 
301     return ret;
302 }
303 
304 static const struct message setfocus_seq[] =
305 {
306     { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
307     { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
308     { BM_GETSTATE, sent|optional }, /* when touchscreen is present */
309     { WM_SETFOCUS, sent|wparam },
310     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) },
311     { WM_APP, sent|wparam|lparam },
312     { WM_PAINT, sent },
313     { 0 }
314 };
315 
316 static const struct message killfocus_seq[] =
317 {
318     { WM_KILLFOCUS, sent|wparam, 0 },
319     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_KILLFOCUS) },
320     { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
321     { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 1 },
322     { WM_APP, sent|wparam|lparam, 0, 0 },
323     { WM_PAINT, sent },
324     { 0 }
325 };
326 
327 static const struct message setfocus_static_seq[] =
328 {
329     { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
330     { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
331     { BM_GETSTATE, sent|optional }, /* when touchscreen is present */
332     { WM_SETFOCUS, sent|wparam, 0 },
333     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) },
334     { WM_COMMAND, sent|wparam|parent|optional, MAKEWPARAM(ID_BUTTON, BN_CLICKED) }, /* radio button */
335     { WM_APP, sent|wparam|lparam, 0, 0 },
336     { WM_PAINT, sent },
337     { 0 }
338 };
339 
340 static const struct message setfocus_groupbox_seq[] =
341 {
342     { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
343     { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
344     { BM_GETSTATE, sent|optional }, /* when touchscreen is present */
345     { WM_SETFOCUS, sent|wparam, 0 },
346     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) },
347     { WM_COMMAND, sent|wparam|parent|optional, MAKEWPARAM(ID_BUTTON, BN_CLICKED) }, /* radio button */
348     { WM_APP, sent|wparam|lparam, 0, 0 },
349     { WM_PAINT, sent },
350     { 0 }
351 };
352 
353 static const struct message killfocus_static_seq[] =
354 {
355     { WM_KILLFOCUS, sent|wparam, 0 },
356     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_KILLFOCUS) },
357     { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
358     { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 1 },
359     { WM_APP, sent|wparam|lparam, 0, 0 },
360     { WM_PAINT, sent },
361     { 0 }
362 };
363 
364 static const struct message setfocus_ownerdraw_seq[] =
365 {
366     { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
367     { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
368     { BM_GETSTATE, sent|optional }, /* when touchscreen is present */
369     { WM_SETFOCUS, sent|wparam, 0 },
370     { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
371     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) },
372     { WM_APP, sent|wparam|lparam, 0, 0 },
373     { WM_IME_SETCONTEXT, sent|wparam|optional, 1 },
374     { 0 }
375 };
376 
377 static const struct message killfocus_ownerdraw_seq[] =
378 {
379     { WM_KILLFOCUS, sent|wparam, 0 },
380     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_KILLFOCUS) },
381     { WM_IME_SETCONTEXT, sent|wparam|optional, 0 },
382     { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 1 },
383     { WM_APP, sent|wparam|lparam, 0, 0 },
384     { WM_PAINT, sent },
385     { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
386     { 0 }
387 };
388 
389 static const struct message lbuttondown_seq[] =
390 {
391     { WM_LBUTTONDOWN, sent|wparam|lparam, 0, 0 },
392     { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 },
393     { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 },
394     { BM_GETSTATE, sent|defwinproc|optional }, /* when touchscreen is present */
395     { WM_SETFOCUS, sent|wparam|defwinproc, 0 },
396     { BM_SETSTATE, sent|wparam|defwinproc, TRUE },
397     { 0 }
398 };
399 
400 static const struct message lbuttonup_seq[] =
401 {
402     { WM_LBUTTONUP, sent|wparam|lparam, 0, 0 },
403     { BM_SETSTATE, sent|wparam|defwinproc, FALSE },
404     { WM_CAPTURECHANGED, sent|wparam|defwinproc, 0 },
405     { WM_COMMAND, sent|wparam|defwinproc, 0 },
406     { 0 }
407 };
408 
409 static const struct message setfont_seq[] =
410 {
411     { WM_SETFONT, sent },
412     { 0 }
413 };
414 
415 static const struct message setstyle_seq[] =
416 {
417     { BM_SETSTYLE, sent },
418     { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
419     { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
420     { WM_APP, sent|wparam|lparam, 0, 0 },
421     { WM_PAINT, sent },
422     { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */
423     { WM_ERASEBKGND, sent|defwinproc|optional },
424     { WM_PAINT, sent|optional },
425     { 0 }
426 };
427 
428 static const struct message setstyle_static_seq[] =
429 {
430     { BM_SETSTYLE, sent },
431     { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
432     { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
433     { WM_APP, sent|wparam|lparam, 0, 0 },
434     { WM_PAINT, sent },
435     { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */
436     { WM_ERASEBKGND, sent|defwinproc|optional },
437     { 0 }
438 };
439 
440 static const struct message setstyle_user_seq[] =
441 {
442     { BM_SETSTYLE, sent },
443     { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
444     { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
445     { WM_APP, sent|wparam|lparam, 0, 0 },
446     { WM_PAINT, sent },
447     { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */
448     { WM_ERASEBKGND, sent|defwinproc|optional },
449     { 0 }
450 };
451 
452 static const struct message setstyle_ownerdraw_seq[] =
453 {
454     { BM_SETSTYLE, sent },
455     { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
456     { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
457     { WM_APP, sent|wparam|lparam, 0, 0 },
458     { WM_PAINT, sent },
459     { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
460     { WM_ERASEBKGND, sent|defwinproc|optional },
461     { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
462     { 0 }
463 };
464 
465 static const struct message setstate_seq[] =
466 {
467     { BM_SETSTATE, sent },
468     { WM_APP, sent|wparam|lparam, 0, 0 },
469     { WM_PAINT, sent },
470     { WM_PAINT, sent|optional },
471     { 0 }
472 };
473 
474 static const struct message setstate_static_seq[] =
475 {
476     { BM_SETSTATE, sent },
477     { WM_APP, sent|wparam|lparam, 0, 0 },
478     { WM_PAINT, sent },
479     { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
480     { WM_ERASEBKGND, sent|defwinproc|optional },
481     { 0 }
482 };
483 
484 static const struct message setstate_user_seq[] =
485 {
486     { BM_SETSTATE, sent },
487     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_HILITE) },
488     { WM_APP, sent|wparam|lparam, 0, 0 },
489     { WM_PAINT, sent },
490     { 0 }
491 };
492 
493 static const struct message setstate_ownerdraw_seq[] =
494 {
495     { BM_SETSTATE, sent },
496     { WM_APP, sent|wparam|lparam, 0, 0 },
497     { WM_PAINT, sent },
498     { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
499     { WM_ERASEBKGND, sent|defwinproc|optional },
500     { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
501     { 0 }
502 };
503 
504 static const struct message clearstate_seq[] =
505 {
506     { BM_SETSTATE, sent },
507     { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_UNHILITE) },
508     { WM_APP, sent|wparam|lparam, 0, 0 },
509     { WM_PAINT, sent },
510     { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
511     { WM_ERASEBKGND, sent|defwinproc|optional },
512     { 0 }
513 };
514 
515 static const struct message clearstate_ownerdraw_seq[] =
516 {
517     { BM_SETSTATE, sent },
518     { WM_APP, sent|wparam|lparam, 0, 0 },
519     { WM_PAINT, sent },
520     { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
521     { WM_ERASEBKGND, sent|defwinproc|optional },
522     { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON },
523     { 0 }
524 };
525 
526 static const struct message setcheck_ignored_seq[] =
527 {
528     { BM_SETCHECK, sent },
529     { WM_APP, sent|wparam|lparam, 0, 0 },
530     { WM_PAINT, sent|optional },
531     { 0 }
532 };
533 
534 static const struct message setcheck_static_seq[] =
535 {
536     { BM_SETCHECK, sent },
537     { WM_APP, sent|wparam|lparam, 0, 0 },
538     { WM_PAINT, sent },
539     { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */
540     { WM_ERASEBKGND, sent|defwinproc|optional },
541     { 0 }
542 };
543 
544 static const struct message setcheck_radio_seq[] =
545 {
546     { BM_SETCHECK, sent },
547     { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
548     { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
549     { WM_APP, sent|wparam|lparam, 0, 0 },
550     { 0 }
551 };
552 
553 static const struct message setcheck_radio_redraw_seq[] =
554 {
555     { BM_SETCHECK, sent },
556     { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE },
557     { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE },
558     { WM_APP, sent|wparam|lparam, 0, 0 },
559     { WM_PAINT, sent },
560     { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */
561     { 0 }
562 };
563 
564 static const struct message empty_cd_seq[] = { { 0 } };
565 
566 static const struct message pre_cd_seq[] =
567 {
568     { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE },
569     { 0 }
570 };
571 
572 static const struct message pre_pre_cd_seq[] =
573 {
574     { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE },
575     { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREPAINT },
576     { 0 }
577 };
578 
579 static const struct message pre_post_pre_cd_seq[] =
580 {
581     { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE },
582     { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_POSTERASE },
583     { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREPAINT },
584     { 0 }
585 };
586 
587 static const struct message pre_pre_post_cd_seq[] =
588 {
589     { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE },
590     { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREPAINT },
591     { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_POSTPAINT },
592     { 0 }
593 };
594 
595 static const struct message pre_post_pre_post_cd_seq[] =
596 {
597     { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREERASE },
598     { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_POSTERASE },
599     { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_PREPAINT },
600     { WM_NOTIFY, sent|parent|id|custdraw, 0, 0, NM_CUSTOMDRAW, CDDS_POSTPAINT },
601     { 0 }
602 };
603 
604 static const struct message bcn_dropdown_seq[] =
605 {
606     { WM_KEYDOWN, sent|wparam|lparam, VK_DOWN, 0 },
607     { BCM_SETDROPDOWNSTATE, sent|wparam|lparam|defwinproc, 1, 0 },
608     { WM_NOTIFY, sent|parent|id, 0, 0, BCN_DROPDOWN },
609     { BCM_SETDROPDOWNSTATE, sent|wparam|lparam|defwinproc, 0, 0 },
610     { WM_KEYUP, sent|wparam|lparam, VK_DOWN, 0xc0000000 },
611     { WM_PAINT, sent },
612     { WM_DRAWITEM, sent|parent|optional },  /* for owner draw button */
613     { WM_PAINT, sent|optional },            /* sometimes sent rarely */
614     { WM_DRAWITEM, sent|parent|optional },
615     { 0 }
616 };
617 
618 static HWND create_button(DWORD style, HWND parent)
619 {
620     HMENU menuid = 0;
621     HWND hwnd;
622 
623     if (parent)
624     {
625         style |= WS_CHILD|BS_NOTIFY;
626         menuid = (HMENU)ID_BUTTON;
627     }
628     hwnd = CreateWindowExA(0, WC_BUTTONA, "test", style, 0, 0, 50, 14, parent, menuid, 0, NULL);
629     ok(hwnd != NULL, "failed to create a button, 0x%08x, %p\n", style, parent);
630     pSetWindowSubclass(hwnd, button_subclass_proc, 0, 0);
631     return hwnd;
632 }
633 
634 static void test_button_messages(void)
635 {
636     enum cd_seq_type
637     {
638         cd_seq_empty,
639         cd_seq_normal,
640         cd_seq_optional
641     };
642 
643     static const struct
644     {
645         DWORD style;
646         DWORD dlg_code;
647         const struct message *setfocus;
648         const struct message *killfocus;
649         const struct message *setstyle;
650         const struct message *setstate;
651         const struct message *clearstate;
652         const struct message *setcheck;
653         enum cd_seq_type cd_setfocus_type;
654         enum cd_seq_type cd_setstyle_type;
655         enum cd_seq_type cd_setstate_type;
656         enum cd_seq_type cd_setcheck_type;
657     } button[] = {
658         { BS_PUSHBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
659           setfocus_seq, killfocus_seq, setstyle_seq,
660           setstate_seq, setstate_seq, setcheck_ignored_seq,
661           cd_seq_normal, cd_seq_normal, cd_seq_normal, cd_seq_optional },
662         { BS_DEFPUSHBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON,
663           setfocus_seq, killfocus_seq, setstyle_seq,
664           setstate_seq, setstate_seq, setcheck_ignored_seq,
665           cd_seq_normal, cd_seq_normal, cd_seq_normal, cd_seq_optional },
666         { BS_CHECKBOX, DLGC_BUTTON,
667           setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
668           setstate_static_seq, setstate_static_seq, setcheck_static_seq,
669           cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional },
670         { BS_AUTOCHECKBOX, DLGC_BUTTON,
671           setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
672           setstate_static_seq, setstate_static_seq, setcheck_static_seq,
673           cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional },
674         { BS_RADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
675           setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
676           setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq,
677           cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional },
678         { BS_3STATE, DLGC_BUTTON,
679           setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
680           setstate_static_seq, setstate_static_seq, setcheck_static_seq,
681           cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional },
682         { BS_AUTO3STATE, DLGC_BUTTON,
683           setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
684           setstate_static_seq, setstate_static_seq, setcheck_static_seq,
685           cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional },
686         { BS_GROUPBOX, DLGC_STATIC,
687           setfocus_groupbox_seq, killfocus_static_seq, setstyle_static_seq,
688           setstate_static_seq, setstate_static_seq, setcheck_ignored_seq,
689           cd_seq_empty, cd_seq_empty, cd_seq_empty, cd_seq_empty },
690         { BS_USERBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
691           setfocus_seq, killfocus_seq, setstyle_user_seq,
692           setstate_user_seq, clearstate_seq, setcheck_ignored_seq,
693           cd_seq_normal, cd_seq_empty, cd_seq_empty, cd_seq_empty },
694         { BS_AUTORADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON,
695           setfocus_static_seq, killfocus_static_seq, setstyle_static_seq,
696           setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq,
697           cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_optional },
698         { BS_OWNERDRAW, DLGC_BUTTON,
699           setfocus_ownerdraw_seq, killfocus_ownerdraw_seq, setstyle_ownerdraw_seq,
700           setstate_ownerdraw_seq, clearstate_ownerdraw_seq, setcheck_ignored_seq,
701           cd_seq_empty, cd_seq_empty, cd_seq_empty, cd_seq_empty },
702         { BS_SPLITBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON | DLGC_WANTARROWS,
703           setfocus_seq, killfocus_seq, setstyle_seq,
704           setstate_seq, setstate_seq, setcheck_ignored_seq,
705           cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_empty },
706         { BS_DEFSPLITBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON | DLGC_WANTARROWS,
707           setfocus_seq, killfocus_seq, setstyle_seq,
708           setstate_seq, setstate_seq, setcheck_ignored_seq,
709           cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_empty },
710         { BS_COMMANDLINK, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON,
711           setfocus_seq, killfocus_seq, setstyle_seq,
712           setstate_seq, setstate_seq, setcheck_ignored_seq,
713           cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_empty },
714         { BS_DEFCOMMANDLINK, DLGC_BUTTON | DLGC_DEFPUSHBUTTON,
715           setfocus_seq, killfocus_seq, setstyle_seq,
716           setstate_seq, setstate_seq, setcheck_ignored_seq,
717           cd_seq_optional, cd_seq_optional, cd_seq_optional, cd_seq_empty }
718     };
719     LOGFONTA logfont = { 0 };
720     const struct message *seq, *cd_seq;
721     HFONT zfont, hfont2;
722     unsigned int i;
723     HWND hwnd, parent;
724     DWORD dlg_code;
725     BOOL todo;
726 
727     /* selection with VK_SPACE should capture button window */
728     hwnd = create_button(BS_CHECKBOX | WS_VISIBLE | WS_POPUP, NULL);
729     ok(hwnd != 0, "Failed to create button window\n");
730     ReleaseCapture();
731     SetFocus(hwnd);
732     SendMessageA(hwnd, WM_KEYDOWN, VK_SPACE, 0);
733     ok(GetCapture() == hwnd, "Should be captured on VK_SPACE WM_KEYDOWN\n");
734     SendMessageA(hwnd, WM_KEYUP, VK_SPACE, 0);
735     DestroyWindow(hwnd);
736 
737     parent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
738                              100, 100, 200, 200, 0, 0, 0, NULL);
739     ok(parent != 0, "Failed to create parent window\n");
740 
741     logfont.lfHeight = -12;
742     logfont.lfWeight = FW_NORMAL;
743     strcpy(logfont.lfFaceName, "Tahoma");
744 
745     hfont2 = CreateFontIndirectA(&logfont);
746     ok(hfont2 != NULL, "Failed to create Tahoma font\n");
747 
748 #define check_cd_seq(type, context) do { \
749         if (button[i].type != cd_seq_optional || !test_cd.empty) \
750             ok_sequence(sequences, PARENT_CD_SEQ_INDEX, cd_seq, "[CustomDraw] " context, FALSE); \
751     } while(0)
752 
753     for (i = 0; i < ARRAY_SIZE(button); i++)
754     {
755         HFONT prevfont, hfont;
756         MSG msg;
757         DWORD style, state;
758         HDC hdc;
759 
760         test_cd.button = button[i].style;
761         hwnd = create_button(button[i].style, parent);
762         ok(hwnd != NULL, "Failed to create a button.\n");
763 
764         style = GetWindowLongA(hwnd, GWL_STYLE);
765         style &= ~(WS_CHILD | BS_NOTIFY);
766         /* XP turns a BS_USERBUTTON into BS_PUSHBUTTON */
767         if (button[i].style == BS_USERBUTTON)
768             ok(style == BS_PUSHBUTTON, "expected style BS_PUSHBUTTON got %x\n", style);
769         else
770             ok(style == button[i].style, "expected style %x got %x\n", button[i].style, style);
771 
772         dlg_code = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0);
773         if (button[i].style == BS_SPLITBUTTON ||
774                 button[i].style == BS_DEFSPLITBUTTON ||
775                 button[i].style == BS_COMMANDLINK ||
776                 button[i].style == BS_DEFCOMMANDLINK)
777         {
778             ok(dlg_code == button[i].dlg_code || broken(dlg_code == DLGC_BUTTON) /* WinXP */, "%u: wrong dlg_code %08x\n", i, dlg_code);
779         }
780         else
781             ok(dlg_code == button[i].dlg_code, "%u: wrong dlg_code %08x\n", i, dlg_code);
782 
783         ShowWindow(hwnd, SW_SHOW);
784         UpdateWindow(hwnd);
785         SetFocus(0);
786         flush_events();
787         SetFocus(0);
788         cd_seq = (button[i].cd_setfocus_type == cd_seq_empty) ? empty_cd_seq : pre_pre_cd_seq;
789         flush_sequences(sequences, NUM_MSG_SEQUENCES);
790         set_test_cd_ret(CDRF_DODEFAULT);
791         set_test_cd_state(CDIS_FOCUS);
792 
793         ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus());
794         SetFocus(hwnd);
795         SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
796         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
797         ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setfocus, "SetFocus(hwnd) on a button", FALSE);
798         check_cd_seq(cd_setfocus_type, "SetFocus(hwnd)");
799 
800         set_test_cd_state(0);
801         SetFocus(0);
802         SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
803         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
804         ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].killfocus, "SetFocus(0) on a button", FALSE);
805         check_cd_seq(cd_setfocus_type, "SetFocus(0)");
806         ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus());
807 
808         cd_seq = (button[i].cd_setstyle_type == cd_seq_empty) ? empty_cd_seq : pre_pre_cd_seq;
809         set_test_cd_state(0);
810 
811         SendMessageA(hwnd, BM_SETSTYLE, button[i].style | BS_BOTTOM, TRUE);
812         SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
813         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
814         todo = button[i].style == BS_OWNERDRAW;
815         ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setstyle, "BM_SETSTYLE on a button", todo);
816         check_cd_seq(cd_setstyle_type, "BM_SETSTYLE");
817 
818         style = GetWindowLongA(hwnd, GWL_STYLE);
819         style &= ~(WS_VISIBLE | WS_CHILD | BS_NOTIFY);
820         /* XP doesn't turn a BS_USERBUTTON into BS_PUSHBUTTON here! */
821         ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
822 
823         state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
824         ok(state == 0, "expected state 0, got %04x\n", state);
825 
826         cd_seq = (button[i].cd_setstate_type == cd_seq_empty) ? empty_cd_seq : pre_pre_cd_seq;
827         flush_sequences(sequences, NUM_MSG_SEQUENCES);
828         set_test_cd_state(CDIS_SELECTED);
829 
830         SendMessageA(hwnd, BM_SETSTATE, TRUE, 0);
831         SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
832         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
833         ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setstate, "BM_SETSTATE/TRUE on a button", FALSE);
834         check_cd_seq(cd_setstate_type, "BM_SETSTATE/TRUE");
835 
836         state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
837         ok(state == BST_PUSHED, "expected state 0x0004, got %04x\n", state);
838 
839         style = GetWindowLongA(hwnd, GWL_STYLE);
840         style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
841         ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
842 
843         flush_sequences(sequences, NUM_MSG_SEQUENCES);
844         set_test_cd_state(0);
845 
846         SendMessageA(hwnd, BM_SETSTATE, FALSE, 0);
847         SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
848         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
849         ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].clearstate, "BM_SETSTATE/FALSE on a button", FALSE);
850         check_cd_seq(cd_setstate_type, "BM_SETSTATE/FALSE");
851 
852         state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
853         ok(state == 0, "expected state 0, got %04x\n", state);
854 
855         style = GetWindowLongA(hwnd, GWL_STYLE);
856         style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
857         ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
858 
859         state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
860         ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state);
861 
862         cd_seq = (button[i].cd_setcheck_type == cd_seq_empty) ? empty_cd_seq : pre_pre_cd_seq;
863         flush_sequences(sequences, NUM_MSG_SEQUENCES);
864         set_test_cd_state(0);
865 
866         if (button[i].style == BS_RADIOBUTTON ||
867             button[i].style == BS_AUTORADIOBUTTON)
868         {
869             seq = setcheck_radio_seq;
870         }
871         else
872             seq = setcheck_ignored_seq;
873 
874         SendMessageA(hwnd, BM_SETCHECK, BST_UNCHECKED, 0);
875         SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
876         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
877         ok_sequence(sequences, COMBINED_SEQ_INDEX, seq, "BM_SETCHECK on a button", FALSE);
878         check_cd_seq(cd_setcheck_type, "BM_SETCHECK");
879 
880         state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
881         ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state);
882 
883         style = GetWindowLongA(hwnd, GWL_STYLE);
884         style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
885         ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
886 
887         flush_sequences(sequences, NUM_MSG_SEQUENCES);
888         set_test_cd_state(0);
889 
890         SendMessageA(hwnd, BM_SETCHECK, BST_CHECKED, 0);
891         SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */
892         while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
893         ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setcheck, "BM_SETCHECK on a button", FALSE);
894         check_cd_seq(cd_setcheck_type, "BM_SETCHECK");
895 
896         state = SendMessageA(hwnd, BM_GETCHECK, 0, 0);
897         if (button[i].style == BS_PUSHBUTTON ||
898             button[i].style == BS_DEFPUSHBUTTON ||
899             button[i].style == BS_GROUPBOX ||
900             button[i].style == BS_USERBUTTON ||
901             button[i].style == BS_OWNERDRAW ||
902             button[i].style == BS_SPLITBUTTON ||
903             button[i].style == BS_DEFSPLITBUTTON ||
904             button[i].style == BS_COMMANDLINK ||
905             button[i].style == BS_DEFCOMMANDLINK)
906         {
907             ok(state == BST_UNCHECKED, "expected check BST_UNCHECKED, got %04x\n", state);
908         }
909         else
910             ok(state == BST_CHECKED, "expected check BST_CHECKED, got %04x\n", state);
911 
912         style = GetWindowLongA(hwnd, GWL_STYLE);
913         style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE);
914         if (button[i].style == BS_RADIOBUTTON ||
915             button[i].style == BS_AUTORADIOBUTTON)
916             ok(style == (button[i].style | WS_TABSTOP), "expected style %04x | WS_TABSTOP got %04x\n", button[i].style, style);
917         else
918             ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style);
919 
920         /* Test that original font is not selected back after painting */
921         hfont = (HFONT)SendMessageA(hwnd, WM_GETFONT, 0, 0);
922         ok(hfont == NULL, "Unexpected control font.\n");
923 
924         SendMessageA(hwnd, WM_SETFONT, (WPARAM)GetStockObject(SYSTEM_FONT), 0);
925 
926         hdc = CreateCompatibleDC(0);
927 
928         prevfont = SelectObject(hdc, hfont2);
929         SendMessageA(hwnd, WM_PRINTCLIENT, (WPARAM)hdc, 0);
930         ok(hfont2 != GetCurrentObject(hdc, OBJ_FONT) || broken(hfont2 == GetCurrentObject(hdc, OBJ_FONT)) /* WinXP */,
931             "button[%u]: unexpected font selected after WM_PRINTCLIENT\n", i);
932         SelectObject(hdc, prevfont);
933 
934         prevfont = SelectObject(hdc, hfont2);
935         SendMessageA(hwnd, WM_PAINT, (WPARAM)hdc, 0);
936         ok(hfont2 != GetCurrentObject(hdc, OBJ_FONT) || broken(hfont2 == GetCurrentObject(hdc, OBJ_FONT)) /* WinXP */,
937             "button[%u]: unexpected font selected after WM_PAINT\n", i);
938         SelectObject(hdc, prevfont);
939 
940         DeleteDC(hdc);
941 
942         /* Test Custom Draw return values */
943         if (button[i].cd_setfocus_type != cd_seq_empty &&
944             broken(button[i].style != BS_USERBUTTON) /* WinXP */)
945         {
946             static const struct
947             {
948                 const char *context;
949                 LRESULT val;
950                 const struct message *seq;
951             } ret[] = {
952                 { "CDRF_DODEFAULT", CDRF_DODEFAULT, pre_pre_cd_seq },
953                 { "CDRF_DOERASE", CDRF_DOERASE, pre_pre_cd_seq },
954                 { "CDRF_SKIPDEFAULT", CDRF_SKIPDEFAULT, pre_cd_seq },
955                 { "CDRF_SKIPDEFAULT | CDRF_NOTIFYPOSTERASE | CDRF_NOTIFYPOSTPAINT",
956                    CDRF_SKIPDEFAULT | CDRF_NOTIFYPOSTERASE | CDRF_NOTIFYPOSTPAINT, pre_cd_seq },
957                 { "CDRF_NOTIFYPOSTERASE", CDRF_NOTIFYPOSTERASE, pre_post_pre_cd_seq },
958                 { "CDRF_NOTIFYPOSTPAINT", CDRF_NOTIFYPOSTPAINT, pre_pre_post_cd_seq },
959                 { "CDRF_NOTIFYPOSTERASE | CDRF_NOTIFYPOSTPAINT",
960                    CDRF_NOTIFYPOSTERASE | CDRF_NOTIFYPOSTPAINT, pre_post_pre_post_cd_seq },
961             };
962             UINT k;
963 
964             for (k = 0; k < ARRAY_SIZE(ret); k++)
965             {
966                 disable_test_cd();
967                 SetFocus(0);
968                 set_test_cd_ret(ret[k].val);
969                 set_test_cd_state(CDIS_FOCUS);
970                 SetFocus(hwnd);
971                 flush_sequences(sequences, NUM_MSG_SEQUENCES);
972 
973                 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
974                 if (button[i].cd_setfocus_type != cd_seq_optional || !test_cd.empty)
975                     ok_sequence(sequences, PARENT_CD_SEQ_INDEX, ret[k].seq, ret[k].context, FALSE);
976             }
977         }
978 
979         disable_test_cd();
980 
981         if (!broken(LOBYTE(LOWORD(GetVersion())) < 6))  /* not available pre-Vista */
982         {
983             /* Send down arrow key to make the buttons send the drop down notification */
984             flush_sequences(sequences, NUM_MSG_SEQUENCES);
985             SendMessageW(hwnd, WM_KEYDOWN, VK_DOWN, 0);
986             SendMessageW(hwnd, WM_KEYUP, VK_DOWN, 0xc0000000);
987             while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg);
988             ok_sequence(sequences, COMBINED_SEQ_INDEX, bcn_dropdown_seq, "BCN_DROPDOWN from the button", FALSE);
989         }
990 
991         DestroyWindow(hwnd);
992     }
993 
994 #undef check_cd_seq
995 
996     DeleteObject(hfont2);
997     DestroyWindow(parent);
998 
999     hwnd = create_button(BS_PUSHBUTTON, NULL);
1000 
1001     SetForegroundWindow(hwnd);
1002     flush_events();
1003 
1004     SetActiveWindow(hwnd);
1005     SetFocus(0);
1006     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1007 
1008     SendMessageA(hwnd, WM_LBUTTONDOWN, 0, 0);
1009     ok_sequence(sequences, COMBINED_SEQ_INDEX, lbuttondown_seq, "WM_LBUTTONDOWN on a button", FALSE);
1010 
1011     SendMessageA(hwnd, WM_LBUTTONUP, 0, 0);
1012     ok_sequence(sequences, COMBINED_SEQ_INDEX, lbuttonup_seq, "WM_LBUTTONUP on a button", TRUE);
1013 
1014     flush_sequences(sequences, NUM_MSG_SEQUENCES);
1015     zfont = GetStockObject(SYSTEM_FONT);
1016     SendMessageA(hwnd, WM_SETFONT, (WPARAM)zfont, TRUE);
1017     UpdateWindow(hwnd);
1018     ok_sequence(sequences, COMBINED_SEQ_INDEX, setfont_seq, "WM_SETFONT on a button", FALSE);
1019 
1020     DestroyWindow(hwnd);
1021 }
1022 
1023 static void test_button_class(void)
1024 {
1025     static const WCHAR testW[] = {'t','e','s','t',0};
1026     WNDCLASSEXW exW, ex2W;
1027     WNDCLASSEXA exA;
1028     char buffA[100];
1029     WCHAR *nameW;
1030     HWND hwnd;
1031     BOOL ret;
1032     int len;
1033 
1034     ret = GetClassInfoExA(NULL, WC_BUTTONA, &exA);
1035     ok(ret, "got %d\n", ret);
1036     ok(IS_WNDPROC_HANDLE(exA.lpfnWndProc), "got %p\n", exA.lpfnWndProc);
1037     ok(exA.cbClsExtra == 0, "Unexpected class bytes %d.\n", exA.cbClsExtra);
1038     ok(exA.cbWndExtra == sizeof(void *), "Unexpected window bytes %d.\n", exA.cbWndExtra);
1039 
1040     ret = GetClassInfoExW(NULL, WC_BUTTONW, &exW);
1041     ok(ret, "got %d\n", ret);
1042     ok(!IS_WNDPROC_HANDLE(exW.lpfnWndProc), "got %p\n", exW.lpfnWndProc);
1043     ok(exW.cbClsExtra == 0, "Unexpected class bytes %d.\n", exW.cbClsExtra);
1044     ok(exW.cbWndExtra == sizeof(void *), "Unexpected window bytes %d.\n", exW.cbWndExtra);
1045 
1046     /* check that versioned class is also accessible */
1047     nameW = get_versioned_classname(WC_BUTTONW);
1048     ok(lstrcmpW(nameW, WC_BUTTONW), "got %s\n", wine_dbgstr_w(nameW));
1049 
1050     ret = GetClassInfoExW(NULL, nameW, &ex2W);
1051     ok(ret, "got %d\n", ret);
1052     ok(ex2W.lpfnWndProc == exW.lpfnWndProc, "got %p, %p\n", exW.lpfnWndProc, ex2W.lpfnWndProc);
1053 
1054     /* Check reported class name */
1055     hwnd = create_button(BS_CHECKBOX, NULL);
1056     len = GetClassNameA(hwnd, buffA, sizeof(buffA));
1057     ok(len == strlen(buffA), "got %d\n", len);
1058     ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
1059 
1060     len = RealGetWindowClassA(hwnd, buffA, sizeof(buffA));
1061     ok(len == strlen(buffA), "got %d\n", len);
1062     ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
1063     DestroyWindow(hwnd);
1064 
1065     /* explicitly create with versioned class name */
1066     hwnd = CreateWindowExW(0, nameW, testW, BS_CHECKBOX, 0, 0, 50, 14, NULL, 0, 0, NULL);
1067     ok(hwnd != NULL, "failed to create a window %s\n", wine_dbgstr_w(nameW));
1068 
1069     len = GetClassNameA(hwnd, buffA, sizeof(buffA));
1070     ok(len == strlen(buffA), "got %d\n", len);
1071     ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
1072 
1073     len = RealGetWindowClassA(hwnd, buffA, sizeof(buffA));
1074     ok(len == strlen(buffA), "got %d\n", len);
1075     ok(!strcmp(buffA, "Button"), "got %s\n", buffA);
1076 
1077     DestroyWindow(hwnd);
1078 }
1079 
1080 static void test_note(void)
1081 {
1082     HWND hwnd;
1083     BOOL ret;
1084     WCHAR test_w[] = {'t', 'e', 's', 't', 0};
1085     WCHAR tes_w[] = {'t', 'e', 's', 0};
1086     WCHAR deadbeef_w[] = {'d', 'e', 'a', 'd', 'b', 'e', 'e', 'f', 0};
1087     WCHAR buffer_w[10];
1088     DWORD size;
1089     DWORD error;
1090     INT type;
1091 
1092     hwnd = create_button(BS_COMMANDLINK, NULL);
1093     ok(hwnd != NULL, "Expect hwnd not null\n");
1094     SetLastError(0xdeadbeef);
1095     size = ARRAY_SIZE(buffer_w);
1096     ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
1097     error = GetLastError();
1098     if (!ret && error == 0xdeadbeef)
1099     {
1100         win_skip("BCM_GETNOTE message is unavailable. Skipping note tests\n"); /* xp or 2003 */
1101         DestroyWindow(hwnd);
1102         return;
1103     }
1104     DestroyWindow(hwnd);
1105 
1106     for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++)
1107     {
1108         if (type == BS_DEFCOMMANDLINK || type == BS_COMMANDLINK)
1109         {
1110             hwnd = create_button(type, NULL);
1111             ok(hwnd != NULL, "Expect hwnd not null\n");
1112 
1113             /* Get note when note hasn't been not set yet */
1114             SetLastError(0xdeadbeef);
1115             lstrcpyW(buffer_w, deadbeef_w);
1116             size = ARRAY_SIZE(buffer_w);
1117             ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
1118             error = GetLastError();
1119             ok(!ret, "Expect BCM_GETNOTE return false\n");
1120             ok(!lstrcmpW(buffer_w, deadbeef_w), "Expect note: %s, got: %s\n",
1121                wine_dbgstr_w(deadbeef_w), wine_dbgstr_w(buffer_w));
1122             ok(size == ARRAY_SIZE(buffer_w), "Got: %d\n", size);
1123             ok(error == ERROR_INVALID_PARAMETER, "Expect last error: 0x%08x, got: 0x%08x\n",
1124                ERROR_INVALID_PARAMETER, error);
1125 
1126             /* Get note length when note is not set */
1127             ret = SendMessageA(hwnd, BCM_GETNOTELENGTH, 0, 0);
1128             ok(ret == 0, "Expect note length: %d, got: %d\n", 0, ret);
1129 
1130             /* Successful set note, get note and get note length */
1131             SetLastError(0xdeadbeef);
1132             ret = SendMessageA(hwnd, BCM_SETNOTE, 0, (LPARAM)test_w);
1133             ok(ret, "Expect BCM_SETNOTE return true\n");
1134             error = GetLastError();
1135             ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error);
1136 
1137             SetLastError(0xdeadbeef);
1138             lstrcpyW(buffer_w, deadbeef_w);
1139             size = ARRAY_SIZE(buffer_w);
1140             ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
1141             ok(ret, "Expect BCM_GETNOTE return true\n");
1142             ok(!lstrcmpW(buffer_w, test_w), "Expect note: %s, got: %s\n", wine_dbgstr_w(test_w),
1143                wine_dbgstr_w(buffer_w));
1144             ok(size == ARRAY_SIZE(buffer_w), "Got: %d\n", size);
1145             error = GetLastError();
1146             ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error);
1147 
1148             ret = SendMessageA(hwnd, BCM_GETNOTELENGTH, 0, 0);
1149             ok(ret == ARRAY_SIZE(test_w) - 1, "Got: %d\n", ret);
1150 
1151             /* Insufficient buffer, return partial string */
1152             SetLastError(0xdeadbeef);
1153             lstrcpyW(buffer_w, deadbeef_w);
1154             size = ARRAY_SIZE(test_w) - 1;
1155             ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
1156             ok(!ret, "Expect BCM_GETNOTE return false\n");
1157             ok(!lstrcmpW(buffer_w, tes_w), "Expect note: %s, got: %s\n", wine_dbgstr_w(tes_w),
1158                wine_dbgstr_w(buffer_w));
1159             ok(size == ARRAY_SIZE(test_w), "Got: %d\n", size);
1160             error = GetLastError();
1161             ok(error == ERROR_INSUFFICIENT_BUFFER, "Expect last error: 0x%08x, got: 0x%08x\n",
1162                ERROR_INSUFFICIENT_BUFFER, error);
1163 
1164             /* Set note with NULL buffer */
1165             SetLastError(0xdeadbeef);
1166             ret = SendMessageA(hwnd, BCM_SETNOTE, 0, 0);
1167             ok(ret, "Expect BCM_SETNOTE return false\n");
1168             error = GetLastError();
1169             ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error);
1170 
1171             /* Check that set note with NULL buffer make note empty */
1172             SetLastError(0xdeadbeef);
1173             lstrcpyW(buffer_w, deadbeef_w);
1174             size = ARRAY_SIZE(buffer_w);
1175             ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
1176             ok(ret, "Expect BCM_GETNOTE return true\n");
1177             ok(lstrlenW(buffer_w) == 0, "Expect note length 0\n");
1178             ok(size == ARRAY_SIZE(buffer_w), "Got: %d\n", size);
1179             error = GetLastError();
1180             ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error);
1181             ret = SendMessageA(hwnd, BCM_GETNOTELENGTH, 0, 0);
1182             ok(ret == 0, "Expect note length: %d, got: %d\n", 0, ret);
1183 
1184             /* Get note with NULL buffer */
1185             SetLastError(0xdeadbeef);
1186             size = ARRAY_SIZE(buffer_w);
1187             ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, 0);
1188             ok(!ret, "Expect BCM_SETNOTE return false\n");
1189             ok(size == ARRAY_SIZE(buffer_w), "Got: %d\n", size);
1190             error = GetLastError();
1191             ok(error == ERROR_INVALID_PARAMETER, "Expect last error: 0x%08x, got: 0x%08x\n",
1192                ERROR_INVALID_PARAMETER, error);
1193 
1194             /* Get note with NULL size */
1195             SetLastError(0xdeadbeef);
1196             lstrcpyW(buffer_w, deadbeef_w);
1197             ret = SendMessageA(hwnd, BCM_GETNOTE, 0, (LPARAM)buffer_w);
1198             ok(!ret, "Expect BCM_SETNOTE return false\n");
1199             ok(!lstrcmpW(buffer_w, deadbeef_w), "Expect note: %s, got: %s\n",
1200                wine_dbgstr_w(deadbeef_w), wine_dbgstr_w(buffer_w));
1201             error = GetLastError();
1202             ok(error == ERROR_INVALID_PARAMETER, "Expect last error: 0x%08x, got: 0x%08x\n",
1203                ERROR_INVALID_PARAMETER, error);
1204 
1205             /* Get note with zero size */
1206             SetLastError(0xdeadbeef);
1207             size = 0;
1208             lstrcpyW(buffer_w, deadbeef_w);
1209             ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
1210             ok(!ret, "Expect BCM_GETNOTE return false\n");
1211             ok(!lstrcmpW(buffer_w, deadbeef_w), "Expect note: %s, got: %s\n",
1212                wine_dbgstr_w(deadbeef_w), wine_dbgstr_w(buffer_w));
1213             ok(size == 1, "Got: %d\n", size);
1214             error = GetLastError();
1215             ok(error == ERROR_INSUFFICIENT_BUFFER, "Expect last error: 0x%08x, got: 0x%08x\n",
1216                ERROR_INSUFFICIENT_BUFFER, error);
1217 
1218             DestroyWindow(hwnd);
1219         }
1220         else
1221         {
1222             hwnd = create_button(type, NULL);
1223             ok(hwnd != NULL, "Expect hwnd not null\n");
1224             SetLastError(0xdeadbeef);
1225             size = ARRAY_SIZE(buffer_w);
1226             ret = SendMessageA(hwnd, BCM_GETNOTE, (WPARAM)&size, (LPARAM)buffer_w);
1227             ok(!ret, "Expect BCM_GETNOTE return false\n");
1228             error = GetLastError();
1229             ok(error == ERROR_NOT_SUPPORTED, "Expect last error: 0x%08x, got: 0x%08x\n",
1230                ERROR_NOT_SUPPORTED, error);
1231             DestroyWindow(hwnd);
1232         }
1233     }
1234 }
1235 
1236 static void test_bm_get_set_image(void)
1237 {
1238     HWND hwnd;
1239     HDC hdc;
1240     HBITMAP hbmp1x1;
1241     HBITMAP hbmp2x2;
1242     HBITMAP hmask2x2;
1243     ICONINFO icon_info2x2;
1244     HICON hicon2x2;
1245     HBITMAP hbmp;
1246     HICON hicon;
1247     ICONINFO icon_info;
1248     BITMAP bm;
1249     static const DWORD default_style = BS_PUSHBUTTON | WS_TABSTOP | WS_POPUP | WS_VISIBLE;
1250 
1251     hdc = GetDC(0);
1252     hbmp1x1 = CreateCompatibleBitmap(hdc, 1, 1);
1253     hbmp2x2 = CreateCompatibleBitmap(hdc, 2, 2);
1254     ZeroMemory(&bm, sizeof(bm));
1255     ok(GetObjectW(hbmp1x1, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1256     ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1,
1257        bm.bmWidth, bm.bmHeight);
1258     ZeroMemory(&bm, sizeof(bm));
1259     ok(GetObjectW(hbmp2x2, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1260     ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
1261        bm.bmWidth, bm.bmHeight);
1262 
1263     hmask2x2 = CreateCompatibleBitmap(hdc, 2, 2);
1264     ZeroMemory(&icon_info2x2, sizeof(icon_info2x2));
1265     icon_info2x2.fIcon = TRUE;
1266     icon_info2x2.hbmMask = hmask2x2;
1267     icon_info2x2.hbmColor = hbmp2x2;
1268     hicon2x2 = CreateIconIndirect(&icon_info2x2);
1269     ok(hicon2x2 !=NULL, "Expect CreateIconIndirect() success\n");
1270 
1271     ZeroMemory(&icon_info, sizeof(icon_info));
1272     ok(GetIconInfo(hicon2x2, &icon_info), "Expect GetIconInfo() success\n");
1273     ZeroMemory(&bm, sizeof(bm));
1274     ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1275     ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
1276        bm.bmWidth, bm.bmHeight);
1277     DeleteObject(icon_info.hbmColor);
1278     DeleteObject(icon_info.hbmMask);
1279 
1280     hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0,
1281                          0, 0);
1282     ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1283     /* Get image when image is not set */
1284     hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0);
1285     ok(hbmp == 0, "Expect hbmp == 0\n");
1286     /* Set image */
1287     hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp1x1);
1288     ok(hbmp == 0, "Expect hbmp == 0\n");
1289     hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0);
1290     ok(hbmp != 0, "Expect hbmp != 0\n");
1291     /* Set null resets image */
1292     hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, 0);
1293     ok(hbmp != 0, "Expect hbmp != 0\n");
1294     hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0);
1295     ok(hbmp == 0, "Expect hbmp == 0\n");
1296     DestroyWindow(hwnd);
1297 
1298     /* Set bitmap with BS_BITMAP */
1299     hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0,
1300                          0, 0);
1301     ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1302     hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp1x1);
1303     ok(hbmp == 0, "Expect hbmp == 0\n");
1304     hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0);
1305     ok(hbmp != 0, "Expect hbmp != 0\n");
1306     ZeroMemory(&bm, sizeof(bm));
1307     ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1308     ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1,
1309        bm.bmWidth, bm.bmHeight);
1310     DestroyWindow(hwnd);
1311 
1312     /* Set bitmap without BS_BITMAP */
1313     hwnd = CreateWindowA(WC_BUTTONA, "test", default_style, 0, 0, 100, 100, 0, 0, 0, 0);
1314     ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1315     hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp1x1);
1316     ok(hbmp == 0, "Expect hbmp == 0\n");
1317     hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0);
1318     if (hbmp == 0)
1319     {
1320         /* on xp or 2003*/
1321         win_skip("Show both image and text is not supported. Skip following tests.\n");
1322         DestroyWindow(hwnd);
1323         goto done;
1324     }
1325     ok(hbmp != 0, "Expect hbmp != 0\n");
1326     ZeroMemory(&bm, sizeof(bm));
1327     ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1328     ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1,
1329        bm.bmWidth, bm.bmHeight);
1330     DestroyWindow(hwnd);
1331 
1332     /* Set icon with BS_ICON */
1333     hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0,
1334                          0);
1335     ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1336     hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon2x2);
1337     ok(hicon == 0, "Expect hicon == 0\n");
1338     hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0);
1339     ok(hicon != 0, "Expect hicon != 0\n");
1340     ZeroMemory(&icon_info, sizeof(icon_info));
1341     ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n");
1342     ZeroMemory(&bm, sizeof(bm));
1343     ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1344     ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
1345        bm.bmWidth, bm.bmHeight);
1346     DeleteObject(icon_info.hbmColor);
1347     DeleteObject(icon_info.hbmMask);
1348     DestroyWindow(hwnd);
1349 
1350     /* Set icon without BS_ICON */
1351     hwnd = CreateWindowA(WC_BUTTONA, "test", default_style, 0, 0, 100, 100, 0, 0, 0, 0);
1352     ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1353     hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon2x2);
1354     ok(hicon == 0, "Expect hicon == 0\n");
1355     hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0);
1356     ok(hicon != 0, "Expect hicon != 0\n");
1357     ZeroMemory(&icon_info, sizeof(icon_info));
1358     ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n");
1359     ZeroMemory(&bm, sizeof(bm));
1360     ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1361     ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
1362        bm.bmWidth, bm.bmHeight);
1363     DeleteObject(icon_info.hbmColor);
1364     DeleteObject(icon_info.hbmMask);
1365     DestroyWindow(hwnd);
1366 
1367     /* Set icon with BS_BITMAP */
1368     hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0,
1369                          0, 0);
1370     ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1371     hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hicon2x2);
1372     ok(hicon == 0, "Expect hicon == 0\n");
1373     hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0);
1374     ok(hicon != 0, "Expect hicon != 0\n");
1375     ZeroMemory(&icon_info, sizeof(icon_info));
1376     ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n");
1377     ZeroMemory(&bm, sizeof(bm));
1378     ok(GetObjectW(icon_info.hbmColor, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1379     ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
1380        bm.bmWidth, bm.bmHeight);
1381     DeleteObject(icon_info.hbmColor);
1382     DeleteObject(icon_info.hbmMask);
1383     DestroyWindow(hwnd);
1384 
1385     /* Set bitmap with BS_ICON */
1386     hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0,
1387                          0);
1388     ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1389     hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp1x1);
1390     ok(hbmp == 0, "Expect hbmp == 0\n");
1391     hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0);
1392     ok(hbmp != 0, "Expect hbmp != 0\n");
1393     ZeroMemory(&bm, sizeof(bm));
1394     ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1395     ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1,
1396        bm.bmWidth, bm.bmHeight);
1397     DestroyWindow(hwnd);
1398 
1399     /* Set bitmap with BS_BITMAP and IMAGE_ICON*/
1400     hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0,
1401                          0, 0);
1402     ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1403     hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hbmp1x1);
1404     ok(hbmp == 0, "Expect hbmp == 0\n");
1405     hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0);
1406     ok(hbmp != 0, "Expect hbmp != 0\n");
1407     ZeroMemory(&bm, sizeof(bm));
1408     ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1409     ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1,
1410        bm.bmWidth, bm.bmHeight);
1411     DestroyWindow(hwnd);
1412 
1413     /* Set icon with BS_ICON and IMAGE_BITMAP */
1414     hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0,
1415                          0);
1416     ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1417     hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hicon2x2);
1418     ok(hicon == 0, "Expect hicon == 0\n");
1419     hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0);
1420     ok(hicon != 0, "Expect hicon != 0\n");
1421     ZeroMemory(&icon_info, sizeof(icon_info));
1422     ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n");
1423     ZeroMemory(&bm, sizeof(bm));
1424     ok(GetObjectW(icon_info.hbmColor, sizeof(BITMAP), &bm), "Expect GetObjectW() success\n");
1425     ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
1426        bm.bmWidth, bm.bmHeight);
1427     DeleteObject(icon_info.hbmColor);
1428     DeleteObject(icon_info.hbmMask);
1429     DestroyWindow(hwnd);
1430 
1431     /* Set bitmap with BS_ICON and IMAGE_ICON */
1432     hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_ICON, 0, 0, 100, 100, 0, 0, 0, 0);
1433     ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1434     hbmp = (HBITMAP)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)hbmp1x1);
1435     ok(hbmp == 0, "Expect hbmp == 0\n");
1436     hbmp = (HBITMAP)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_ICON, 0);
1437     ok(hbmp != 0, "Expect hbmp != 0\n");
1438     ZeroMemory(&bm, sizeof(bm));
1439     ok(GetObjectW(hbmp, sizeof(bm), &bm), "Expect GetObjectW() success\n");
1440     ok(bm.bmWidth == 1 && bm.bmHeight == 1, "Expect bitmap size: %d,%d, got: %d,%d\n", 1, 1,
1441        bm.bmWidth, bm.bmHeight);
1442     DestroyWindow(hwnd);
1443 
1444     /* Set icon with BS_BITMAP and IMAGE_BITMAP */
1445     hwnd = CreateWindowA(WC_BUTTONA, "test", default_style | BS_BITMAP, 0, 0, 100, 100, 0, 0, 0, 0);
1446     ok(hwnd != NULL, "Expect hwnd to be not NULL\n");
1447     hicon = (HICON)SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hicon2x2);
1448     ok(hicon == 0, "Expect hicon == 0\n");
1449     hicon = (HICON)SendMessageA(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0);
1450     ok(hicon != 0, "Expect hicon != 0\n");
1451     ZeroMemory(&icon_info, sizeof(icon_info));
1452     ok(GetIconInfo(hicon, &icon_info), "Expect GetIconInfo() success\n");
1453     ZeroMemory(&bm, sizeof(bm));
1454     ok(GetObjectW(icon_info.hbmColor, sizeof(BITMAP), &bm), "Expect GetObjectW() success\n");
1455     ok(bm.bmWidth == 2 && bm.bmHeight == 2, "Expect bitmap size: %d,%d, got: %d,%d\n", 2, 2,
1456        bm.bmWidth, bm.bmHeight);
1457     DeleteObject(icon_info.hbmColor);
1458     DeleteObject(icon_info.hbmMask);
1459     DestroyWindow(hwnd);
1460 
1461 done:
1462     DestroyIcon(hicon2x2);
1463     DeleteObject(hmask2x2);
1464     DeleteObject(hbmp2x2);
1465     DeleteObject(hbmp1x1);
1466     ReleaseDC(0, hdc);
1467 }
1468 
1469 static void register_parent_class(void)
1470 {
1471     WNDCLASSA cls;
1472 
1473     cls.style = 0;
1474     cls.lpfnWndProc = test_parent_wndproc;
1475     cls.cbClsExtra = 0;
1476     cls.cbWndExtra = 0;
1477     cls.hInstance = GetModuleHandleA(0);
1478     cls.hIcon = 0;
1479     cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
1480     cls.hbrBackground = GetStockObject(WHITE_BRUSH);
1481     cls.lpszMenuName = NULL;
1482     cls.lpszClassName = "TestParentClass";
1483     RegisterClassA(&cls);
1484 }
1485 
1486 static void test_bcm_splitinfo(HWND hwnd)
1487 {
1488     UINT button = GetWindowLongA(hwnd, GWL_STYLE) & BS_TYPEMASK;
1489     int glyph_size = GetSystemMetrics(SM_CYMENUCHECK);
1490     int border_w = GetSystemMetrics(SM_CXEDGE) * 2;
1491     BUTTON_SPLITINFO info, dummy;
1492     HIMAGELIST img;
1493     BOOL ret;
1494 
1495     memset(&info, 0xCC, sizeof(info));
1496     info.mask = 0;
1497     memcpy(&dummy, &info, sizeof(info));
1498 
1499     ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info);
1500     if (ret != TRUE)
1501     {
1502         static BOOL once;
1503         if (!once)
1504             win_skip("BCM_GETSPLITINFO message is unavailable. Skipping related tests\n");  /* Pre-Vista */
1505         once = TRUE;
1506         return;
1507     }
1508     ok(!memcmp(&info, &dummy, sizeof(info)), "[%u] split info struct was changed with mask = 0\n", button);
1509 
1510     ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, 0);
1511     ok(ret == FALSE, "[%u] expected FALSE, got %d\n", button, ret);
1512     ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, 0);
1513     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1514 
1515     info.mask = BCSIF_GLYPH | BCSIF_SIZE | BCSIF_STYLE;
1516     ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info);
1517     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1518     ok(info.mask == (BCSIF_GLYPH | BCSIF_SIZE | BCSIF_STYLE), "[%u] wrong mask, got %u\n", button, info.mask);
1519     ok(info.himlGlyph == (HIMAGELIST)0x36, "[%u] expected 0x36 default glyph, got 0x%p\n", button, info.himlGlyph);
1520     ok(info.uSplitStyle == BCSS_STRETCH, "[%u] expected 0x%08x default style, got 0x%08x\n", button, BCSS_STRETCH, info.uSplitStyle);
1521     ok(info.size.cx == glyph_size, "[%u] expected %d default size.cx, got %d\n", button, glyph_size, info.size.cx);
1522     ok(info.size.cy == 0, "[%u] expected 0 default size.cy, got %d\n", button, info.size.cy);
1523 
1524     info.mask = BCSIF_SIZE;
1525     info.size.cx = glyph_size + 7;
1526     info.size.cy = 0;
1527     ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info);
1528     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1529     info.size.cx = info.size.cy = 0xdeadbeef;
1530     ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info);
1531     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1532     ok(info.mask == BCSIF_SIZE, "[%u] wrong mask, got %u\n", button, info.mask);
1533     ok(info.size.cx == glyph_size + 7, "[%u] expected %d, got %d\n", button, glyph_size + 7, info.size.cx);
1534     ok(info.size.cy == 0, "[%u] expected 0, got %d\n", button, info.size.cy);
1535 
1536     /* Invalid size.cx resets it to default glyph size, while size.cy is stored */
1537     info.size.cx = 0;
1538     info.size.cy = -20;
1539     ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info);
1540     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1541     info.size.cx = info.size.cy = 0xdeadbeef;
1542     ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info);
1543     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1544     ok(info.mask == BCSIF_SIZE, "[%u] wrong mask, got %u\n", button, info.mask);
1545     ok(info.size.cx == glyph_size, "[%u] expected %d, got %d\n", button, glyph_size, info.size.cx);
1546     ok(info.size.cy == -20, "[%u] expected -20, got %d\n", button, info.size.cy);
1547 
1548     info.size.cx = -glyph_size - 7;
1549     info.size.cy = -10;
1550     ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info);
1551     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1552     info.size.cx = info.size.cy = 0xdeadbeef;
1553     ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info);
1554     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1555     ok(info.mask == BCSIF_SIZE, "[%u] wrong mask, got %u\n", button, info.mask);
1556     ok(info.size.cx == glyph_size, "[%u] expected %d, got %d\n", button, glyph_size, info.size.cx);
1557     ok(info.size.cy == -10, "[%u] expected -10, got %d\n", button, info.size.cy);
1558 
1559     /* Set to a valid size other than glyph_size */
1560     info.mask = BCSIF_SIZE;
1561     info.size.cx = glyph_size + 7;
1562     info.size.cy = 11;
1563     ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info);
1564     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1565     info.size.cx = info.size.cy = 0xdeadbeef;
1566     ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info);
1567     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1568     ok(info.mask == BCSIF_SIZE, "[%u] wrong mask, got %u\n", button, info.mask);
1569     ok(info.size.cx == glyph_size + 7, "[%u] expected %d, got %d\n", button, glyph_size + 7, info.size.cx);
1570     ok(info.size.cy == 11, "[%u] expected 11, got %d\n", button, info.size.cy);
1571 
1572     /* Change the glyph, size.cx should be automatically adjusted and size.cy set to 0 */
1573     dummy.mask = BCSIF_GLYPH;
1574     dummy.himlGlyph = (HIMAGELIST)0x35;
1575     ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&dummy);
1576     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1577     info.mask = BCSIF_GLYPH | BCSIF_SIZE;
1578     ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info);
1579     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1580     ok(info.mask == (BCSIF_GLYPH | BCSIF_SIZE), "[%u] wrong mask, got %u\n", button, info.mask);
1581     ok(info.himlGlyph == (HIMAGELIST)0x35, "[%u] expected 0x35, got %p\n", button, info.himlGlyph);
1582     ok(info.size.cx == glyph_size, "[%u] expected %d, got %d\n", button, glyph_size, info.size.cx);
1583     ok(info.size.cy == 0, "[%u] expected 0, got %d\n", button, info.size.cy);
1584 
1585     /* Unless the size is specified manually */
1586     dummy.mask = BCSIF_GLYPH | BCSIF_SIZE;
1587     dummy.himlGlyph = (HIMAGELIST)0x34;
1588     dummy.size.cx = glyph_size + 11;
1589     dummy.size.cy = 7;
1590     ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&dummy);
1591     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1592     ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info);
1593     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1594     ok(info.mask == (BCSIF_GLYPH | BCSIF_SIZE), "[%u] wrong mask, got %u\n", button, info.mask);
1595     ok(info.himlGlyph == (HIMAGELIST)0x34, "[%u] expected 0x34, got %p\n", button, info.himlGlyph);
1596     ok(info.size.cx == glyph_size + 11, "[%u] expected %d, got %d\n", button, glyph_size, info.size.cx);
1597     ok(info.size.cy == 7, "[%u] expected 7, got %d\n", button, info.size.cy);
1598 
1599     /* Add the BCSS_IMAGE style manually with the wrong BCSIF_GLYPH mask, should treat it as invalid image */
1600     info.mask = BCSIF_GLYPH | BCSIF_STYLE;
1601     info.himlGlyph = (HIMAGELIST)0x37;
1602     info.uSplitStyle = BCSS_IMAGE;
1603     ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info);
1604     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1605     info.mask |= BCSIF_SIZE;
1606     ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info);
1607     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1608     ok(info.mask == (BCSIF_GLYPH | BCSIF_STYLE | BCSIF_SIZE), "[%u] wrong mask, got %u\n", button, info.mask);
1609     ok(info.himlGlyph == (HIMAGELIST)0x37, "[%u] expected 0x37, got %p\n", button, info.himlGlyph);
1610     ok(info.uSplitStyle == BCSS_IMAGE, "[%u] expected 0x%08x style, got 0x%08x\n", button, BCSS_IMAGE, info.uSplitStyle);
1611     ok(info.size.cx == border_w, "[%u] expected %d, got %d\n", button, border_w, info.size.cx);
1612     ok(info.size.cy == 0, "[%u] expected 0, got %d\n", button, info.size.cy);
1613 
1614     /* Change the size to prevent ambiguity */
1615     dummy.mask = BCSIF_SIZE;
1616     dummy.size.cx = glyph_size + 5;
1617     dummy.size.cy = 4;
1618     ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&dummy);
1619     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1620     ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info);
1621     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1622     ok(info.mask == (BCSIF_GLYPH | BCSIF_STYLE | BCSIF_SIZE), "[%u] wrong mask, got %u\n", button, info.mask);
1623     ok(info.himlGlyph == (HIMAGELIST)0x37, "[%u] expected 0x37, got %p\n", button, info.himlGlyph);
1624     ok(info.uSplitStyle == BCSS_IMAGE, "[%u] expected 0x%08x style, got 0x%08x\n", button, BCSS_IMAGE, info.uSplitStyle);
1625     ok(info.size.cx == glyph_size + 5, "[%u] expected %d, got %d\n", button, glyph_size + 5, info.size.cx);
1626     ok(info.size.cy == 4, "[%u] expected 4, got %d\n", button, info.size.cy);
1627 
1628     /* Now remove the BCSS_IMAGE style manually with the wrong BCSIF_IMAGE mask */
1629     info.mask = BCSIF_IMAGE | BCSIF_STYLE;
1630     info.himlGlyph = (HIMAGELIST)0x35;
1631     info.uSplitStyle = BCSS_STRETCH;
1632     ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info);
1633     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1634     info.mask |= BCSIF_SIZE;
1635     ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info);
1636     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1637     ok(info.mask == (BCSIF_IMAGE | BCSIF_STYLE | BCSIF_SIZE), "[%u] wrong mask, got %u\n", button, info.mask);
1638     ok(info.himlGlyph == (HIMAGELIST)0x35, "[%u] expected 0x35, got %p\n", button, info.himlGlyph);
1639     ok(info.uSplitStyle == BCSS_STRETCH, "[%u] expected 0x%08x style, got 0x%08x\n", button, BCSS_STRETCH, info.uSplitStyle);
1640     ok(info.size.cx == glyph_size, "[%u] expected %d, got %d\n", button, glyph_size, info.size.cx);
1641     ok(info.size.cy == 0, "[%u] expected 0, got %d\n", button, info.size.cy);
1642 
1643     /* Add a proper valid image, the BCSS_IMAGE style should be set automatically */
1644     img = pImageList_Create(42, 33, ILC_COLOR, 1, 1);
1645     ok(img != NULL, "[%u] failed to create ImageList\n", button);
1646     info.mask = BCSIF_IMAGE;
1647     info.himlGlyph = img;
1648     ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info);
1649     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1650     info.mask |= BCSIF_STYLE | BCSIF_SIZE;
1651     ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info);
1652     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1653     ok(info.mask == (BCSIF_IMAGE | BCSIF_STYLE | BCSIF_SIZE), "[%u] wrong mask, got %u\n", button, info.mask);
1654     ok(info.himlGlyph == img, "[%u] expected %p, got %p\n", button, img, info.himlGlyph);
1655     ok(info.uSplitStyle == (BCSS_IMAGE | BCSS_STRETCH), "[%u] expected 0x%08x style, got 0x%08x\n", button, BCSS_IMAGE | BCSS_STRETCH, info.uSplitStyle);
1656     ok(info.size.cx == 42 + border_w, "[%u] expected %d, got %d\n", button, 42 + border_w, info.size.cx);
1657     ok(info.size.cy == 0, "[%u] expected 0, got %d\n", button, info.size.cy);
1658     pImageList_Destroy(img);
1659     dummy.mask = BCSIF_SIZE;
1660     dummy.size.cx = glyph_size + 5;
1661     dummy.size.cy = 4;
1662     ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&dummy);
1663     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1664 
1665     /* Change it to a glyph; when both specified, BCSIF_GLYPH takes priority */
1666     info.mask = BCSIF_GLYPH | BCSIF_IMAGE;
1667     info.himlGlyph = (HIMAGELIST)0x37;
1668     ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info);
1669     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1670     info.mask |= BCSIF_STYLE | BCSIF_SIZE;
1671     ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info);
1672     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1673     ok(info.mask == (BCSIF_GLYPH | BCSIF_IMAGE | BCSIF_STYLE | BCSIF_SIZE), "[%u] wrong mask, got %u\n", button, info.mask);
1674     ok(info.himlGlyph == (HIMAGELIST)0x37, "[%u] expected 0x37, got %p\n", button, info.himlGlyph);
1675     ok(info.uSplitStyle == BCSS_STRETCH, "[%u] expected 0x%08x style, got 0x%08x\n", button, BCSS_STRETCH, info.uSplitStyle);
1676     ok(info.size.cx == glyph_size, "[%u] expected %d, got %d\n", button, glyph_size, info.size.cx);
1677     ok(info.size.cy == 0, "[%u] expected 0, got %d\n", button, info.size.cy);
1678 
1679     /* Try a NULL image */
1680     info.mask = BCSIF_IMAGE;
1681     info.himlGlyph = NULL;
1682     ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&info);
1683     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1684     info.mask |= BCSIF_STYLE | BCSIF_SIZE;
1685     ret = SendMessageA(hwnd, BCM_GETSPLITINFO, 0, (LPARAM)&info);
1686     ok(ret == TRUE, "[%u] expected TRUE, got %d\n", button, ret);
1687     ok(info.mask == (BCSIF_IMAGE | BCSIF_STYLE | BCSIF_SIZE), "[%u] wrong mask, got %u\n", button, info.mask);
1688     ok(info.himlGlyph == NULL, "[%u] expected NULL, got %p\n", button, info.himlGlyph);
1689     ok(info.uSplitStyle == (BCSS_IMAGE | BCSS_STRETCH), "[%u] expected 0x%08x style, got 0x%08x\n", button, BCSS_IMAGE | BCSS_STRETCH, info.uSplitStyle);
1690     ok(info.size.cx == border_w, "[%u] expected %d, got %d\n", button, border_w, info.size.cx);
1691     ok(info.size.cy == 0, "[%u] expected 0, got %d\n", button, info.size.cy);
1692 }
1693 
1694 static void test_button_data(void)
1695 {
1696     static const DWORD styles[] =
1697     {
1698         BS_PUSHBUTTON,
1699         BS_DEFPUSHBUTTON,
1700         BS_CHECKBOX,
1701         BS_AUTOCHECKBOX,
1702         BS_RADIOBUTTON,
1703         BS_3STATE,
1704         BS_AUTO3STATE,
1705         BS_GROUPBOX,
1706         BS_USERBUTTON,
1707         BS_AUTORADIOBUTTON,
1708         BS_OWNERDRAW,
1709         BS_SPLITBUTTON,
1710         BS_DEFSPLITBUTTON,
1711         BS_COMMANDLINK,
1712         BS_DEFCOMMANDLINK,
1713     };
1714 
1715     struct button_desc
1716     {
1717         HWND self;
1718         HWND parent;
1719         LONG style;
1720     };
1721     unsigned int i;
1722     HWND parent;
1723 
1724     parent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
1725                              100, 100, 200, 200, 0, 0, 0, NULL);
1726     ok(parent != 0, "Failed to create parent window\n");
1727 
1728     for (i = 0; i < ARRAY_SIZE(styles); i++)
1729     {
1730         struct button_desc *desc;
1731         HWND hwnd;
1732 
1733         hwnd = create_button(styles[i], parent);
1734         ok(hwnd != NULL, "Failed to create a button.\n");
1735 
1736         desc = (void *)GetWindowLongPtrA(hwnd, 0);
1737         ok(desc != NULL, "Expected window data.\n");
1738 
1739         if (desc)
1740         {
1741             ok(desc->self == hwnd, "Unexpected 'self' field.\n");
1742             ok(desc->parent == parent, "Unexpected 'parent' field.\n");
1743             ok(desc->style == (WS_CHILD | BS_NOTIFY | styles[i]), "Unexpected 'style' field.\n");
1744         }
1745 
1746         /* Data set and retrieved by these messages is valid for all buttons */
1747         test_bcm_splitinfo(hwnd);
1748 
1749         DestroyWindow(hwnd);
1750     }
1751 
1752     DestroyWindow(parent);
1753 }
1754 
1755 static void test_get_set_imagelist(void)
1756 {
1757     HWND hwnd;
1758     HIMAGELIST himl;
1759     BUTTON_IMAGELIST biml = {0};
1760     HDC hdc;
1761     HBITMAP hbmp;
1762     INT width = 16;
1763     INT height = 16;
1764     INT index;
1765     DWORD type;
1766     BOOL ret;
1767 
1768     hdc = GetDC(0);
1769     hbmp = CreateCompatibleBitmap(hdc, width, height);
1770     ok(hbmp != NULL, "Expect hbmp not null\n");
1771 
1772     himl = pImageList_Create(width, height, ILC_COLOR, 1, 0);
1773     ok(himl != NULL, "Expect himl not null\n");
1774     index = pImageList_Add(himl, hbmp, NULL);
1775     ok(index == 0, "Expect index == 0\n");
1776     DeleteObject(hbmp);
1777     ReleaseDC(0, hdc);
1778 
1779     for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++)
1780     {
1781         hwnd = create_button(type, NULL);
1782         ok(hwnd != NULL, "Expect hwnd not null\n");
1783 
1784         /* Get imagelist when imagelist is unset yet */
1785         ret = SendMessageA(hwnd, BCM_GETIMAGELIST, 0, (LPARAM)&biml);
1786         ok(ret, "Expect BCM_GETIMAGELIST return true\n");
1787         ok(biml.himl == 0 && IsRectEmpty(&biml.margin) && biml.uAlign == 0,
1788            "Expect BUTTON_IMAGELIST is empty\n");
1789 
1790         /* Set imagelist with himl null */
1791         biml.himl = 0;
1792         biml.uAlign = BUTTON_IMAGELIST_ALIGN_CENTER;
1793         ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml);
1794         ok(ret || broken(!ret), /* xp or 2003 */
1795            "Expect BCM_SETIMAGELIST return true\n");
1796 
1797         /* Set imagelist with uAlign invalid */
1798         biml.himl = himl;
1799         biml.uAlign = -1;
1800         ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml);
1801         ok(ret, "Expect BCM_SETIMAGELIST return true\n");
1802 
1803         /* Successful get and set imagelist */
1804         biml.himl = himl;
1805         biml.uAlign = BUTTON_IMAGELIST_ALIGN_CENTER;
1806         ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml);
1807         ok(ret, "Expect BCM_SETIMAGELIST return true\n");
1808         ret = SendMessageA(hwnd, BCM_GETIMAGELIST, 0, (LPARAM)&biml);
1809         ok(ret, "Expect BCM_GETIMAGELIST return true\n");
1810         ok(biml.himl == himl, "Expect himl to be same\n");
1811         ok(biml.uAlign == BUTTON_IMAGELIST_ALIGN_CENTER, "Expect uAlign to be %x\n",
1812            BUTTON_IMAGELIST_ALIGN_CENTER);
1813 
1814         /* BCM_SETIMAGELIST null pointer handling */
1815         ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, 0);
1816         ok(!ret, "Expect BCM_SETIMAGELIST return false\n");
1817         ret = SendMessageA(hwnd, BCM_GETIMAGELIST, 0, (LPARAM)&biml);
1818         ok(ret, "Expect BCM_GETIMAGELIST return true\n");
1819         ok(biml.himl == himl, "Expect himl to be same\n");
1820 
1821         /* BCM_GETIMAGELIST null pointer handling */
1822         biml.himl = himl;
1823         biml.uAlign = BUTTON_IMAGELIST_ALIGN_CENTER;
1824         ret = SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml);
1825         ok(ret, "Expect BCM_SETIMAGELIST return true\n");
1826         ret = SendMessageA(hwnd, BCM_GETIMAGELIST, 0, 0);
1827         ok(!ret, "Expect BCM_GETIMAGELIST return false\n");
1828 
1829         DestroyWindow(hwnd);
1830     }
1831 
1832     pImageList_Destroy(himl);
1833 }
1834 
1835 static void test_get_set_textmargin(void)
1836 {
1837     HWND hwnd;
1838     RECT margin_in;
1839     RECT margin_out;
1840     BOOL ret;
1841     DWORD type;
1842 
1843     SetRect(&margin_in, 2, 1, 3, 4);
1844     for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++)
1845     {
1846         hwnd = create_button(type, NULL);
1847         ok(hwnd != NULL, "Expect hwnd not null\n");
1848 
1849         /* Get text margin when it is unset */
1850         ret = SendMessageA(hwnd, BCM_GETTEXTMARGIN, 0, (LPARAM)&margin_out);
1851         ok(ret, "Expect ret to be true\n");
1852         ok(IsRectEmpty(&margin_out), "Expect margin empty\n");
1853 
1854         /* Successful get and set text margin */
1855         ret = SendMessageA(hwnd, BCM_SETTEXTMARGIN, 0, (LPARAM)&margin_in);
1856         ok(ret, "Expect ret to be true\n");
1857         SetRectEmpty(&margin_out);
1858         ret = SendMessageA(hwnd, BCM_GETTEXTMARGIN, 0, (LPARAM)&margin_out);
1859         ok(ret, "Expect ret to be true\n");
1860         ok(EqualRect(&margin_in, &margin_out), "Expect margins to be equal\n");
1861 
1862         /* BCM_SETTEXTMARGIN null pointer handling */
1863         ret = SendMessageA(hwnd, BCM_SETTEXTMARGIN, 0, 0);
1864         ok(!ret, "Expect ret to be false\n");
1865         SetRectEmpty(&margin_out);
1866         ret = SendMessageA(hwnd, BCM_GETTEXTMARGIN, 0, (LPARAM)&margin_out);
1867         ok(ret, "Expect ret to be true\n");
1868         ok(EqualRect(&margin_in, &margin_out), "Expect margins to be equal\n");
1869 
1870         /* BCM_GETTEXTMARGIN null pointer handling */
1871         ret = SendMessageA(hwnd, BCM_SETTEXTMARGIN, 0, (LPARAM)&margin_in);
1872         ok(ret, "Expect ret to be true\n");
1873         ret = SendMessageA(hwnd, BCM_GETTEXTMARGIN, 0, 0);
1874         ok(!ret, "Expect ret to be true\n");
1875 
1876         DestroyWindow(hwnd);
1877     }
1878 }
1879 
1880 static void test_state(void)
1881 {
1882     HWND hwnd;
1883     DWORD type;
1884     LONG state;
1885 
1886     /* Initial button state */
1887     for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++)
1888     {
1889         hwnd = create_button(type, NULL);
1890         state = SendMessageA(hwnd, BM_GETSTATE, 0, 0);
1891         ok(state == BST_UNCHECKED, "Expect state 0x%08x, got 0x%08x\n", BST_UNCHECKED, state);
1892         DestroyWindow(hwnd);
1893     }
1894 }
1895 
1896 static void test_bcm_get_ideal_size(void)
1897 {
1898     static const char *button_text2 = "WWWW\nWWWW";
1899     static const char *button_text = "WWWW";
1900     static const WCHAR button_note_short[] = { 'W',0 };
1901     static const WCHAR button_note_long[]  = { 'W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W',0 };
1902     static const WCHAR button_note_wordy[] = { 'T','h','i','s',' ','i','s',' ','a',' ','l','o','n','g',' ','n','o','t','e',' ','f','o','r',' ','t','h','e',' ','b','u','t','t','o','n',',',' ',
1903                                                'w','i','t','h',' ','m','a','n','y',' ','w','o','r','d','s',',',' ','w','h','i','c','h',' ','s','h','o','u','l','d',' ','b','e',' ',
1904                                                'o','v','e','r','a','l','l',' ','l','o','n','g','e','r',' ','t','h','a','n',' ','t','h','e',' ','t','e','x','t',' ','(','g','i','v','e','n',' ',
1905                                                't','h','e',' ','s','m','a','l','l','e','r',' ','f','o','n','t',')',' ','a','n','d',' ','t','h','u','s',' ','w','r','a','p','.',0 };
1906     static const DWORD imagelist_aligns[] = {BUTTON_IMAGELIST_ALIGN_LEFT, BUTTON_IMAGELIST_ALIGN_RIGHT,
1907                                              BUTTON_IMAGELIST_ALIGN_TOP, BUTTON_IMAGELIST_ALIGN_BOTTOM,
1908                                              BUTTON_IMAGELIST_ALIGN_CENTER};
1909     static const DWORD aligns[] = {0,         BS_TOP,     BS_LEFT,        BS_RIGHT,   BS_BOTTOM,
1910                                    BS_CENTER, BS_VCENTER, BS_RIGHTBUTTON, WS_EX_RIGHT};
1911     DWORD default_style = WS_TABSTOP | WS_POPUP | WS_VISIBLE;
1912     const LONG client_width = 400, client_height = 200, extra_width = 123, large_height = 500;
1913     struct
1914     {
1915         DWORD style;
1916         LONG extra_width;
1917     } pushtype[] =
1918     {
1919         { BS_PUSHBUTTON, 0 },
1920         { BS_DEFPUSHBUTTON, 0 },
1921         { BS_SPLITBUTTON, extra_width * 2 + GetSystemMetrics(SM_CXEDGE) },
1922         { BS_DEFSPLITBUTTON, extra_width * 2 + GetSystemMetrics(SM_CXEDGE) }
1923     };
1924     LONG image_width = 48, height = 48, line_count, text_width;
1925     HFONT hfont, prev_font;
1926     DWORD style, type;
1927     BOOL ret;
1928     HWND hwnd;
1929     HDC hdc;
1930     LOGFONTA lf;
1931     TEXTMETRICA tm;
1932     SIZE size;
1933     HBITMAP hmask, hbmp;
1934     ICONINFO icon_info;
1935     HICON hicon;
1936     HIMAGELIST himl;
1937     BUTTON_IMAGELIST biml = {0};
1938     RECT rect;
1939     INT i, j, k;
1940 
1941     /* Check for NULL pointer handling */
1942     hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_PUSHBUTTON | default_style, 0, 0, client_width, client_height,
1943         NULL, NULL, 0, NULL);
1944     ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, 0);
1945     ok(!ret, "Expect BCM_GETIDEALSIZE message to return false.\n");
1946 
1947     /* Set font so that the test is consistent on Wine and Windows */
1948     ZeroMemory(&lf, sizeof(lf));
1949     lf.lfWeight = FW_NORMAL;
1950     lf.lfHeight = 20;
1951     lstrcpyA(lf.lfFaceName, "Tahoma");
1952     hfont = CreateFontIndirectA(&lf);
1953     ok(hfont != NULL, "Failed to create test font.\n");
1954 
1955     /* Get tmHeight */
1956     hdc = GetDC(hwnd);
1957     prev_font = SelectObject(hdc, hfont);
1958     GetTextMetricsA(hdc, &tm);
1959     SelectObject(hdc, prev_font);
1960     DrawTextA(hdc, button_text, -1, &rect, DT_CALCRECT);
1961     text_width = rect.right - rect.left;
1962     ReleaseDC(hwnd, hdc);
1963     DestroyWindow(hwnd);
1964 
1965     /* XP and 2003 doesn't support command links, getting ideal size with button having only text returns client size on these platforms. */
1966     hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_DEFCOMMANDLINK | default_style, 0, 0, client_width, client_height, NULL,
1967                          NULL, 0, NULL);
1968     ok(hwnd != NULL, "Expect hwnd not NULL\n");
1969     SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
1970     ZeroMemory(&size, sizeof(size));
1971     ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
1972     ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
1973     if (size.cx == client_width && size.cy == client_height)
1974     {
1975         /* on XP and 2003, buttons with image are not supported */
1976         win_skip("Skipping further tests on XP and 2003\n");
1977         return;
1978     }
1979 
1980     /* Tests for image placements */
1981     /* Prepare bitmap */
1982     hdc = GetDC(0);
1983     hmask = CreateCompatibleBitmap(hdc, image_width, height);
1984     hbmp = CreateCompatibleBitmap(hdc, image_width, height);
1985     himl = pImageList_Create(image_width, height, ILC_COLOR, 1, 1);
1986     pImageList_Add(himl, hbmp, 0);
1987 
1988 #define set_split_info(hwnd) do { \
1989     BUTTON_SPLITINFO _info; \
1990     int _ret; \
1991     _info.mask = BCSIF_SIZE; \
1992     _info.size.cx = extra_width; \
1993     _info.size.cy = large_height; \
1994     _ret = SendMessageA(hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&_info); \
1995     ok(_ret == TRUE, "Expected BCM_SETSPLITINFO message to return true\n"); \
1996 } while (0)
1997 
1998     for (k = 0; k < ARRAY_SIZE(pushtype); k++)
1999     {
2000         /* Only bitmap for push button, ideal size should be enough for image and text */
2001         hwnd = CreateWindowA(WC_BUTTONA, button_text, pushtype[k].style | BS_BITMAP | default_style, 0, 0, client_width,
2002                              client_height, NULL, NULL, 0, NULL);
2003         ok(hwnd != NULL, "Expect hwnd not NULL\n");
2004         set_split_info(hwnd);
2005         SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp);
2006         SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
2007         ZeroMemory(&size, sizeof(size));
2008         ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
2009         ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
2010         /* Ideal size contains text rect even show bitmap only */
2011         ok(size.cx >= image_width + text_width + pushtype[k].extra_width && size.cy >= max(height, tm.tmHeight),
2012                 "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx,
2013                 image_width + text_width + pushtype[k].extra_width, size.cy, max(height, tm.tmHeight));
2014         ok(size.cy < large_height, "Expect ideal cy %d < %d\n", size.cy, large_height);
2015         DestroyWindow(hwnd);
2016 
2017         /* Image alignments when button has bitmap and text*/
2018         for (i = 0; i < ARRAY_SIZE(aligns); i++)
2019             for (j = 0; j < ARRAY_SIZE(aligns); j++)
2020             {
2021                 style = pushtype[k].style | default_style | aligns[i] | aligns[j];
2022                 hwnd = CreateWindowA(WC_BUTTONA, button_text, style, 0, 0, client_width, client_height, NULL, NULL, 0, NULL);
2023                 ok(hwnd != NULL, "Expect hwnd not NULL\n");
2024                 set_split_info(hwnd);
2025                 SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp);
2026                 SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
2027                 ZeroMemory(&size, sizeof(size));
2028                 ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
2029                 ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
2030                 if (!(style & (BS_CENTER | BS_VCENTER)) || ((style & BS_CENTER) && (style & BS_CENTER) != BS_CENTER)
2031                     || !(style & BS_VCENTER) || (style & BS_VCENTER) == BS_VCENTER)
2032                     ok(size.cx >= image_width + text_width + pushtype[k].extra_width && size.cy >= max(height, tm.tmHeight),
2033                        "Style: 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", style, size.cx,
2034                        image_width + text_width + pushtype[k].extra_width, size.cy, max(height, tm.tmHeight));
2035                 else
2036                     ok(size.cx >= max(text_width, height) + pushtype[k].extra_width && size.cy >= height + tm.tmHeight,
2037                        "Style: 0x%08x expect ideal cx %d >= %d and ideal cy %d >= %d\n", style, size.cx,
2038                        max(text_width, height) + pushtype[k].extra_width, size.cy, height + tm.tmHeight);
2039                 ok(size.cy < large_height, "Expect ideal cy %d < %d\n", size.cy, large_height);
2040                 DestroyWindow(hwnd);
2041             }
2042 
2043         /* Image list alignments */
2044         biml.himl = himl;
2045         for (i = 0; i < ARRAY_SIZE(imagelist_aligns); i++)
2046         {
2047             biml.uAlign = imagelist_aligns[i];
2048             hwnd = CreateWindowA(WC_BUTTONA, button_text, pushtype[k].style | default_style, 0, 0, client_width,
2049                 client_height, NULL, NULL, 0, NULL);
2050             ok(hwnd != NULL, "Expect hwnd not NULL\n");
2051             set_split_info(hwnd);
2052             SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
2053             SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml);
2054             ZeroMemory(&size, sizeof(size));
2055             ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
2056             ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
2057             if (biml.uAlign == BUTTON_IMAGELIST_ALIGN_TOP || biml.uAlign == BUTTON_IMAGELIST_ALIGN_BOTTOM)
2058                 ok(size.cx >= max(text_width, height) + pushtype[k].extra_width && size.cy >= height + tm.tmHeight,
2059                    "Align:%d expect ideal cx %d >= %d and ideal cy %d >= %d\n", biml.uAlign, size.cx,
2060                    max(text_width, height) + pushtype[k].extra_width, size.cy, height + tm.tmHeight);
2061             else if (biml.uAlign == BUTTON_IMAGELIST_ALIGN_LEFT || biml.uAlign == BUTTON_IMAGELIST_ALIGN_RIGHT)
2062                 ok(size.cx >= image_width + text_width + pushtype[k].extra_width && size.cy >= max(height, tm.tmHeight),
2063                    "Align:%d expect ideal cx %d >= %d and ideal cy %d >= %d\n", biml.uAlign, size.cx,
2064                    image_width + text_width + pushtype[k].extra_width, size.cy, max(height, tm.tmHeight));
2065             else
2066                 ok(size.cx >= image_width + pushtype[k].extra_width && size.cy >= height,
2067                    "Align:%d expect ideal cx %d >= %d and ideal cy %d >= %d\n",
2068                    biml.uAlign, size.cx, image_width + pushtype[k].extra_width, size.cy, height);
2069             ok(size.cy < large_height, "Expect ideal cy %d < %d\n", size.cy, large_height);
2070             DestroyWindow(hwnd);
2071         }
2072 
2073         /* Icon as image */
2074         /* Create icon from bitmap */
2075         ZeroMemory(&icon_info, sizeof(icon_info));
2076         icon_info.fIcon = TRUE;
2077         icon_info.hbmMask = hmask;
2078         icon_info.hbmColor = hbmp;
2079         hicon = CreateIconIndirect(&icon_info);
2080 
2081         /* Only icon, ideal size should be enough for image and text */
2082         hwnd = CreateWindowA(WC_BUTTONA, button_text, pushtype[k].style | BS_ICON | default_style, 0, 0, client_width,
2083                              client_height, NULL, NULL, 0, NULL);
2084         ok(hwnd != NULL, "Expect hwnd not NULL\n");
2085         set_split_info(hwnd);
2086         SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hicon);
2087         SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
2088         ZeroMemory(&size, sizeof(size));
2089         ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
2090         ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
2091         /* Ideal size contains text rect even show icons only */
2092         ok(size.cx >= image_width + text_width + pushtype[k].extra_width && size.cy >= max(height, tm.tmHeight),
2093            "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx,
2094            image_width + text_width + pushtype[k].extra_width, size.cy, max(height, tm.tmHeight));
2095         ok(size.cy < large_height, "Expect ideal cy %d < %d\n", size.cy, large_height);
2096         DestroyWindow(hwnd);
2097 
2098         /* Show icon and text */
2099         hwnd = CreateWindowA(WC_BUTTONA, button_text, pushtype[k].style | default_style, 0, 0, client_width,
2100             client_height, NULL, NULL, 0, NULL);
2101         ok(hwnd != NULL, "Expect hwnd not NULL\n");
2102         set_split_info(hwnd);
2103         SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)hicon);
2104         SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
2105         ZeroMemory(&size, sizeof(size));
2106         ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
2107         ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
2108         ok(size.cx >= image_width + text_width + pushtype[k].extra_width && size.cy >= max(height, tm.tmHeight),
2109            "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx,
2110            image_width + text_width + pushtype[k].extra_width, size.cy, max(height, tm.tmHeight));
2111         ok(size.cy < large_height, "Expect ideal cy %d < %d\n", size.cy, large_height);
2112         DestroyWindow(hwnd);
2113         DestroyIcon(hicon);
2114     }
2115 
2116 #undef set_split_info
2117 
2118     /* Checkbox */
2119     /* Both bitmap and text for checkbox, ideal size is only enough for text because it doesn't support image(but not image list)*/
2120     hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_AUTOCHECKBOX | default_style, 0, 0, client_width, client_height,
2121         NULL, NULL, 0, NULL);
2122     ok(hwnd != NULL, "Expect hwnd not NULL\n");
2123     SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp);
2124     SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
2125     ZeroMemory(&size, sizeof(size));
2126     ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
2127     ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
2128     ok((size.cx <= image_width + text_width && size.cx >= text_width && size.cy <= max(height, tm.tmHeight)
2129         && size.cy >= tm.tmHeight),
2130        "Expect ideal cx %d within range (%d, %d ) and ideal cy %d within range (%d, %d )\n", size.cx,
2131        text_width, image_width + text_width, size.cy, tm.tmHeight, max(height, tm.tmHeight));
2132     DestroyWindow(hwnd);
2133 
2134     /* Both image list and text for checkbox, ideal size should have enough for image list and text */
2135     biml.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT;
2136     hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_AUTOCHECKBOX | BS_BITMAP | default_style, 0, 0, client_width,
2137                          client_height, NULL, NULL, 0, NULL);
2138     ok(hwnd != NULL, "Expect hwnd not NULL\n");
2139     SendMessageA(hwnd, BCM_SETIMAGELIST, 0, (LPARAM)&biml);
2140     SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
2141     ZeroMemory(&size, sizeof(size));
2142     ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
2143     ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
2144     ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)),
2145        "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width, size.cy,
2146        max(height, tm.tmHeight));
2147     DestroyWindow(hwnd);
2148 
2149     /* Only bitmap for checkbox, ideal size should have enough for image and text */
2150     hwnd = CreateWindowA(WC_BUTTONA, button_text, BS_AUTOCHECKBOX | BS_BITMAP | default_style, 0, 0, client_width,
2151                          client_height, NULL, NULL, 0, NULL);
2152     ok(hwnd != NULL, "Expect hwnd not NULL\n");
2153     SendMessageA(hwnd, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hbmp);
2154     SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
2155     ZeroMemory(&size, sizeof(size));
2156     ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
2157     ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
2158     ok((size.cx >= image_width + text_width && size.cy >= max(height, tm.tmHeight)),
2159        "Expect ideal cx %d >= %d and ideal cy %d >= %d\n", size.cx, image_width + text_width, size.cy,
2160        max(height, tm.tmHeight));
2161     DestroyWindow(hwnd);
2162 
2163     /* Test button with only text */
2164     /* No text */
2165     for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++)
2166     {
2167         style = type | default_style;
2168         hwnd = CreateWindowA(WC_BUTTONA, "", style, 0, 0, client_width, client_height, NULL, NULL, 0, NULL);
2169         ok(hwnd != NULL, "Expect hwnd not NULL\n");
2170         SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
2171 
2172         ZeroMemory(&size, sizeof(size));
2173         ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
2174         ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
2175 
2176         if (type == BS_COMMANDLINK || type == BS_DEFCOMMANDLINK)
2177         {
2178             ok((size.cx == 0 && size.cy > 0), "Style 0x%08x expect ideal cx %d == %d and ideal cy %d > %d\n",
2179                style, size.cx, 0, size.cy, 0);
2180         }
2181         else
2182         {
2183             ok(size.cx == client_width && size.cy == client_height,
2184                "Style 0x%08x expect size.cx == %d and size.cy == %d, got size.cx: %d size.cy: %d\n", style,
2185                client_width, client_height, size.cx, size.cy);
2186         }
2187         DestroyWindow(hwnd);
2188     }
2189 
2190     /* Single line and multiple lines text */
2191     for (line_count = 1; line_count <= 2; line_count++)
2192     {
2193         for (type = BS_PUSHBUTTON; type <= BS_DEFCOMMANDLINK; type++)
2194         {
2195             style = line_count > 1 ? type | BS_MULTILINE : type;
2196             style |= default_style;
2197 
2198             hwnd = CreateWindowA(WC_BUTTONA, (line_count == 2 ? button_text2 : button_text), style, 0, 0, client_width,
2199                                  client_height, NULL, NULL, 0, NULL);
2200             ok(hwnd != NULL, "Expect hwnd not NULL\n");
2201             SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, (LPARAM)TRUE);
2202             ZeroMemory(&size, sizeof(size));
2203             ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
2204             ok(ret, "Expect BCM_GETIDEALSIZE message to return true\n");
2205 
2206             if (type == BS_3STATE || type == BS_AUTO3STATE || type == BS_GROUPBOX || type == BS_PUSHBOX
2207                 || type == BS_OWNERDRAW)
2208             {
2209                 ok(size.cx == client_width && size.cy == client_height,
2210                    "Style 0x%08x expect ideal size (%d,%d), got (%d,%d)\n", style, client_width, client_height, size.cx,
2211                    size.cy);
2212             }
2213             else if (type == BS_COMMANDLINK || type == BS_DEFCOMMANDLINK)
2214             {
2215                 ok((size.cx == 0 && size.cy > 0),
2216                    "Style 0x%08x expect ideal cx %d == %d and ideal cy %d > %d\n", style, size.cx, 0,
2217                    size.cy, 0);
2218             }
2219             else
2220             {
2221                 height = line_count == 2 ? 2 * tm.tmHeight : tm.tmHeight;
2222                 ok(size.cx >= 0 && size.cy >= height, "Style 0x%08x expect ideal cx %d >= 0 and ideal cy %d >= %d\n",
2223                     style, size.cx, size.cy, height);
2224             }
2225             DestroyWindow(hwnd);
2226         }
2227     }
2228 
2229     /* Command Link with note */
2230     hwnd = CreateWindowA(WC_BUTTONA, "a", style, 0, 0, client_width, client_height, NULL, NULL, 0, NULL);
2231     ok(hwnd != NULL, "Expected hwnd not NULL\n");
2232     SendMessageA(hwnd, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp);
2233     ret = SendMessageA(hwnd, BCM_SETNOTE, 0, (LPARAM)button_note_short);
2234     ok(ret == TRUE, "Expected BCM_SETNOTE to return true\n");
2235     size.cx = 13;
2236     size.cy = 0;
2237     ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
2238     ok(ret == TRUE, "Expected BCM_GETIDEALSIZE message to return true\n");
2239     ok(size.cx == 13 && size.cy > 0, "Expected ideal cx %d == %d and ideal cy %d > %d\n", size.cx, 13, size.cy, 0);
2240     height = size.cy;
2241     size.cx = 32767;
2242     size.cy = 7;
2243     ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
2244     ok(ret == TRUE, "Expected BCM_GETIDEALSIZE message to return true\n");
2245     ok(size.cx < 32767, "Expected ideal cx to have been adjusted\n");
2246     ok(size.cx > image_width && size.cy == height, "Expected ideal cx %d > %d and ideal cy %d == %d\n", size.cx, image_width, size.cy, height);
2247 
2248     /* Try longer note without word breaks, shouldn't extend height because no word splitting */
2249     ret = SendMessageA(hwnd, BCM_SETNOTE, 0, (LPARAM)button_note_long);
2250     ok(ret == TRUE, "Expected BCM_SETNOTE to return true\n");
2251     k = size.cx;
2252     size.cy = 0;
2253     ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
2254     ok(ret == TRUE, "Expected BCM_GETIDEALSIZE message to return true\n");
2255     ok(size.cx == k && size.cy == height, "Expected ideal cx %d == %d and ideal cy %d == %d\n", size.cx, k, size.cy, height);
2256 
2257     /* Now let it extend the width */
2258     size.cx = 32767;
2259     size.cy = 0;
2260     ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
2261     ok(ret == TRUE, "Expected BCM_GETIDEALSIZE message to return true\n");
2262     ok(size.cx > k && size.cy == height, "Expected ideal cx %d > %d and ideal cy %d == %d\n", size.cx, k, size.cy, height);
2263 
2264     /* Use a very long note with words and the same width, should extend the height due to word wrap */
2265     ret = SendMessageA(hwnd, BCM_SETNOTE, 0, (LPARAM)button_note_wordy);
2266     ok(ret == TRUE, "Expected BCM_SETNOTE to return true\n");
2267     k = size.cx;
2268     ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
2269     ok(ret == TRUE, "Expected BCM_GETIDEALSIZE message to return true\n");
2270     ok(size.cx <= k && size.cy > height, "Expected ideal cx %d <= %d and ideal cy %d > %d\n", size.cx, k, size.cy, height);
2271 
2272     /* Now try the wordy note with a width smaller than the image itself, which prevents wrapping */
2273     size.cx = 13;
2274     ret = SendMessageA(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&size);
2275     ok(ret == TRUE, "Expected BCM_GETIDEALSIZE message to return true\n");
2276     ok(size.cx == 13 && size.cy == height, "Expected ideal cx %d == %d and ideal cy %d == %d\n", size.cx, 13, size.cy, height);
2277     DestroyWindow(hwnd);
2278 
2279 
2280     pImageList_Destroy(himl);
2281     DeleteObject(hbmp);
2282     DeleteObject(hmask);
2283     ReleaseDC(0, hdc);
2284     DeleteObject(hfont);
2285 }
2286 
2287 START_TEST(button)
2288 {
2289     ULONG_PTR ctx_cookie;
2290     HANDLE hCtx;
2291 
2292     if (!load_v6_module(&ctx_cookie, &hCtx))
2293         return;
2294 
2295     register_parent_class();
2296 
2297     init_functions();
2298     init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
2299 
2300     test_button_class();
2301     test_button_messages();
2302     test_note();
2303     test_button_data();
2304     test_bm_get_set_image();
2305     test_get_set_imagelist();
2306     test_get_set_textmargin();
2307     test_state();
2308     test_bcm_get_ideal_size();
2309 
2310     unload_v6_module(ctx_cookie, hCtx);
2311 }
2312