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