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