1 /* Unit test suite for ComboBox and ComboBoxEx32 controls. 2 * 3 * Copyright 2005 Jason Edmeades 4 * Copyright 2007 Mikolaj Zalewski 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 #include <limits.h> 22 #include <stdio.h> 23 #include <windows.h> 24 #include <commctrl.h> 25 26 #include "wine/test.h" 27 #include "v6util.h" 28 #include "msg.h" 29 30 #define EDITBOX_SEQ_INDEX 0 31 #define NUM_MSG_SEQUENCES 1 32 33 #define EDITBOX_ID 0 34 #define COMBO_ID 1995 35 36 #define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got) 37 38 #define expect_rect(r, _left, _top, _right, _bottom) ok(r.left == _left && r.top == _top && \ 39 r.bottom == _bottom && r.right == _right, "Invalid rect %s vs (%d,%d)-(%d,%d)\n", \ 40 wine_dbgstr_rect(&r), _left, _top, _right, _bottom); 41 42 43 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES]; 44 45 static HWND hComboExParentWnd, hMainWnd; 46 static HINSTANCE hMainHinst; 47 static const char ComboExTestClass[] = "ComboExTestClass"; 48 49 static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR); 50 51 #define MAX_CHARS 100 52 static char *textBuffer = NULL; 53 54 static BOOL received_end_edit = FALSE; 55 56 static void get_combobox_info(HWND hwnd, COMBOBOXINFO *info) 57 { 58 BOOL ret; 59 60 info->cbSize = sizeof(*info); 61 ret = GetComboBoxInfo(hwnd, info); 62 ok(ret, "Failed to get combobox info structure, error %d\n", GetLastError()); 63 } 64 65 static HWND createComboEx(DWORD style) { 66 return CreateWindowExA(0, WC_COMBOBOXEXA, NULL, style, 0, 0, 300, 300, 67 hComboExParentWnd, NULL, hMainHinst, NULL); 68 } 69 70 static LONG addItem(HWND cbex, int idx, const char *text) { 71 COMBOBOXEXITEMA cbexItem; 72 memset(&cbexItem, 0x00, sizeof(cbexItem)); 73 cbexItem.mask = CBEIF_TEXT; 74 cbexItem.iItem = idx; 75 cbexItem.pszText = (char*)text; 76 cbexItem.cchTextMax = 0; 77 return SendMessageA(cbex, CBEM_INSERTITEMA, 0, (LPARAM)&cbexItem); 78 } 79 80 static LONG setItem(HWND cbex, int idx, const char *text) { 81 COMBOBOXEXITEMA cbexItem; 82 memset(&cbexItem, 0x00, sizeof(cbexItem)); 83 cbexItem.mask = CBEIF_TEXT; 84 cbexItem.iItem = idx; 85 cbexItem.pszText = (char*)text; 86 cbexItem.cchTextMax = 0; 87 return SendMessageA(cbex, CBEM_SETITEMA, 0, (LPARAM)&cbexItem); 88 } 89 90 static LONG delItem(HWND cbex, int idx) { 91 return SendMessageA(cbex, CBEM_DELETEITEM, idx, 0); 92 } 93 94 static LONG getItem(HWND cbex, int idx, COMBOBOXEXITEMA *cbItem) { 95 memset(cbItem, 0x00, sizeof(COMBOBOXEXITEMA)); 96 cbItem->mask = CBEIF_TEXT; 97 cbItem->pszText = textBuffer; 98 cbItem->iItem = idx; 99 cbItem->cchTextMax = 100; 100 return SendMessageA(cbex, CBEM_GETITEMA, 0, (LPARAM)cbItem); 101 } 102 103 static LRESULT WINAPI editbox_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) 104 { 105 WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA); 106 static LONG defwndproc_counter = 0; 107 struct message msg = { 0 }; 108 LRESULT ret; 109 110 msg.message = message; 111 msg.flags = sent|wparam|lparam; 112 if (defwndproc_counter) msg.flags |= defwinproc; 113 msg.wParam = wParam; 114 msg.lParam = lParam; 115 msg.id = EDITBOX_ID; 116 117 if (message != WM_PAINT && 118 message != WM_ERASEBKGND && 119 message != WM_NCPAINT && 120 message != WM_NCHITTEST && 121 message != WM_GETTEXT && 122 message != WM_GETICON && 123 message != WM_DEVICECHANGE) 124 { 125 add_message(sequences, EDITBOX_SEQ_INDEX, &msg); 126 } 127 128 defwndproc_counter++; 129 ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam); 130 defwndproc_counter--; 131 return ret; 132 } 133 134 static HWND subclass_editbox(HWND hwndComboEx) 135 { 136 WNDPROC oldproc; 137 HWND hwnd; 138 139 hwnd = (HWND)SendMessageA(hwndComboEx, CBEM_GETEDITCONTROL, 0, 0); 140 oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC, 141 (LONG_PTR)editbox_subclass_proc); 142 SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc); 143 144 return hwnd; 145 } 146 147 static void test_comboex(void) 148 { 149 HWND myHwnd = 0; 150 LONG res; 151 COMBOBOXEXITEMA cbexItem; 152 static const char *first_item = "First Item", 153 *second_item = "Second Item", 154 *third_item = "Third Item", 155 *middle_item = "Between First and Second Items", 156 *replacement_item = "Between First and Second Items", 157 *out_of_range_item = "Out of Range Item"; 158 159 /* Allocate space for result */ 160 textBuffer = heap_alloc(MAX_CHARS); 161 162 /* Basic comboboxex test */ 163 myHwnd = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN); 164 165 /* Add items onto the end of the combobox */ 166 res = addItem(myHwnd, -1, first_item); 167 ok(res == 0, "Adding simple item failed (%d)\n", res); 168 res = addItem(myHwnd, -1, second_item); 169 ok(res == 1, "Adding simple item failed (%d)\n", res); 170 res = addItem(myHwnd, 2, third_item); 171 ok(res == 2, "Adding simple item failed (%d)\n", res); 172 res = addItem(myHwnd, 1, middle_item); 173 ok(res == 1, "Inserting simple item failed (%d)\n", res); 174 175 /* Add an item completely out of range */ 176 res = addItem(myHwnd, 99, out_of_range_item); 177 ok(res == -1, "Adding using out of range index worked unexpectedly (%d)\n", res); 178 res = addItem(myHwnd, 5, out_of_range_item); 179 ok(res == -1, "Adding using out of range index worked unexpectedly (%d)\n", res); 180 /* Removed: Causes traps on Windows XP 181 res = addItem(myHwnd, -2, "Out Of Range Item"); 182 ok(res == -1, "Adding out of range worked unexpectedly (%ld)\n", res); 183 */ 184 185 /* Get an item completely out of range */ 186 res = getItem(myHwnd, 99, &cbexItem); 187 ok(res == 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res, cbexItem.pszText); 188 res = getItem(myHwnd, 4, &cbexItem); 189 ok(res == 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res, cbexItem.pszText); 190 res = getItem(myHwnd, -2, &cbexItem); 191 ok(res == 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res, cbexItem.pszText); 192 193 /* Get an item in range */ 194 res = getItem(myHwnd, 0, &cbexItem); 195 ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res); 196 ok(strcmp(first_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText); 197 198 res = getItem(myHwnd, 1, &cbexItem); 199 ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res); 200 ok(strcmp(middle_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText); 201 202 res = getItem(myHwnd, 2, &cbexItem); 203 ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res); 204 ok(strcmp(second_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText); 205 206 res = getItem(myHwnd, 3, &cbexItem); 207 ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res); 208 ok(strcmp(third_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText); 209 210 /* Set an item completely out of range */ 211 res = setItem(myHwnd, 99, replacement_item); 212 ok(res == 0, "Setting item using out of range index worked unexpectedly (%d)\n", res); 213 res = setItem(myHwnd, 4, replacement_item); 214 ok(res == 0, "Setting item using out of range index worked unexpectedly (%d)\n", res); 215 res = setItem(myHwnd, -2, replacement_item); 216 ok(res == 0, "Setting item using out of range index worked unexpectedly (%d)\n", res); 217 218 /* Set an item in range */ 219 res = setItem(myHwnd, 0, replacement_item); 220 ok(res != 0, "Setting first item failed (%d)\n", res); 221 res = setItem(myHwnd, 3, replacement_item); 222 ok(res != 0, "Setting last item failed (%d)\n", res); 223 224 /* Remove items completely out of range (4 items in control at this point) */ 225 res = delItem(myHwnd, -1); 226 ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%d)\n", res); 227 res = delItem(myHwnd, 4); 228 ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%d)\n", res); 229 230 /* Remove items in range (4 items in control at this point) */ 231 res = delItem(myHwnd, 3); 232 ok(res == 3, "Deleting using out of range index failed (%d)\n", res); 233 res = delItem(myHwnd, 0); 234 ok(res == 2, "Deleting using out of range index failed (%d)\n", res); 235 res = delItem(myHwnd, 0); 236 ok(res == 1, "Deleting using out of range index failed (%d)\n", res); 237 res = delItem(myHwnd, 0); 238 ok(res == 0, "Deleting using out of range index failed (%d)\n", res); 239 240 /* Remove from an empty box */ 241 res = delItem(myHwnd, 0); 242 ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%d)\n", res); 243 244 245 /* Cleanup */ 246 heap_free(textBuffer); 247 DestroyWindow(myHwnd); 248 } 249 250 static void test_comboex_WM_LBUTTONDOWN(void) 251 { 252 HWND hComboEx, hCombo, hEdit, hList; 253 COMBOBOXINFO cbInfo; 254 UINT x, y, item_height; 255 LRESULT result; 256 UINT i; 257 int idx; 258 RECT rect; 259 WCHAR buffer[3]; 260 static const UINT choices[] = {8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72}; 261 static const WCHAR stringFormat[] = {'%','2','d','\0'}; 262 263 hComboEx = CreateWindowExA(0, WC_COMBOBOXEXA, NULL, 264 WS_VISIBLE|WS_CHILD|CBS_DROPDOWN, 0, 0, 200, 150, 265 hComboExParentWnd, NULL, hMainHinst, NULL); 266 267 for (i = 0; i < ARRAY_SIZE(choices); i++){ 268 COMBOBOXEXITEMW cbexItem; 269 wsprintfW(buffer, stringFormat, choices[i]); 270 271 memset(&cbexItem, 0x00, sizeof(cbexItem)); 272 cbexItem.mask = CBEIF_TEXT; 273 cbexItem.iItem = i; 274 cbexItem.pszText = buffer; 275 cbexItem.cchTextMax = 0; 276 ok(SendMessageW(hComboEx, CBEM_INSERTITEMW, 0, (LPARAM)&cbexItem) >= 0, 277 "Failed to add item %d\n", i); 278 } 279 280 hCombo = (HWND)SendMessageA(hComboEx, CBEM_GETCOMBOCONTROL, 0, 0); 281 hEdit = (HWND)SendMessageA(hComboEx, CBEM_GETEDITCONTROL, 0, 0); 282 283 get_combobox_info(hCombo, &cbInfo); 284 hList = cbInfo.hwndList; 285 286 ok(GetFocus() == hComboExParentWnd, 287 "Focus not on Main Window, instead on %p\n", GetFocus()); 288 289 /* Click on the button to drop down the list */ 290 x = cbInfo.rcButton.left + (cbInfo.rcButton.right-cbInfo.rcButton.left)/2; 291 y = cbInfo.rcButton.top + (cbInfo.rcButton.bottom-cbInfo.rcButton.top)/2; 292 result = SendMessageA(hCombo, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y)); 293 ok(result, "WM_LBUTTONDOWN was not processed. LastError=%d\n", 294 GetLastError()); 295 ok(GetFocus() == hCombo || 296 broken(GetFocus() != hCombo), /* win98 */ 297 "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n", 298 GetFocus()); 299 ok(SendMessageA(hComboEx, CB_GETDROPPEDSTATE, 0, 0), 300 "The dropdown list should have appeared after clicking the button.\n"); 301 idx = SendMessageA(hCombo, CB_GETTOPINDEX, 0, 0); 302 ok(idx == 0, "For TopIndex expected %d, got %d\n", 0, idx); 303 304 result = SendMessageA(hCombo, WM_LBUTTONUP, 0, MAKELPARAM(x, y)); 305 ok(result, "WM_LBUTTONUP was not processed. LastError=%d\n", 306 GetLastError()); 307 ok(GetFocus() == hCombo || 308 broken(GetFocus() != hCombo), /* win98 */ 309 "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n", 310 GetFocus()); 311 312 /* Click on the 5th item in the list */ 313 item_height = SendMessageA(hCombo, CB_GETITEMHEIGHT, 0, 0); 314 ok(GetClientRect(hList, &rect), "Failed to get list's client rect.\n"); 315 x = rect.left + (rect.right-rect.left)/2; 316 y = item_height/2 + item_height*4; 317 result = SendMessageA(hList, WM_MOUSEMOVE, 0, MAKELPARAM(x, y)); 318 ok(!result, "WM_MOUSEMOVE was not processed. LastError=%d\n", 319 GetLastError()); 320 ok(GetFocus() == hCombo || 321 broken(GetFocus() != hCombo), /* win98 */ 322 "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n", 323 GetFocus()); 324 325 result = SendMessageA(hList, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y)); 326 ok(!result, "WM_LBUTTONDOWN was not processed. LastError=%d\n", 327 GetLastError()); 328 ok(GetFocus() == hCombo || 329 broken(GetFocus() != hCombo), /* win98 */ 330 "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n", 331 GetFocus()); 332 ok(SendMessageA(hComboEx, CB_GETDROPPEDSTATE, 0, 0), 333 "The dropdown list should still be visible.\n"); 334 335 result = SendMessageA(hList, WM_LBUTTONUP, 0, MAKELPARAM(x, y)); 336 ok(!result, "WM_LBUTTONUP was not processed. LastError=%d\n", 337 GetLastError()); 338 todo_wine ok(GetFocus() == hEdit || 339 broken(GetFocus() == hCombo), /* win98 */ 340 "Focus not on ComboBoxEx's Edit Control, instead on %p\n", 341 GetFocus()); 342 343 result = SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0); 344 ok(!result || 345 broken(result != 0), /* win98 */ 346 "The dropdown list should have been rolled up.\n"); 347 idx = SendMessageA(hComboEx, CB_GETCURSEL, 0, 0); 348 ok(idx == 4 || 349 broken(idx == -1), /* win98 */ 350 "Current Selection: expected %d, got %d\n", 4, idx); 351 ok(received_end_edit, "Expected to receive a CBEN_ENDEDIT message\n"); 352 353 SetFocus( hComboExParentWnd ); 354 ok( GetFocus() == hComboExParentWnd, "got %p\n", GetFocus() ); 355 SetFocus( hComboEx ); 356 ok( GetFocus() == hEdit, "got %p\n", GetFocus() ); 357 358 DestroyWindow(hComboEx); 359 } 360 361 static void test_comboex_CB_GETLBTEXT(void) 362 { 363 HWND hCombo; 364 CHAR buff[1]; 365 COMBOBOXEXITEMA item; 366 LRESULT ret; 367 368 hCombo = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN); 369 370 /* set text to null */ 371 addItem(hCombo, 0, NULL); 372 373 buff[0] = 'a'; 374 item.mask = CBEIF_TEXT; 375 item.iItem = 0; 376 item.pszText = buff; 377 item.cchTextMax = 1; 378 ret = SendMessageA(hCombo, CBEM_GETITEMA, 0, (LPARAM)&item); 379 ok(ret != 0, "CBEM_GETITEM failed\n"); 380 ok(buff[0] == 0, "\n"); 381 382 ret = SendMessageA(hCombo, CB_GETLBTEXTLEN, 0, 0); 383 ok(ret == 0, "Expected zero length\n"); 384 385 ret = SendMessageA(hCombo, CB_GETLBTEXTLEN, 0, 0); 386 ok(ret == 0, "Expected zero length\n"); 387 388 buff[0] = 'a'; 389 ret = SendMessageA(hCombo, CB_GETLBTEXT, 0, (LPARAM)buff); 390 ok(ret == 0, "Expected zero length\n"); 391 ok(buff[0] == 0, "Expected null terminator as a string, got %s\n", buff); 392 393 DestroyWindow(hCombo); 394 } 395 396 static void test_comboex_WM_WINDOWPOSCHANGING(void) 397 { 398 HWND hCombo; 399 WINDOWPOS wp; 400 RECT rect; 401 int combo_height; 402 int ret; 403 404 hCombo = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN); 405 ok(hCombo != NULL, "createComboEx failed\n"); 406 ret = GetWindowRect(hCombo, &rect); 407 ok(ret, "GetWindowRect failed\n"); 408 combo_height = rect.bottom - rect.top; 409 ok(combo_height > 0, "wrong combo height\n"); 410 411 /* Test height > combo_height */ 412 wp.x = rect.left; 413 wp.y = rect.top; 414 wp.cx = (rect.right - rect.left); 415 wp.cy = combo_height * 2; 416 wp.flags = 0; 417 wp.hwnd = hCombo; 418 wp.hwndInsertAfter = NULL; 419 420 ret = SendMessageA(hCombo, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp); 421 ok(ret == 0, "expected 0, got %x\n", ret); 422 ok(wp.cy == combo_height, 423 "Expected height %d, got %d\n", combo_height, wp.cy); 424 425 /* Test height < combo_height */ 426 wp.x = rect.left; 427 wp.y = rect.top; 428 wp.cx = (rect.right - rect.left); 429 wp.cy = combo_height / 2; 430 wp.flags = 0; 431 wp.hwnd = hCombo; 432 wp.hwndInsertAfter = NULL; 433 434 ret = SendMessageA(hCombo, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp); 435 ok(ret == 0, "expected 0, got %x\n", ret); 436 ok(wp.cy == combo_height, 437 "Expected height %d, got %d\n", combo_height, wp.cy); 438 439 ret = DestroyWindow(hCombo); 440 ok(ret, "DestroyWindow failed\n"); 441 } 442 443 static LRESULT ComboExTestOnNotify(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 444 { 445 NMHDR *hdr = (NMHDR*)lParam; 446 switch(hdr->code){ 447 case CBEN_ENDEDITA: 448 { 449 NMCBEENDEDITA *edit_info = (NMCBEENDEDITA*)hdr; 450 if(edit_info->iWhy==CBENF_DROPDOWN){ 451 received_end_edit = TRUE; 452 } 453 break; 454 } 455 case CBEN_ENDEDITW: 456 { 457 NMCBEENDEDITW *edit_info = (NMCBEENDEDITW*)hdr; 458 if(edit_info->iWhy==CBENF_DROPDOWN){ 459 received_end_edit = TRUE; 460 } 461 break; 462 } 463 } 464 return 0; 465 } 466 467 static LRESULT CALLBACK ComboExTestWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) 468 { 469 switch(msg) { 470 471 case WM_DESTROY: 472 PostQuitMessage(0); 473 break; 474 case WM_NOTIFY: 475 return ComboExTestOnNotify(hWnd,msg,wParam,lParam); 476 default: 477 return DefWindowProcA(hWnd, msg, wParam, lParam); 478 } 479 480 return 0L; 481 } 482 483 static void init_functions(void) 484 { 485 HMODULE hComCtl32 = LoadLibraryA("comctl32.dll"); 486 487 #define X(f) p##f = (void*)GetProcAddress(hComCtl32, #f); 488 #define X2(f, ord) p##f = (void*)GetProcAddress(hComCtl32, (const char *)ord); 489 X2(SetWindowSubclass, 410); 490 #undef X 491 #undef X2 492 } 493 494 static BOOL init(void) 495 { 496 WNDCLASSA wc; 497 498 wc.style = CS_HREDRAW | CS_VREDRAW; 499 wc.cbClsExtra = 0; 500 wc.cbWndExtra = 0; 501 wc.hInstance = GetModuleHandleA(NULL); 502 wc.hIcon = NULL; 503 wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW); 504 wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW); 505 wc.lpszMenuName = NULL; 506 wc.lpszClassName = ComboExTestClass; 507 wc.lpfnWndProc = ComboExTestWndProc; 508 RegisterClassA(&wc); 509 510 hMainWnd = CreateWindowA(WC_STATICA, "Test", WS_OVERLAPPEDWINDOW, 10, 10, 300, 300, NULL, NULL, NULL, 0); 511 ShowWindow(hMainWnd, SW_SHOW); 512 513 hComboExParentWnd = CreateWindowExA(0, ComboExTestClass, "ComboEx test", WS_OVERLAPPEDWINDOW|WS_VISIBLE, 514 CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, GetModuleHandleA(NULL), 0); 515 ok(hComboExParentWnd != NULL, "failed to create parent window\n"); 516 517 hMainHinst = GetModuleHandleA(NULL); 518 519 return hComboExParentWnd != NULL; 520 } 521 522 static void cleanup(void) 523 { 524 MSG msg; 525 526 PostMessageA(hComboExParentWnd, WM_CLOSE, 0, 0); 527 while (GetMessageA(&msg,0,0,0)) { 528 TranslateMessage(&msg); 529 DispatchMessageA(&msg); 530 } 531 532 DestroyWindow(hComboExParentWnd); 533 UnregisterClassA(ComboExTestClass, GetModuleHandleA(NULL)); 534 535 DestroyWindow(hMainWnd); 536 } 537 538 static void test_comboex_subclass(void) 539 { 540 HWND hComboEx, hCombo, hEdit; 541 542 hComboEx = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN); 543 544 hCombo = (HWND)SendMessageA(hComboEx, CBEM_GETCOMBOCONTROL, 0, 0); 545 ok(hCombo != NULL, "Failed to get internal combo\n"); 546 hEdit = (HWND)SendMessageA(hComboEx, CBEM_GETEDITCONTROL, 0, 0); 547 ok(hEdit != NULL, "Failed to get internal edit\n"); 548 549 if (pSetWindowSubclass) 550 { 551 ok(GetPropA(hCombo, "CC32SubclassInfo") != NULL, "Expected CC32SubclassInfo property\n"); 552 ok(GetPropA(hEdit, "CC32SubclassInfo") != NULL, "Expected CC32SubclassInfo property\n"); 553 } 554 555 DestroyWindow(hComboEx); 556 } 557 558 static const struct message test_setitem_edit_seq[] = { 559 { WM_SETTEXT, sent|id, 0, 0, EDITBOX_ID }, 560 { EM_SETSEL, sent|id|wparam|lparam, 0, 0, EDITBOX_ID }, 561 { EM_SETSEL, sent|id|wparam|lparam, 0, -1, EDITBOX_ID }, 562 { 0 } 563 }; 564 565 static void test_comboex_get_set_item(void) 566 { 567 char textA[] = "test"; 568 HWND hComboEx; 569 COMBOBOXEXITEMA item; 570 BOOL ret; 571 572 hComboEx = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN); 573 574 subclass_editbox(hComboEx); 575 576 flush_sequences(sequences, NUM_MSG_SEQUENCES); 577 578 memset(&item, 0, sizeof(item)); 579 item.mask = CBEIF_TEXT; 580 item.pszText = textA; 581 item.iItem = -1; 582 ret = SendMessageA(hComboEx, CBEM_SETITEMA, 0, (LPARAM)&item); 583 expect(TRUE, ret); 584 585 ok_sequence(sequences, EDITBOX_SEQ_INDEX, test_setitem_edit_seq, "set item data for edit", FALSE); 586 587 /* get/set lParam */ 588 item.mask = CBEIF_LPARAM; 589 item.iItem = -1; 590 item.lParam = 0xdeadbeef; 591 ret = SendMessageA(hComboEx, CBEM_GETITEMA, 0, (LPARAM)&item); 592 expect(TRUE, ret); 593 ok(item.lParam == 0, "Expected zero, got %lx\n", item.lParam); 594 595 item.lParam = 0x1abe11ed; 596 ret = SendMessageA(hComboEx, CBEM_SETITEMA, 0, (LPARAM)&item); 597 expect(TRUE, ret); 598 599 item.lParam = 0; 600 ret = SendMessageA(hComboEx, CBEM_GETITEMA, 0, (LPARAM)&item); 601 expect(TRUE, ret); 602 ok(item.lParam == 0x1abe11ed, "Expected 0x1abe11ed, got %lx\n", item.lParam); 603 604 DestroyWindow(hComboEx); 605 } 606 607 static HWND create_combobox(DWORD style) 608 { 609 return CreateWindowA(WC_COMBOBOXA, "Combo", WS_VISIBLE|WS_CHILD|style, 5, 5, 100, 100, hMainWnd, (HMENU)COMBO_ID, NULL, 0); 610 } 611 612 static int font_height(HFONT hFont) 613 { 614 TEXTMETRICA tm; 615 HFONT hFontOld; 616 HDC hDC; 617 618 hDC = CreateCompatibleDC(NULL); 619 hFontOld = SelectObject(hDC, hFont); 620 GetTextMetricsA(hDC, &tm); 621 SelectObject(hDC, hFontOld); 622 DeleteDC(hDC); 623 624 return tm.tmHeight; 625 } 626 627 static void test_combo_setitemheight(DWORD style) 628 { 629 HWND hCombo = create_combobox(style); 630 RECT r; 631 int i; 632 633 GetClientRect(hCombo, &r); 634 expect_rect(r, 0, 0, 100, font_height(GetStockObject(SYSTEM_FONT)) + 8); 635 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); 636 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); 637 todo_wine expect_rect(r, 5, 5, 105, 105); 638 639 for (i = 1; i < 30; i++) 640 { 641 SendMessageA(hCombo, CB_SETITEMHEIGHT, -1, i); 642 GetClientRect(hCombo, &r); 643 ok((r.bottom - r.top) == (i + 6), "Unexpected client rect height.\n"); 644 } 645 646 DestroyWindow(hCombo); 647 } 648 649 static void test_combo_setfont(DWORD style) 650 { 651 HFONT hFont1, hFont2; 652 HWND hCombo; 653 RECT r; 654 int i; 655 656 hCombo = create_combobox(style); 657 hFont1 = CreateFontA(10, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett"); 658 hFont2 = CreateFontA(8, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett"); 659 660 GetClientRect(hCombo, &r); 661 expect_rect(r, 0, 0, 100, font_height(GetStockObject(SYSTEM_FONT)) + 8); 662 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); 663 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); 664 todo_wine expect_rect(r, 5, 5, 105, 105); 665 666 /* The size of the dropped control is initially equal to the size 667 of the window when it was created. The size of the calculated 668 dropped area changes only by how much the selection area 669 changes, not by how much the list area changes. */ 670 if (font_height(hFont1) == 10 && font_height(hFont2) == 8) 671 { 672 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE); 673 GetClientRect(hCombo, &r); 674 expect_rect(r, 0, 0, 100, 18); 675 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); 676 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); 677 todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont1))); 678 679 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont2, FALSE); 680 GetClientRect(hCombo, &r); 681 expect_rect(r, 0, 0, 100, 16); 682 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); 683 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); 684 todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont2))); 685 686 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE); 687 GetClientRect(hCombo, &r); 688 expect_rect(r, 0, 0, 100, 18); 689 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); 690 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); 691 todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont1))); 692 } 693 else 694 { 695 ok(0, "Expected Marlett font heights 10/8, got %d/%d\n", 696 font_height(hFont1), font_height(hFont2)); 697 } 698 699 for (i = 1; i < 30; i++) 700 { 701 HFONT hFont = CreateFontA(i, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett"); 702 int height = font_height(hFont); 703 704 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont, FALSE); 705 GetClientRect(hCombo, &r); 706 ok((r.bottom - r.top) == (height + 8), "Unexpected client rect height.\n"); 707 SendMessageA(hCombo, WM_SETFONT, 0, FALSE); 708 DeleteObject(hFont); 709 } 710 711 DestroyWindow(hCombo); 712 DeleteObject(hFont1); 713 DeleteObject(hFont2); 714 } 715 716 static LRESULT (CALLBACK *old_parent_proc)(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); 717 static LPCSTR expected_edit_text; 718 static LPCSTR expected_list_text; 719 static BOOL selchange_fired; 720 721 static LRESULT CALLBACK parent_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) 722 { 723 switch (msg) 724 { 725 case WM_COMMAND: 726 switch (wparam) 727 { 728 case MAKEWPARAM(COMBO_ID, CBN_SELCHANGE): 729 { 730 HWND hCombo = (HWND)lparam; 731 char list[20], edit[20]; 732 int idx; 733 734 memset(list, 0, sizeof(list)); 735 memset(edit, 0, sizeof(edit)); 736 737 idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0); 738 SendMessageA(hCombo, CB_GETLBTEXT, idx, (LPARAM)list); 739 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit); 740 741 ok(!strcmp(edit, expected_edit_text), "edit: got %s, expected %s\n", 742 edit, expected_edit_text); 743 ok(!strcmp(list, expected_list_text), "list: got %s, expected %s\n", 744 list, expected_list_text); 745 746 selchange_fired = TRUE; 747 } 748 break; 749 } 750 break; 751 } 752 753 return CallWindowProcA(old_parent_proc, hwnd, msg, wparam, lparam); 754 } 755 756 static void test_selection(DWORD style, const char * const text[], const int *edit, const int *list) 757 { 758 HWND hCombo; 759 INT idx; 760 761 hCombo = create_combobox(style); 762 763 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)text[0]); 764 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)text[1]); 765 SendMessageA(hCombo, CB_SETCURSEL, -1, 0); 766 767 old_parent_proc = (void *)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)parent_wnd_proc); 768 769 idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0); 770 ok(idx == -1, "expected selection -1, got %d\n", idx); 771 772 /* keyboard navigation */ 773 774 expected_list_text = text[list[0]]; 775 expected_edit_text = text[edit[0]]; 776 selchange_fired = FALSE; 777 SendMessageA(hCombo, WM_KEYDOWN, VK_DOWN, 0); 778 ok(selchange_fired, "CBN_SELCHANGE not sent!\n"); 779 780 expected_list_text = text[list[1]]; 781 expected_edit_text = text[edit[1]]; 782 selchange_fired = FALSE; 783 SendMessageA(hCombo, WM_KEYDOWN, VK_DOWN, 0); 784 ok(selchange_fired, "CBN_SELCHANGE not sent!\n"); 785 786 expected_list_text = text[list[2]]; 787 expected_edit_text = text[edit[2]]; 788 selchange_fired = FALSE; 789 SendMessageA(hCombo, WM_KEYDOWN, VK_UP, 0); 790 ok(selchange_fired, "CBN_SELCHANGE not sent!\n"); 791 792 /* programmatic navigation */ 793 794 expected_list_text = text[list[3]]; 795 expected_edit_text = text[edit[3]]; 796 selchange_fired = FALSE; 797 SendMessageA(hCombo, CB_SETCURSEL, list[3], 0); 798 ok(!selchange_fired, "CBN_SELCHANGE sent!\n"); 799 800 expected_list_text = text[list[4]]; 801 expected_edit_text = text[edit[4]]; 802 selchange_fired = FALSE; 803 SendMessageA(hCombo, CB_SETCURSEL, list[4], 0); 804 ok(!selchange_fired, "CBN_SELCHANGE sent!\n"); 805 806 SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc); 807 DestroyWindow(hCombo); 808 } 809 810 static void test_combo_CBN_SELCHANGE(void) 811 { 812 static const char * const text[] = { "alpha", "beta", "" }; 813 static const int sel_1[] = { 2, 0, 1, 0, 1 }; 814 static const int sel_2[] = { 0, 1, 0, 0, 1 }; 815 816 test_selection(CBS_SIMPLE, text, sel_1, sel_2); 817 test_selection(CBS_DROPDOWN, text, sel_1, sel_2); 818 test_selection(CBS_DROPDOWNLIST, text, sel_2, sel_2); 819 } 820 821 static void test_combo_changesize(DWORD style) 822 { 823 INT ddheight, clheight, ddwidth, clwidth; 824 HWND hCombo; 825 RECT rc; 826 827 hCombo = create_combobox(style); 828 829 /* get initial measurements */ 830 GetClientRect( hCombo, &rc); 831 clheight = rc.bottom - rc.top; 832 clwidth = rc.right - rc.left; 833 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc); 834 ddheight = rc.bottom - rc.top; 835 ddwidth = rc.right - rc.left; 836 /* use MoveWindow to move & resize the combo */ 837 /* first make it slightly smaller */ 838 MoveWindow( hCombo, 10, 10, clwidth - 2, clheight - 2, TRUE); 839 GetClientRect( hCombo, &rc); 840 ok( rc.right - rc.left == clwidth - 2, "clientrect width is %d vs %d\n", 841 rc.right - rc.left, clwidth - 2); 842 ok( rc.bottom - rc.top == clheight, "clientrect height is %d vs %d\n", 843 rc.bottom - rc.top, clheight); 844 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc); 845 ok( rc.right - rc.left == clwidth - 2, "drop-down rect width is %d vs %d\n", 846 rc.right - rc.left, clwidth - 2); 847 ok( rc.bottom - rc.top == ddheight, "drop-down rect height is %d vs %d\n", 848 rc.bottom - rc.top, ddheight); 849 ok( rc.right - rc.left == ddwidth -2, "drop-down rect width is %d vs %d\n", 850 rc.right - rc.left, ddwidth - 2); 851 /* new cx, cy is slightly bigger than the initial values */ 852 MoveWindow( hCombo, 10, 10, clwidth + 2, clheight + 2, TRUE); 853 GetClientRect( hCombo, &rc); 854 ok( rc.right - rc.left == clwidth + 2, "clientrect width is %d vs %d\n", 855 rc.right - rc.left, clwidth + 2); 856 ok( rc.bottom - rc.top == clheight, "clientrect height is %d vs %d\n", 857 rc.bottom - rc.top, clheight); 858 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc); 859 ok( rc.right - rc.left == clwidth + 2, "drop-down rect width is %d vs %d\n", 860 rc.right - rc.left, clwidth + 2); 861 todo_wine { 862 ok( rc.bottom - rc.top == clheight + 2, "drop-down rect height is %d vs %d\n", 863 rc.bottom - rc.top, clheight + 2); 864 } 865 866 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, -1, 0); 867 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); 868 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); 869 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); 870 871 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 0, 0); 872 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); 873 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); 874 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); 875 876 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, clwidth - 1, 0); 877 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); 878 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); 879 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); 880 881 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, clwidth << 1, 0); 882 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1); 883 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); 884 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1); 885 886 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 0, 0); 887 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1); 888 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); 889 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1); 890 891 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 1, 0); 892 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); 893 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); 894 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); 895 896 DestroyWindow(hCombo); 897 } 898 899 static void test_combo_editselection(void) 900 { 901 COMBOBOXINFO cbInfo; 902 INT start, end; 903 char edit[20]; 904 HWND hCombo; 905 HWND hEdit; 906 DWORD len; 907 908 /* Build a combo */ 909 hCombo = create_combobox(CBS_SIMPLE); 910 911 get_combobox_info(hCombo, &cbInfo); 912 hEdit = cbInfo.hwndItem; 913 914 /* Initially combo selection is empty*/ 915 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); 916 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len)); 917 ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len)); 918 919 /* Set some text, and press a key to replace it */ 920 edit[0] = 0x00; 921 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)"Jason1"); 922 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit); 923 ok(strcmp(edit, "Jason1")==0, "Unexpected text retrieved %s\n", edit); 924 925 /* Now what is the selection - still empty */ 926 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end); 927 ok(start==0, "Unexpected start position for selection %d\n", start); 928 ok(end==0, "Unexpected end position for selection %d\n", end); 929 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); 930 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len)); 931 ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len)); 932 933 /* Give it focus, and it gets selected */ 934 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit); 935 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end); 936 ok(start==0, "Unexpected start position for selection %d\n", start); 937 ok(end==6, "Unexpected end position for selection %d\n", end); 938 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); 939 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len)); 940 ok(HIWORD(len)==6, "Unexpected end position for selection %d\n", HIWORD(len)); 941 942 /* Now emulate a key press */ 943 edit[0] = 0x00; 944 SendMessageA(hCombo, WM_CHAR, 'A', 0x1c0001); 945 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit); 946 ok(strcmp(edit, "A")==0, "Unexpected text retrieved %s\n", edit); 947 948 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); 949 ok(LOWORD(len)==1, "Unexpected start position for selection %d\n", LOWORD(len)); 950 ok(HIWORD(len)==1, "Unexpected end position for selection %d\n", HIWORD(len)); 951 952 /* Now what happens when it gets more focus a second time - it doesn't reselect */ 953 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit); 954 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); 955 ok(LOWORD(len)==1, "Unexpected start position for selection %d\n", LOWORD(len)); 956 ok(HIWORD(len)==1, "Unexpected end position for selection %d\n", HIWORD(len)); 957 DestroyWindow(hCombo); 958 959 /* Start again - Build a combo */ 960 hCombo = create_combobox(CBS_SIMPLE); 961 get_combobox_info(hCombo, &cbInfo); 962 hEdit = cbInfo.hwndItem; 963 964 /* Set some text and give focus so it gets selected */ 965 edit[0] = 0x00; 966 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)"Jason2"); 967 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit); 968 ok(strcmp(edit, "Jason2")==0, "Unexpected text retrieved %s\n", edit); 969 970 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit); 971 972 /* Now what is the selection */ 973 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end); 974 ok(start==0, "Unexpected start position for selection %d\n", start); 975 ok(end==6, "Unexpected end position for selection %d\n", end); 976 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); 977 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len)); 978 ok(HIWORD(len)==6, "Unexpected end position for selection %d\n", HIWORD(len)); 979 980 /* Now change the selection to the apparently invalid start -1, end -1 and 981 show it means no selection (ie start -1) but cursor at end */ 982 SendMessageA(hCombo, CB_SETEDITSEL, 0, -1); 983 edit[0] = 0x00; 984 SendMessageA(hCombo, WM_CHAR, 'A', 0x1c0001); 985 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit); 986 ok(strcmp(edit, "Jason2A")==0, "Unexpected text retrieved %s\n", edit); 987 DestroyWindow(hCombo); 988 } 989 990 static WNDPROC edit_window_proc; 991 static long setsel_start = 1, setsel_end = 1; 992 static HWND hCBN_SetFocus, hCBN_KillFocus; 993 994 static LRESULT CALLBACK combobox_subclass_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 995 { 996 if (msg == EM_SETSEL) 997 { 998 setsel_start = wParam; 999 setsel_end = lParam; 1000 } 1001 return CallWindowProcA(edit_window_proc, hwnd, msg, wParam, lParam); 1002 } 1003 1004 static LRESULT CALLBACK test_window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 1005 { 1006 switch (msg) 1007 { 1008 case WM_COMMAND: 1009 switch (HIWORD(wParam)) 1010 { 1011 case CBN_SETFOCUS: 1012 hCBN_SetFocus = (HWND)lParam; 1013 break; 1014 case CBN_KILLFOCUS: 1015 hCBN_KillFocus = (HWND)lParam; 1016 break; 1017 } 1018 break; 1019 case WM_NEXTDLGCTL: 1020 SetFocus((HWND)wParam); 1021 break; 1022 } 1023 return CallWindowProcA(old_parent_proc, hwnd, msg, wParam, lParam); 1024 } 1025 1026 static void test_combo_editselection_focus(DWORD style) 1027 { 1028 static const char wine_test[] = "Wine Test"; 1029 HWND hCombo, hEdit, hButton; 1030 char buffer[16] = {0}; 1031 COMBOBOXINFO cbInfo; 1032 DWORD len; 1033 1034 hCombo = create_combobox(style); 1035 get_combobox_info(hCombo, &cbInfo); 1036 hEdit = cbInfo.hwndItem; 1037 1038 hButton = CreateWindowA(WC_BUTTONA, "OK", WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON, 1039 5, 50, 100, 20, hMainWnd, NULL, 1040 (HINSTANCE)GetWindowLongPtrA(hMainWnd, GWLP_HINSTANCE), NULL); 1041 1042 old_parent_proc = (WNDPROC)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)test_window_proc); 1043 edit_window_proc = (WNDPROC)SetWindowLongPtrA(hEdit, GWLP_WNDPROC, (ULONG_PTR)combobox_subclass_proc); 1044 1045 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit); 1046 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start); 1047 todo_wine ok(setsel_end == INT_MAX, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end); 1048 ok(hCBN_SetFocus == hCombo, "Wrong handle set by CBN_SETFOCUS; got %p\n", hCBN_SetFocus); 1049 ok(GetFocus() == hEdit, "hEdit should have keyboard focus\n"); 1050 1051 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hButton, TRUE); 1052 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start); 1053 todo_wine ok(setsel_end == 0, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end); 1054 ok(hCBN_KillFocus == hCombo, "Wrong handle set by CBN_KILLFOCUS; got %p\n", hCBN_KillFocus); 1055 ok(GetFocus() == hButton, "hButton should have keyboard focus\n"); 1056 1057 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)wine_test); 1058 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hCombo, TRUE); 1059 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start); 1060 todo_wine ok(setsel_end == INT_MAX, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end); 1061 ok(hCBN_SetFocus == hCombo, "Wrong handle set by CBN_SETFOCUS; got %p\n", hCBN_SetFocus); 1062 ok(GetFocus() == hEdit, "hEdit should have keyboard focus\n"); 1063 SendMessageA(hCombo, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer); 1064 ok(!strcmp(buffer, wine_test), "Unexpected text in edit control; got '%s'\n", buffer); 1065 1066 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hButton, TRUE); 1067 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start); 1068 todo_wine ok(setsel_end == 0, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end); 1069 ok(hCBN_KillFocus == hCombo, "Wrong handle set by CBN_KILLFOCUS; got %p\n", hCBN_KillFocus); 1070 ok(GetFocus() == hButton, "hButton should have keyboard focus\n"); 1071 len = SendMessageA(hCombo, CB_GETEDITSEL, 0, 0); 1072 ok(len == 0, "Unexpected text selection; start: %u, end: %u\n", LOWORD(len), HIWORD(len)); 1073 1074 SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc); 1075 DestroyWindow(hButton); 1076 DestroyWindow(hCombo); 1077 } 1078 1079 static void test_combo_listbox_styles(DWORD cb_style) 1080 { 1081 DWORD style, exstyle, expect_style, expect_exstyle; 1082 COMBOBOXINFO info; 1083 HWND combo; 1084 1085 expect_style = WS_CHILD|WS_CLIPSIBLINGS|LBS_COMBOBOX|LBS_HASSTRINGS|LBS_NOTIFY; 1086 if (cb_style == CBS_SIMPLE) 1087 { 1088 expect_style |= WS_VISIBLE; 1089 expect_exstyle = WS_EX_CLIENTEDGE; 1090 } 1091 else 1092 { 1093 expect_style |= WS_BORDER; 1094 expect_exstyle = WS_EX_TOOLWINDOW; 1095 } 1096 1097 combo = create_combobox(cb_style); 1098 get_combobox_info(combo, &info); 1099 1100 style = GetWindowLongW( info.hwndList, GWL_STYLE ); 1101 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE ); 1102 ok(style == expect_style, "%08x: got %08x\n", cb_style, style); 1103 ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle); 1104 1105 if (cb_style != CBS_SIMPLE) 1106 expect_exstyle |= WS_EX_TOPMOST; 1107 1108 SendMessageW(combo, CB_SHOWDROPDOWN, TRUE, 0 ); 1109 style = GetWindowLongW( info.hwndList, GWL_STYLE ); 1110 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE ); 1111 ok(style == (expect_style | WS_VISIBLE), "%08x: got %08x\n", cb_style, style); 1112 ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle); 1113 1114 SendMessageW(combo, CB_SHOWDROPDOWN, FALSE, 0 ); 1115 style = GetWindowLongW( info.hwndList, GWL_STYLE ); 1116 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE ); 1117 ok(style == expect_style, "%08x: got %08x\n", cb_style, style); 1118 ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle); 1119 1120 DestroyWindow(combo); 1121 } 1122 1123 static void test_combo_WS_VSCROLL(void) 1124 { 1125 HWND hCombo, hList; 1126 COMBOBOXINFO info; 1127 DWORD style; 1128 int i; 1129 1130 hCombo = create_combobox(CBS_DROPDOWNLIST); 1131 1132 get_combobox_info(hCombo, &info); 1133 hList = info.hwndList; 1134 1135 for (i = 0; i < 3; i++) 1136 { 1137 char buffer[2]; 1138 sprintf(buffer, "%d", i); 1139 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)buffer); 1140 } 1141 1142 style = GetWindowLongA(info.hwndList, GWL_STYLE); 1143 SetWindowLongA(hList, GWL_STYLE, style | WS_VSCROLL); 1144 1145 SendMessageA(hCombo, CB_SHOWDROPDOWN, TRUE, 0); 1146 SendMessageA(hCombo, CB_SHOWDROPDOWN, FALSE, 0); 1147 1148 style = GetWindowLongA(hList, GWL_STYLE); 1149 ok((style & WS_VSCROLL) != 0, "Style does not include WS_VSCROLL\n"); 1150 1151 DestroyWindow(hCombo); 1152 } 1153 1154 static void test_combo_dropdown_size(DWORD style) 1155 { 1156 static const char wine_test[] = "Wine Test"; 1157 HWND hCombo, hList; 1158 COMBOBOXINFO cbInfo; 1159 int i, test, ret; 1160 1161 static const struct list_size_info 1162 { 1163 int num_items; 1164 int height_combo; 1165 int limit; 1166 } info_height[] = { 1167 {33, 50, -1}, 1168 {35, 100, 40}, 1169 {15, 50, 3}, 1170 }; 1171 1172 for (test = 0; test < ARRAY_SIZE(info_height); test++) 1173 { 1174 const struct list_size_info *info_test = &info_height[test]; 1175 int height_item; /* Height of a list item */ 1176 int height_list; /* Height of the list we got */ 1177 int expected_height_list; 1178 RECT rect_list_client; 1179 int min_visible_expected; 1180 1181 hCombo = CreateWindowA(WC_COMBOBOXA, "Combo", CBS_DROPDOWN | WS_VISIBLE | WS_CHILD | style, 5, 5, 100, 1182 info_test->height_combo, hMainWnd, (HMENU)COMBO_ID, NULL, 0); 1183 1184 min_visible_expected = SendMessageA(hCombo, CB_GETMINVISIBLE, 0, 0); 1185 ok(min_visible_expected == 30, "Unexpected number of items %d.\n", min_visible_expected); 1186 1187 cbInfo.cbSize = sizeof(COMBOBOXINFO); 1188 ret = SendMessageA(hCombo, CB_GETCOMBOBOXINFO, 0, (LPARAM)&cbInfo); 1189 ok(ret, "Failed to get combo info, %d\n", ret); 1190 1191 hList = cbInfo.hwndList; 1192 for (i = 0; i < info_test->num_items; i++) 1193 { 1194 ret = SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM) wine_test); 1195 ok(ret == i, "Failed to add string %d, returned %d.\n", i, ret); 1196 } 1197 1198 if (info_test->limit != -1) 1199 { 1200 int min_visible_actual; 1201 min_visible_expected = info_test->limit; 1202 1203 ret = SendMessageA(hCombo, CB_SETMINVISIBLE, min_visible_expected, 0); 1204 ok(ret, "Failed to set visible limit.\n"); 1205 min_visible_actual = SendMessageA(hCombo, CB_GETMINVISIBLE, 0, 0); 1206 ok(min_visible_expected == min_visible_actual, "test %d: unexpected number of items %d.\n", 1207 test, min_visible_actual); 1208 } 1209 1210 ret = SendMessageA(hCombo, CB_SHOWDROPDOWN, TRUE,0); 1211 ok(ret, "Failed to show dropdown.\n"); 1212 ret = SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0); 1213 ok(ret, "Unexpected dropped state.\n"); 1214 1215 GetClientRect(hList, &rect_list_client); 1216 height_list = rect_list_client.bottom - rect_list_client.top; 1217 height_item = (int)SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0); 1218 1219 if (style & CBS_NOINTEGRALHEIGHT) 1220 { 1221 RECT rect_list_complete; 1222 int list_height_nonclient; 1223 int list_height_calculated; 1224 int edit_padding_size = cbInfo.rcItem.top; /* edit client rect top is the padding it has to its parent 1225 We assume it's the same on the bottom */ 1226 1227 GetWindowRect(hList, &rect_list_complete); 1228 1229 list_height_nonclient = (rect_list_complete.bottom - rect_list_complete.top) 1230 - (rect_list_client.bottom - rect_list_client.top); 1231 1232 /* Calculate the expected client size of the listbox popup from the size of the combobox. */ 1233 list_height_calculated = info_test->height_combo /* Take height we created combobox with */ 1234 - (cbInfo.rcItem.bottom - cbInfo.rcItem.top) /* Subtract size of edit control */ 1235 - list_height_nonclient /* Subtract list nonclient area */ 1236 - edit_padding_size * 2; /* subtract space around the edit control */ 1237 1238 expected_height_list = min(list_height_calculated, height_item * info_test->num_items); 1239 if (expected_height_list < 0) 1240 expected_height_list = 0; 1241 1242 ok(expected_height_list == height_list, "Test %d, expected list height to be %d, got %d\n", 1243 test, expected_height_list, height_list); 1244 } 1245 else 1246 { 1247 expected_height_list = min(info_test->num_items, min_visible_expected) * height_item; 1248 1249 ok(expected_height_list == height_list, "Test %d, expected list height to be %d, got %d\n", 1250 test, expected_height_list, height_list); 1251 } 1252 1253 DestroyWindow(hCombo); 1254 } 1255 } 1256 1257 START_TEST(combo) 1258 { 1259 ULONG_PTR ctx_cookie; 1260 HANDLE hCtx; 1261 1262 init_functions(); 1263 1264 if (!init()) 1265 return; 1266 1267 init_msg_sequences(sequences, NUM_MSG_SEQUENCES); 1268 1269 /* ComboBoxEx32 tests. */ 1270 test_comboex(); 1271 test_comboex_WM_LBUTTONDOWN(); 1272 test_comboex_CB_GETLBTEXT(); 1273 test_comboex_WM_WINDOWPOSCHANGING(); 1274 test_comboex_subclass(); 1275 test_comboex_get_set_item(); 1276 1277 if (!load_v6_module(&ctx_cookie, &hCtx)) 1278 { 1279 cleanup(); 1280 return; 1281 } 1282 1283 /* ComboBox control tests. */ 1284 test_combo_WS_VSCROLL(); 1285 test_combo_setfont(CBS_DROPDOWN); 1286 test_combo_setfont(CBS_DROPDOWNLIST); 1287 test_combo_setitemheight(CBS_DROPDOWN); 1288 test_combo_setitemheight(CBS_DROPDOWNLIST); 1289 test_combo_CBN_SELCHANGE(); 1290 test_combo_changesize(CBS_DROPDOWN); 1291 test_combo_changesize(CBS_DROPDOWNLIST); 1292 test_combo_editselection(); 1293 test_combo_editselection_focus(CBS_SIMPLE); 1294 test_combo_editselection_focus(CBS_DROPDOWN); 1295 test_combo_listbox_styles(CBS_SIMPLE); 1296 test_combo_listbox_styles(CBS_DROPDOWN); 1297 test_combo_listbox_styles(CBS_DROPDOWNLIST); 1298 test_combo_dropdown_size(0); 1299 test_combo_dropdown_size(CBS_NOINTEGRALHEIGHT); 1300 1301 cleanup(); 1302 unload_v6_module(ctx_cookie, hCtx); 1303 } 1304