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