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 #undef USE_WINE_TODOS 24 25 #include "wine/test.h" 26 27 #include <assert.h> 28 #include <wingdi.h> 29 #include <winuser.h> 30 #include <commctrl.h> 31 32 #include "v6util.h" 33 #include "msg.h" 34 35 #define IS_WNDPROC_HANDLE(x) (((ULONG_PTR)(x) >> 16) == (~0u >> 16)) 36 37 static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR); 38 static BOOL (WINAPI *pRemoveWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR); 39 static LRESULT (WINAPI *pDefSubclassProc)(HWND, UINT, WPARAM, LPARAM); 40 41 /****************** button message test *************************/ 42 #define ID_BUTTON 0x000e 43 44 #define COMBINED_SEQ_INDEX 0 45 #define NUM_MSG_SEQUENCES 1 46 47 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES]; 48 49 struct wndclass_redirect_data 50 { 51 ULONG size; 52 DWORD res; 53 ULONG name_len; 54 ULONG name_offset; 55 ULONG module_len; 56 ULONG module_offset; 57 }; 58 59 /* returned pointer is valid as long as activation context is alive */ 60 static WCHAR* get_versioned_classname(const WCHAR *name) 61 { 62 BOOL (WINAPI *pFindActCtxSectionStringW)(DWORD,const GUID *,ULONG,LPCWSTR,PACTCTX_SECTION_KEYED_DATA); 63 struct wndclass_redirect_data *wnddata; 64 ACTCTX_SECTION_KEYED_DATA data; 65 BOOL ret; 66 67 pFindActCtxSectionStringW = (void*)GetProcAddress(GetModuleHandleA("kernel32"), "FindActCtxSectionStringW"); 68 69 memset(&data, 0, sizeof(data)); 70 data.cbSize = sizeof(data); 71 72 ret = pFindActCtxSectionStringW(0, NULL, 73 ACTIVATION_CONTEXT_SECTION_WINDOW_CLASS_REDIRECTION, 74 name, &data); 75 ok(ret, "got %d, error %u\n", ret, GetLastError()); 76 wnddata = (struct wndclass_redirect_data*)data.lpData; 77 return (WCHAR*)((BYTE*)wnddata + wnddata->name_offset); 78 } 79 80 static void init_functions(void) 81 { 82 HMODULE hmod = GetModuleHandleA("comctl32.dll"); 83 ok(hmod != NULL, "got %p\n", hmod); 84 85 #define MAKEFUNC_ORD(f, ord) (p##f = (void*)GetProcAddress(hmod, (LPSTR)(ord))) 86 MAKEFUNC_ORD(SetWindowSubclass, 410); 87 MAKEFUNC_ORD(RemoveWindowSubclass, 412); 88 MAKEFUNC_ORD(DefSubclassProc, 413); 89 #undef MAKEFUNC_ORD 90 } 91 92 /* try to make sure pending X events have been processed before continuing */ 93 static void flush_events(void) 94 { 95 MSG msg; 96 int diff = 200; 97 int min_timeout = 100; 98 DWORD time = GetTickCount() + diff; 99 100 while (diff > 0) 101 { 102 if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min_timeout, QS_ALLINPUT ) == WAIT_TIMEOUT) break; 103 while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg ); 104 diff = time - GetTickCount(); 105 } 106 } 107 108 static BOOL ignore_message( UINT message ) 109 { 110 /* these are always ignored */ 111 return (message >= 0xc000 || 112 message == WM_GETICON || 113 message == WM_GETOBJECT || 114 message == WM_TIMECHANGE || 115 message == WM_DISPLAYCHANGE || 116 message == WM_DEVICECHANGE || 117 message == WM_DWMNCRENDERINGCHANGED || 118 message == WM_GETTEXTLENGTH || 119 message == WM_GETTEXT); 120 } 121 122 static LRESULT CALLBACK button_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR id, DWORD_PTR ref_data) 123 { 124 static LONG defwndproc_counter = 0; 125 struct message msg = { 0 }; 126 LRESULT ret; 127 128 if (ignore_message( message )) return pDefSubclassProc(hwnd, message, wParam, lParam); 129 130 switch (message) 131 { 132 case WM_SYNCPAINT: 133 break; 134 case BM_SETSTATE: 135 if (GetCapture()) 136 ok(GetCapture() == hwnd, "GetCapture() = %p\n", GetCapture()); 137 /* fall through */ 138 default: 139 msg.message = message; 140 msg.flags = sent|wparam|lparam; 141 if (defwndproc_counter) msg.flags |= defwinproc; 142 msg.wParam = wParam; 143 msg.lParam = lParam; 144 add_message(sequences, COMBINED_SEQ_INDEX, &msg); 145 } 146 147 if (message == WM_NCDESTROY) 148 pRemoveWindowSubclass(hwnd, button_subclass_proc, 0); 149 150 defwndproc_counter++; 151 ret = pDefSubclassProc(hwnd, message, wParam, lParam); 152 defwndproc_counter--; 153 154 return ret; 155 } 156 157 static LRESULT WINAPI test_parent_wndproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 158 { 159 static LONG defwndproc_counter = 0; 160 static LONG beginpaint_counter = 0; 161 struct message msg = { 0 }; 162 LRESULT ret; 163 164 if (ignore_message( message )) return 0; 165 166 if (message == WM_PARENTNOTIFY || message == WM_CANCELMODE || 167 message == WM_SETFOCUS || message == WM_KILLFOCUS || 168 message == WM_ENABLE || message == WM_ENTERIDLE || 169 message == WM_DRAWITEM || message == WM_COMMAND || 170 message == WM_IME_SETCONTEXT) 171 { 172 msg.message = message; 173 msg.flags = sent|parent|wparam|lparam; 174 if (defwndproc_counter) msg.flags |= defwinproc; 175 if (beginpaint_counter) msg.flags |= beginpaint; 176 msg.wParam = wParam; 177 msg.lParam = lParam; 178 add_message(sequences, COMBINED_SEQ_INDEX, &msg); 179 } 180 181 if (message == WM_PAINT) 182 { 183 PAINTSTRUCT ps; 184 beginpaint_counter++; 185 BeginPaint( hwnd, &ps ); 186 beginpaint_counter--; 187 EndPaint( hwnd, &ps ); 188 return 0; 189 } 190 191 defwndproc_counter++; 192 ret = DefWindowProcA(hwnd, message, wParam, lParam); 193 defwndproc_counter--; 194 195 return ret; 196 } 197 198 static const struct message setfocus_seq[] = 199 { 200 { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, 201 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 }, 202 { BM_GETSTATE, sent|optional }, /* when touchscreen is present */ 203 { WM_SETFOCUS, sent|wparam }, 204 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) }, 205 { WM_APP, sent|wparam|lparam }, 206 { WM_PAINT, sent }, 207 { 0 } 208 }; 209 210 static const struct message killfocus_seq[] = 211 { 212 { WM_KILLFOCUS, sent|wparam, 0 }, 213 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_KILLFOCUS) }, 214 { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, 215 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 1 }, 216 { WM_APP, sent|wparam|lparam, 0, 0 }, 217 { WM_PAINT, sent }, 218 { 0 } 219 }; 220 221 static const struct message setfocus_static_seq[] = 222 { 223 { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, 224 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 }, 225 { BM_GETSTATE, sent|optional }, /* when touchscreen is present */ 226 { WM_SETFOCUS, sent|wparam, 0 }, 227 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) }, 228 { WM_COMMAND, sent|wparam|parent|optional, MAKEWPARAM(ID_BUTTON, BN_CLICKED) }, /* radio button */ 229 { WM_APP, sent|wparam|lparam, 0, 0 }, 230 { WM_PAINT, sent }, 231 { 0 } 232 }; 233 234 static const struct message setfocus_groupbox_seq[] = 235 { 236 { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, 237 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 }, 238 { BM_GETSTATE, sent|optional }, /* when touchscreen is present */ 239 { WM_SETFOCUS, sent|wparam, 0 }, 240 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) }, 241 { WM_COMMAND, sent|wparam|parent|optional, MAKEWPARAM(ID_BUTTON, BN_CLICKED) }, /* radio button */ 242 { WM_APP, sent|wparam|lparam, 0, 0 }, 243 { WM_PAINT, sent }, 244 { 0 } 245 }; 246 247 static const struct message killfocus_static_seq[] = 248 { 249 { WM_KILLFOCUS, sent|wparam, 0 }, 250 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_KILLFOCUS) }, 251 { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, 252 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 1 }, 253 { WM_APP, sent|wparam|lparam, 0, 0 }, 254 { WM_PAINT, sent }, 255 { 0 } 256 }; 257 258 static const struct message setfocus_ownerdraw_seq[] = 259 { 260 { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, 261 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 }, 262 { BM_GETSTATE, sent|optional }, /* when touchscreen is present */ 263 { WM_SETFOCUS, sent|wparam, 0 }, 264 { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON }, 265 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_SETFOCUS) }, 266 { WM_APP, sent|wparam|lparam, 0, 0 }, 267 { WM_IME_SETCONTEXT, sent|wparam|optional, 1 }, 268 { 0 } 269 }; 270 271 static const struct message killfocus_ownerdraw_seq[] = 272 { 273 { WM_KILLFOCUS, sent|wparam, 0 }, 274 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_KILLFOCUS) }, 275 { WM_IME_SETCONTEXT, sent|wparam|optional, 0 }, 276 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 1 }, 277 { WM_APP, sent|wparam|lparam, 0, 0 }, 278 { WM_PAINT, sent }, 279 { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON }, 280 { 0 } 281 }; 282 283 static const struct message lbuttondown_seq[] = 284 { 285 { WM_LBUTTONDOWN, sent|wparam|lparam, 0, 0 }, 286 { WM_IME_SETCONTEXT, sent|wparam|defwinproc|optional, 1 }, 287 { WM_IME_NOTIFY, sent|wparam|defwinproc|optional, 2 }, 288 { BM_GETSTATE, sent|defwinproc|optional }, /* when touchscreen is present */ 289 { WM_SETFOCUS, sent|wparam|defwinproc, 0 }, 290 { BM_SETSTATE, sent|wparam|defwinproc, TRUE }, 291 { 0 } 292 }; 293 294 static const struct message lbuttonup_seq[] = 295 { 296 { WM_LBUTTONUP, sent|wparam|lparam, 0, 0 }, 297 { BM_SETSTATE, sent|wparam|defwinproc, FALSE }, 298 { WM_CAPTURECHANGED, sent|wparam|defwinproc, 0 }, 299 { WM_COMMAND, sent|wparam|defwinproc, 0 }, 300 { 0 } 301 }; 302 303 static const struct message setfont_seq[] = 304 { 305 { WM_SETFONT, sent }, 306 { 0 } 307 }; 308 309 static const struct message setstyle_seq[] = 310 { 311 { BM_SETSTYLE, sent }, 312 { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE }, 313 { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE }, 314 { WM_APP, sent|wparam|lparam, 0, 0 }, 315 { WM_PAINT, sent }, 316 { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */ 317 { WM_ERASEBKGND, sent|defwinproc|optional }, 318 { WM_PAINT, sent|optional }, 319 { 0 } 320 }; 321 322 static const struct message setstyle_static_seq[] = 323 { 324 { BM_SETSTYLE, sent }, 325 { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE }, 326 { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE }, 327 { WM_APP, sent|wparam|lparam, 0, 0 }, 328 { WM_PAINT, sent }, 329 { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */ 330 { WM_ERASEBKGND, sent|defwinproc|optional }, 331 { 0 } 332 }; 333 334 static const struct message setstyle_user_seq[] = 335 { 336 { BM_SETSTYLE, sent }, 337 { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE }, 338 { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE }, 339 { WM_APP, sent|wparam|lparam, 0, 0 }, 340 { WM_PAINT, sent }, 341 { WM_NCPAINT, sent|defwinproc|optional }, /* FIXME: Wine sends it */ 342 { WM_ERASEBKGND, sent|defwinproc|optional }, 343 { 0 } 344 }; 345 346 static const struct message setstyle_ownerdraw_seq[] = 347 { 348 { BM_SETSTYLE, sent }, 349 { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE }, 350 { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE }, 351 { WM_APP, sent|wparam|lparam, 0, 0 }, 352 { WM_PAINT, sent }, 353 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */ 354 { WM_ERASEBKGND, sent|defwinproc|optional }, 355 { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON }, 356 { 0 } 357 }; 358 359 static const struct message setstate_seq[] = 360 { 361 { BM_SETSTATE, sent }, 362 { WM_APP, sent|wparam|lparam, 0, 0 }, 363 { WM_PAINT, sent }, 364 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */ 365 { WM_ERASEBKGND, sent|defwinproc|optional }, 366 { WM_PAINT, sent|optional }, 367 { 0 } 368 }; 369 370 static const struct message setstate_static_seq[] = 371 { 372 { BM_SETSTATE, sent }, 373 { WM_APP, sent|wparam|lparam, 0, 0 }, 374 { WM_PAINT, sent }, 375 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */ 376 { WM_ERASEBKGND, sent|defwinproc|optional }, 377 { 0 } 378 }; 379 380 static const struct message setstate_user_seq[] = 381 { 382 { BM_SETSTATE, sent }, 383 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_HILITE) }, 384 { WM_APP, sent|wparam|lparam, 0, 0 }, 385 { WM_PAINT, sent }, 386 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */ 387 { WM_ERASEBKGND, sent|defwinproc|optional }, 388 { 0 } 389 }; 390 391 static const struct message setstate_ownerdraw_seq[] = 392 { 393 { BM_SETSTATE, sent }, 394 { WM_APP, sent|wparam|lparam, 0, 0 }, 395 { WM_PAINT, sent }, 396 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */ 397 { WM_ERASEBKGND, sent|defwinproc|optional }, 398 { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON }, 399 { 0 } 400 }; 401 402 static const struct message clearstate_seq[] = 403 { 404 { BM_SETSTATE, sent }, 405 { WM_COMMAND, sent|wparam|parent, MAKEWPARAM(ID_BUTTON, BN_UNHILITE) }, 406 { WM_APP, sent|wparam|lparam, 0, 0 }, 407 { WM_PAINT, sent }, 408 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */ 409 { WM_ERASEBKGND, sent|defwinproc|optional }, 410 { 0 } 411 }; 412 413 static const struct message clearstate_ownerdraw_seq[] = 414 { 415 { BM_SETSTATE, sent }, 416 { WM_APP, sent|wparam|lparam, 0, 0 }, 417 { WM_PAINT, sent }, 418 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */ 419 { WM_ERASEBKGND, sent|defwinproc|optional }, 420 { WM_DRAWITEM, sent|wparam|parent, ID_BUTTON }, 421 { 0 } 422 }; 423 424 static const struct message setcheck_ignored_seq[] = 425 { 426 { BM_SETCHECK, sent }, 427 { WM_APP, sent|wparam|lparam, 0, 0 }, 428 { WM_PAINT, sent|optional }, 429 { 0 } 430 }; 431 432 static const struct message setcheck_static_seq[] = 433 { 434 { BM_SETCHECK, sent }, 435 { WM_APP, sent|wparam|lparam, 0, 0 }, 436 { WM_PAINT, sent }, 437 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */ 438 { WM_ERASEBKGND, sent|defwinproc|optional }, 439 { 0 } 440 }; 441 442 static const struct message setcheck_radio_seq[] = 443 { 444 { BM_SETCHECK, sent }, 445 { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE }, 446 { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE }, 447 { WM_APP, sent|wparam|lparam, 0, 0 }, 448 { 0 } 449 }; 450 451 static const struct message setcheck_radio_redraw_seq[] = 452 { 453 { BM_SETCHECK, sent }, 454 { WM_STYLECHANGING, sent|wparam|defwinproc, GWL_STYLE }, 455 { WM_STYLECHANGED, sent|wparam|defwinproc, GWL_STYLE }, 456 { WM_APP, sent|wparam|lparam, 0, 0 }, 457 { WM_PAINT, sent }, 458 { WM_NCPAINT, sent|optional }, /* FIXME: Wine sends it */ 459 { WM_ERASEBKGND, sent|defwinproc|optional }, 460 { 0 } 461 }; 462 463 static HWND create_button(DWORD style, HWND parent) 464 { 465 HMENU menuid = 0; 466 HWND hwnd; 467 468 if (parent) 469 { 470 style |= WS_CHILD|BS_NOTIFY; 471 menuid = (HMENU)ID_BUTTON; 472 } 473 hwnd = CreateWindowExA(0, "Button", "test", style, 0, 0, 50, 14, parent, menuid, 0, NULL); 474 ok(hwnd != NULL, "failed to create a button, 0x%08x, %p\n", style, parent); 475 pSetWindowSubclass(hwnd, button_subclass_proc, 0, 0); 476 return hwnd; 477 } 478 479 static void test_button_messages(void) 480 { 481 static const struct 482 { 483 DWORD style; 484 DWORD dlg_code; 485 const struct message *setfocus; 486 const struct message *killfocus; 487 const struct message *setstyle; 488 const struct message *setstate; 489 const struct message *clearstate; 490 const struct message *setcheck; 491 } button[] = { 492 { BS_PUSHBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON, 493 setfocus_seq, killfocus_seq, setstyle_seq, 494 setstate_seq, setstate_seq, setcheck_ignored_seq }, 495 { BS_DEFPUSHBUTTON, DLGC_BUTTON | DLGC_DEFPUSHBUTTON, 496 setfocus_seq, killfocus_seq, setstyle_seq, 497 setstate_seq, setstate_seq, setcheck_ignored_seq }, 498 { BS_CHECKBOX, DLGC_BUTTON, 499 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, 500 setstate_static_seq, setstate_static_seq, setcheck_static_seq }, 501 { BS_AUTOCHECKBOX, DLGC_BUTTON, 502 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, 503 setstate_static_seq, setstate_static_seq, setcheck_static_seq }, 504 { BS_RADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON, 505 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, 506 setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq }, 507 { BS_3STATE, DLGC_BUTTON, 508 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, 509 setstate_static_seq, setstate_static_seq, setcheck_static_seq }, 510 { BS_AUTO3STATE, DLGC_BUTTON, 511 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, 512 setstate_static_seq, setstate_static_seq, setcheck_static_seq }, 513 { BS_GROUPBOX, DLGC_STATIC, 514 setfocus_groupbox_seq, killfocus_static_seq, setstyle_static_seq, 515 setstate_static_seq, setstate_static_seq, setcheck_ignored_seq }, 516 { BS_USERBUTTON, DLGC_BUTTON | DLGC_UNDEFPUSHBUTTON, 517 setfocus_seq, killfocus_seq, setstyle_user_seq, 518 setstate_user_seq, clearstate_seq, setcheck_ignored_seq }, 519 { BS_AUTORADIOBUTTON, DLGC_BUTTON | DLGC_RADIOBUTTON, 520 setfocus_static_seq, killfocus_static_seq, setstyle_static_seq, 521 setstate_static_seq, setstate_static_seq, setcheck_radio_redraw_seq }, 522 { BS_OWNERDRAW, DLGC_BUTTON, 523 setfocus_ownerdraw_seq, killfocus_ownerdraw_seq, setstyle_ownerdraw_seq, 524 setstate_ownerdraw_seq, clearstate_ownerdraw_seq, setcheck_ignored_seq }, 525 }; 526 const struct message *seq; 527 unsigned int i; 528 HWND hwnd, parent; 529 DWORD dlg_code; 530 HFONT zfont; 531 BOOL todo; 532 533 /* selection with VK_SPACE should capture button window */ 534 hwnd = create_button(BS_CHECKBOX | WS_VISIBLE | WS_POPUP, NULL); 535 ok(hwnd != 0, "Failed to create button window\n"); 536 ReleaseCapture(); 537 SetFocus(hwnd); 538 SendMessageA(hwnd, WM_KEYDOWN, VK_SPACE, 0); 539 ok(GetCapture() == hwnd, "Should be captured on VK_SPACE WM_KEYDOWN\n"); 540 SendMessageA(hwnd, WM_KEYUP, VK_SPACE, 0); 541 DestroyWindow(hwnd); 542 543 parent = CreateWindowExA(0, "TestParentClass", "Test parent", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 544 100, 100, 200, 200, 0, 0, 0, NULL); 545 ok(parent != 0, "Failed to create parent window\n"); 546 547 for (i = 0; i < sizeof(button)/sizeof(button[0]); i++) 548 { 549 MSG msg; 550 DWORD style, state; 551 552 trace("%d: button test sequence\n", i); 553 hwnd = create_button(button[i].style, parent); 554 555 style = GetWindowLongA(hwnd, GWL_STYLE); 556 style &= ~(WS_CHILD | BS_NOTIFY); 557 /* XP turns a BS_USERBUTTON into BS_PUSHBUTTON */ 558 if (button[i].style == BS_USERBUTTON) 559 ok(style == BS_PUSHBUTTON, "expected style BS_PUSHBUTTON got %x\n", style); 560 else 561 ok(style == button[i].style, "expected style %x got %x\n", button[i].style, style); 562 563 dlg_code = SendMessageA(hwnd, WM_GETDLGCODE, 0, 0); 564 ok(dlg_code == button[i].dlg_code, "%u: wrong dlg_code %08x\n", i, dlg_code); 565 566 ShowWindow(hwnd, SW_SHOW); 567 UpdateWindow(hwnd); 568 SetFocus(0); 569 flush_events(); 570 SetFocus(0); 571 flush_sequences(sequences, NUM_MSG_SEQUENCES); 572 573 todo = button[i].style != BS_OWNERDRAW; 574 ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus()); 575 SetFocus(hwnd); 576 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ 577 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); 578 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setfocus, "SetFocus(hwnd) on a button", todo); 579 580 todo = button[i].style == BS_OWNERDRAW; 581 SetFocus(0); 582 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ 583 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); 584 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].killfocus, "SetFocus(0) on a button", todo); 585 ok(GetFocus() == 0, "expected focus 0, got %p\n", GetFocus()); 586 587 SendMessageA(hwnd, BM_SETSTYLE, button[i].style | BS_BOTTOM, TRUE); 588 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ 589 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); 590 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setstyle, "BM_SETSTYLE on a button", TRUE); 591 592 style = GetWindowLongA(hwnd, GWL_STYLE); 593 style &= ~(WS_VISIBLE | WS_CHILD | BS_NOTIFY); 594 /* XP doesn't turn a BS_USERBUTTON into BS_PUSHBUTTON here! */ 595 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style); 596 597 state = SendMessageA(hwnd, BM_GETSTATE, 0, 0); 598 ok(state == 0, "expected state 0, got %04x\n", state); 599 600 flush_sequences(sequences, NUM_MSG_SEQUENCES); 601 602 SendMessageA(hwnd, BM_SETSTATE, TRUE, 0); 603 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ 604 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); 605 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setstate, "BM_SETSTATE/TRUE on a button", TRUE); 606 607 state = SendMessageA(hwnd, BM_GETSTATE, 0, 0); 608 ok(state == BST_PUSHED, "expected state 0x0004, got %04x\n", state); 609 610 style = GetWindowLongA(hwnd, GWL_STYLE); 611 style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE); 612 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style); 613 614 flush_sequences(sequences, NUM_MSG_SEQUENCES); 615 616 SendMessageA(hwnd, BM_SETSTATE, FALSE, 0); 617 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ 618 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); 619 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].clearstate, "BM_SETSTATE/FALSE on a button", TRUE); 620 621 state = SendMessageA(hwnd, BM_GETSTATE, 0, 0); 622 ok(state == 0, "expected state 0, got %04x\n", state); 623 624 style = GetWindowLongA(hwnd, GWL_STYLE); 625 style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE); 626 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style); 627 628 state = SendMessageA(hwnd, BM_GETCHECK, 0, 0); 629 ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state); 630 631 flush_sequences(sequences, NUM_MSG_SEQUENCES); 632 633 if (button[i].style == BS_RADIOBUTTON || 634 button[i].style == BS_AUTORADIOBUTTON) 635 { 636 seq = setcheck_radio_seq; 637 todo = TRUE; 638 } 639 else 640 { 641 seq = setcheck_ignored_seq; 642 todo = FALSE; 643 } 644 SendMessageA(hwnd, BM_SETCHECK, BST_UNCHECKED, 0); 645 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ 646 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); 647 ok_sequence(sequences, COMBINED_SEQ_INDEX, seq, "BM_SETCHECK on a button", todo); 648 649 state = SendMessageA(hwnd, BM_GETCHECK, 0, 0); 650 ok(state == BST_UNCHECKED, "expected BST_UNCHECKED, got %04x\n", state); 651 652 style = GetWindowLongA(hwnd, GWL_STYLE); 653 style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE); 654 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style); 655 656 flush_sequences(sequences, NUM_MSG_SEQUENCES); 657 658 SendMessageA(hwnd, BM_SETCHECK, BST_CHECKED, 0); 659 SendMessageA(hwnd, WM_APP, 0, 0); /* place a separator mark here */ 660 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); 661 662 if (button[i].style == BS_PUSHBUTTON || 663 button[i].style == BS_DEFPUSHBUTTON || 664 button[i].style == BS_GROUPBOX || 665 button[i].style == BS_USERBUTTON || 666 button[i].style == BS_OWNERDRAW) 667 { 668 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setcheck, "BM_SETCHECK on a button", FALSE); 669 state = SendMessageA(hwnd, BM_GETCHECK, 0, 0); 670 ok(state == BST_UNCHECKED, "expected check BST_UNCHECKED, got %04x\n", state); 671 } 672 else 673 { 674 ok_sequence(sequences, COMBINED_SEQ_INDEX, button[i].setcheck, "BM_SETCHECK on a button", TRUE); 675 state = SendMessageA(hwnd, BM_GETCHECK, 0, 0); 676 ok(state == BST_CHECKED, "expected check BST_CHECKED, got %04x\n", state); 677 } 678 679 style = GetWindowLongA(hwnd, GWL_STYLE); 680 style &= ~(WS_CHILD | BS_NOTIFY | WS_VISIBLE); 681 if (button[i].style == BS_RADIOBUTTON || 682 button[i].style == BS_AUTORADIOBUTTON) 683 ok(style == (button[i].style | WS_TABSTOP), "expected style %04x | WS_TABSTOP got %04x\n", button[i].style, style); 684 else 685 ok(style == button[i].style, "expected style %04x got %04x\n", button[i].style, style); 686 687 DestroyWindow(hwnd); 688 } 689 690 DestroyWindow(parent); 691 692 hwnd = create_button(BS_PUSHBUTTON, NULL); 693 694 SetForegroundWindow(hwnd); 695 flush_events(); 696 697 SetActiveWindow(hwnd); 698 SetFocus(0); 699 flush_sequences(sequences, NUM_MSG_SEQUENCES); 700 701 SendMessageA(hwnd, WM_LBUTTONDOWN, 0, 0); 702 ok_sequence(sequences, COMBINED_SEQ_INDEX, lbuttondown_seq, "WM_LBUTTONDOWN on a button", FALSE); 703 704 SendMessageA(hwnd, WM_LBUTTONUP, 0, 0); 705 ok_sequence(sequences, COMBINED_SEQ_INDEX, lbuttonup_seq, "WM_LBUTTONUP on a button", TRUE); 706 707 flush_sequences(sequences, NUM_MSG_SEQUENCES); 708 zfont = GetStockObject(SYSTEM_FONT); 709 SendMessageA(hwnd, WM_SETFONT, (WPARAM)zfont, TRUE); 710 UpdateWindow(hwnd); 711 ok_sequence(sequences, COMBINED_SEQ_INDEX, setfont_seq, "WM_SETFONT on a button", FALSE); 712 713 DestroyWindow(hwnd); 714 } 715 716 static void test_button_class(void) 717 { 718 static const WCHAR testW[] = {'t','e','s','t',0}; 719 WNDCLASSEXW exW, ex2W; 720 WNDCLASSEXA exA; 721 char buffA[100]; 722 WCHAR *nameW; 723 HWND hwnd; 724 BOOL ret; 725 int len; 726 727 ret = GetClassInfoExA(NULL, WC_BUTTONA, &exA); 728 ok(ret, "got %d\n", ret); 729 todo_wine 730 ok(IS_WNDPROC_HANDLE(exA.lpfnWndProc), "got %p\n", exA.lpfnWndProc); 731 732 ret = GetClassInfoExW(NULL, WC_BUTTONW, &exW); 733 ok(ret, "got %d\n", ret); 734 ok(!IS_WNDPROC_HANDLE(exW.lpfnWndProc), "got %p\n", exW.lpfnWndProc); 735 736 /* check that versioned class is also accessible */ 737 nameW = get_versioned_classname(WC_BUTTONW); 738 ok(lstrcmpW(nameW, WC_BUTTONW), "got %s\n", wine_dbgstr_w(nameW)); 739 740 ret = GetClassInfoExW(NULL, nameW, &ex2W); 741 todo_wine 742 ok(ret, "got %d\n", ret); 743 if (ret) /* TODO: remove once Wine is fixed */ 744 ok(ex2W.lpfnWndProc == exW.lpfnWndProc, "got %p, %p\n", exW.lpfnWndProc, ex2W.lpfnWndProc); 745 746 /* Check reported class name */ 747 hwnd = create_button(BS_CHECKBOX, NULL); 748 len = GetClassNameA(hwnd, buffA, sizeof(buffA)); 749 ok(len == strlen(buffA), "got %d\n", len); 750 ok(!strcmp(buffA, "Button"), "got %s\n", buffA); 751 752 len = RealGetWindowClassA(hwnd, buffA, sizeof(buffA)); 753 ok(len == strlen(buffA), "got %d\n", len); 754 ok(!strcmp(buffA, "Button"), "got %s\n", buffA); 755 DestroyWindow(hwnd); 756 757 /* explicitly create with versioned class name */ 758 hwnd = CreateWindowExW(0, nameW, testW, BS_CHECKBOX, 0, 0, 50, 14, NULL, 0, 0, NULL); 759 todo_wine 760 ok(hwnd != NULL, "failed to create a window %s\n", wine_dbgstr_w(nameW)); 761 if (hwnd) 762 { 763 len = GetClassNameA(hwnd, buffA, sizeof(buffA)); 764 ok(len == strlen(buffA), "got %d\n", len); 765 ok(!strcmp(buffA, "Button"), "got %s\n", buffA); 766 767 len = RealGetWindowClassA(hwnd, buffA, sizeof(buffA)); 768 ok(len == strlen(buffA), "got %d\n", len); 769 ok(!strcmp(buffA, "Button"), "got %s\n", buffA); 770 771 DestroyWindow(hwnd); 772 } 773 } 774 775 static void register_parent_class(void) 776 { 777 WNDCLASSA cls; 778 779 cls.style = 0; 780 cls.lpfnWndProc = test_parent_wndproc; 781 cls.cbClsExtra = 0; 782 cls.cbWndExtra = 0; 783 cls.hInstance = GetModuleHandleA(0); 784 cls.hIcon = 0; 785 cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); 786 cls.hbrBackground = GetStockObject(WHITE_BRUSH); 787 cls.lpszMenuName = NULL; 788 cls.lpszClassName = "TestParentClass"; 789 RegisterClassA(&cls); 790 } 791 792 START_TEST(button) 793 { 794 ULONG_PTR ctx_cookie; 795 HANDLE hCtx; 796 797 if (!load_v6_module(&ctx_cookie, &hCtx)) 798 return; 799 800 register_parent_class(); 801 802 init_functions(); 803 init_msg_sequences(sequences, NUM_MSG_SEQUENCES); 804 805 test_button_class(); 806 test_button_messages(); 807 808 unload_v6_module(ctx_cookie, hCtx); 809 } 810