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