1 /* Unit test suite for combo boxes. 2 * 3 * Copyright 2007 Mikolaj Zalewski 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 18 */ 19 20 #include <limits.h> 21 #include <stdarg.h> 22 #include <stdio.h> 23 24 #define STRICT 25 #define WIN32_LEAN_AND_MEAN 26 #include <windows.h> 27 28 #include "wine/test.h" 29 30 #define COMBO_ID 1995 31 32 #define COMBO_YBORDERSIZE() 2 33 34 static HWND hMainWnd; 35 36 #define expect_eq(expr, value, type, fmt); { type val = expr; ok(val == (value), #expr " expected " #fmt " got " #fmt "\n", (value), val); } 37 #define expect_rect(r, _left, _top, _right, _bottom) ok(r.left == _left && r.top == _top && \ 38 r.bottom == _bottom && r.right == _right, "Invalid rect %s vs (%d,%d)-(%d,%d)\n", \ 39 wine_dbgstr_rect(&r), _left, _top, _right, _bottom); 40 41 static HWND build_combo(DWORD style) 42 { 43 return CreateWindowA("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|style, 5, 5, 100, 100, hMainWnd, (HMENU)COMBO_ID, NULL, 0); 44 } 45 46 static int font_height(HFONT hFont) 47 { 48 TEXTMETRICA tm; 49 HFONT hFontOld; 50 HDC hDC; 51 52 hDC = CreateCompatibleDC(NULL); 53 hFontOld = SelectObject(hDC, hFont); 54 GetTextMetricsA(hDC, &tm); 55 SelectObject(hDC, hFontOld); 56 DeleteDC(hDC); 57 58 return tm.tmHeight; 59 } 60 61 static INT CALLBACK is_font_installed_proc(const LOGFONTA *elf, const TEXTMETRICA *tm, DWORD type, LPARAM lParam) 62 { 63 return 0; 64 } 65 66 static BOOL is_font_installed(const char *name) 67 { 68 HDC hdc = GetDC(NULL); 69 BOOL ret = !EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0); 70 ReleaseDC(NULL, hdc); 71 return ret; 72 } 73 74 static void test_setitemheight(DWORD style) 75 { 76 HWND hCombo = build_combo(style); 77 RECT r; 78 int i; 79 80 trace("Style %x\n", style); 81 GetClientRect(hCombo, &r); 82 expect_rect(r, 0, 0, 100, font_height(GetStockObject(SYSTEM_FONT)) + 8); 83 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); 84 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); 85 todo_wine expect_rect(r, 5, 5, 105, 105); 86 87 for (i = 1; i < 30; i++) 88 { 89 SendMessageA(hCombo, CB_SETITEMHEIGHT, -1, i); 90 GetClientRect(hCombo, &r); 91 expect_eq(r.bottom - r.top, i + 6, int, "%d"); 92 } 93 94 DestroyWindow(hCombo); 95 } 96 97 static void test_setfont(DWORD style) 98 { 99 HWND hCombo; 100 HFONT hFont1, hFont2; 101 RECT r; 102 int i; 103 104 if (!is_font_installed("Marlett")) 105 { 106 skip("Marlett font not available\n"); 107 return; 108 } 109 110 trace("Style %x\n", style); 111 112 hCombo = build_combo(style); 113 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"); 114 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"); 115 116 GetClientRect(hCombo, &r); 117 expect_rect(r, 0, 0, 100, font_height(GetStockObject(SYSTEM_FONT)) + 8); 118 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); 119 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); 120 todo_wine expect_rect(r, 5, 5, 105, 105); 121 122 /* The size of the dropped control is initially equal to the size 123 of the window when it was created. The size of the calculated 124 dropped area changes only by how much the selection area 125 changes, not by how much the list area changes. */ 126 if (font_height(hFont1) == 10 && font_height(hFont2) == 8) 127 { 128 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE); 129 GetClientRect(hCombo, &r); 130 expect_rect(r, 0, 0, 100, 18); 131 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); 132 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); 133 todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont1))); 134 135 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont2, FALSE); 136 GetClientRect(hCombo, &r); 137 expect_rect(r, 0, 0, 100, 16); 138 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); 139 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); 140 todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont2))); 141 142 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE); 143 GetClientRect(hCombo, &r); 144 expect_rect(r, 0, 0, 100, 18); 145 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); 146 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); 147 todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont1))); 148 } 149 else 150 { 151 ok(0, "Expected Marlett font heights 10/8, got %d/%d\n", 152 font_height(hFont1), font_height(hFont2)); 153 } 154 155 for (i = 1; i < 30; i++) 156 { 157 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"); 158 int height = font_height(hFont); 159 160 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont, FALSE); 161 GetClientRect(hCombo, &r); 162 expect_eq(r.bottom - r.top, height + 8, int, "%d"); 163 SendMessageA(hCombo, WM_SETFONT, 0, FALSE); 164 DeleteObject(hFont); 165 } 166 167 DestroyWindow(hCombo); 168 DeleteObject(hFont1); 169 DeleteObject(hFont2); 170 } 171 172 static LRESULT (CALLBACK *old_parent_proc)(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); 173 static LPCSTR expected_edit_text; 174 static LPCSTR expected_list_text; 175 static BOOL selchange_fired; 176 177 static LRESULT CALLBACK parent_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) 178 { 179 switch (msg) 180 { 181 case WM_COMMAND: 182 switch (wparam) 183 { 184 case MAKEWPARAM(COMBO_ID, CBN_SELCHANGE): 185 { 186 HWND hCombo = (HWND)lparam; 187 int idx; 188 char list[20], edit[20]; 189 190 memset(list, 0, sizeof(list)); 191 memset(edit, 0, sizeof(edit)); 192 193 idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0); 194 SendMessageA(hCombo, CB_GETLBTEXT, idx, (LPARAM)list); 195 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit); 196 197 ok(!strcmp(edit, expected_edit_text), "edit: got %s, expected %s\n", 198 edit, expected_edit_text); 199 ok(!strcmp(list, expected_list_text), "list: got %s, expected %s\n", 200 list, expected_list_text); 201 202 selchange_fired = TRUE; 203 } 204 break; 205 } 206 break; 207 } 208 209 return CallWindowProcA(old_parent_proc, hwnd, msg, wparam, lparam); 210 } 211 212 static void test_selection(DWORD style, const char * const text[], 213 const int *edit, const int *list) 214 { 215 INT idx; 216 HWND hCombo; 217 218 hCombo = build_combo(style); 219 220 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)text[0]); 221 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)text[1]); 222 SendMessageA(hCombo, CB_SETCURSEL, -1, 0); 223 224 old_parent_proc = (void *)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)parent_wnd_proc); 225 226 idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0); 227 ok(idx == -1, "expected selection -1, got %d\n", idx); 228 229 /* keyboard navigation */ 230 231 expected_list_text = text[list[0]]; 232 expected_edit_text = text[edit[0]]; 233 selchange_fired = FALSE; 234 SendMessageA(hCombo, WM_KEYDOWN, VK_DOWN, 0); 235 ok(selchange_fired, "CBN_SELCHANGE not sent!\n"); 236 237 expected_list_text = text[list[1]]; 238 expected_edit_text = text[edit[1]]; 239 selchange_fired = FALSE; 240 SendMessageA(hCombo, WM_KEYDOWN, VK_DOWN, 0); 241 ok(selchange_fired, "CBN_SELCHANGE not sent!\n"); 242 243 expected_list_text = text[list[2]]; 244 expected_edit_text = text[edit[2]]; 245 selchange_fired = FALSE; 246 SendMessageA(hCombo, WM_KEYDOWN, VK_UP, 0); 247 ok(selchange_fired, "CBN_SELCHANGE not sent!\n"); 248 249 /* programmatic navigation */ 250 251 expected_list_text = text[list[3]]; 252 expected_edit_text = text[edit[3]]; 253 selchange_fired = FALSE; 254 SendMessageA(hCombo, CB_SETCURSEL, list[3], 0); 255 ok(!selchange_fired, "CBN_SELCHANGE sent!\n"); 256 257 expected_list_text = text[list[4]]; 258 expected_edit_text = text[edit[4]]; 259 selchange_fired = FALSE; 260 SendMessageA(hCombo, CB_SETCURSEL, list[4], 0); 261 ok(!selchange_fired, "CBN_SELCHANGE sent!\n"); 262 263 SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc); 264 DestroyWindow(hCombo); 265 } 266 267 static void test_CBN_SELCHANGE(void) 268 { 269 static const char * const text[] = { "alpha", "beta", "" }; 270 static const int sel_1[] = { 2, 0, 1, 0, 1 }; 271 static const int sel_2[] = { 0, 1, 0, 0, 1 }; 272 273 test_selection(CBS_SIMPLE, text, sel_1, sel_2); 274 test_selection(CBS_DROPDOWN, text, sel_1, sel_2); 275 test_selection(CBS_DROPDOWNLIST, text, sel_2, sel_2); 276 } 277 278 static void test_WM_LBUTTONDOWN(void) 279 { 280 HWND hCombo, hEdit, hList; 281 COMBOBOXINFO cbInfo; 282 UINT x, y, item_height; 283 LRESULT result; 284 int i, idx; 285 RECT rect; 286 CHAR buffer[3]; 287 static const UINT choices[] = {8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72}; 288 static const CHAR stringFormat[] = "%2d"; 289 BOOL ret; 290 291 hCombo = CreateWindowA("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|CBS_DROPDOWN, 292 0, 0, 200, 150, hMainWnd, (HMENU)COMBO_ID, NULL, 0); 293 294 for (i = 0; i < sizeof(choices)/sizeof(UINT); i++){ 295 sprintf(buffer, stringFormat, choices[i]); 296 result = SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)buffer); 297 ok(result == i, 298 "Failed to add item %d\n", i); 299 } 300 301 cbInfo.cbSize = sizeof(COMBOBOXINFO); 302 SetLastError(0xdeadbeef); 303 ret = GetComboBoxInfo(hCombo, &cbInfo); 304 ok(ret, "Failed to get combobox info structure. LastError=%d\n", 305 GetLastError()); 306 hEdit = cbInfo.hwndItem; 307 hList = cbInfo.hwndList; 308 309 trace("hMainWnd=%p, hCombo=%p, hList=%p, hEdit=%p\n", hMainWnd, hCombo, hList, hEdit); 310 ok(GetFocus() == hMainWnd, "Focus not on Main Window, instead on %p\n", GetFocus()); 311 312 /* Click on the button to drop down the list */ 313 x = cbInfo.rcButton.left + (cbInfo.rcButton.right-cbInfo.rcButton.left)/2; 314 y = cbInfo.rcButton.top + (cbInfo.rcButton.bottom-cbInfo.rcButton.top)/2; 315 result = SendMessageA(hCombo, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y)); 316 ok(result, "WM_LBUTTONDOWN was not processed. LastError=%d\n", 317 GetLastError()); 318 ok(SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0), 319 "The dropdown list should have appeared after clicking the button.\n"); 320 321 ok(GetFocus() == hEdit, 322 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus()); 323 result = SendMessageA(hCombo, WM_LBUTTONUP, 0, MAKELPARAM(x, y)); 324 ok(result, "WM_LBUTTONUP was not processed. LastError=%d\n", 325 GetLastError()); 326 ok(GetFocus() == hEdit, 327 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus()); 328 329 /* Click on the 5th item in the list */ 330 item_height = SendMessageA(hCombo, CB_GETITEMHEIGHT, 0, 0); 331 ok(GetClientRect(hList, &rect), "Failed to get list's client rect.\n"); 332 x = rect.left + (rect.right-rect.left)/2; 333 y = item_height/2 + item_height*4; 334 result = SendMessageA(hList, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y)); 335 ok(!result, "WM_LBUTTONDOWN was not processed. LastError=%d\n", 336 GetLastError()); 337 ok(GetFocus() == hEdit, 338 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus()); 339 340 result = SendMessageA(hList, WM_MOUSEMOVE, 0, MAKELPARAM(x, y)); 341 ok(!result, "WM_MOUSEMOVE was not processed. LastError=%d\n", 342 GetLastError()); 343 ok(GetFocus() == hEdit, 344 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus()); 345 ok(SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0), 346 "The dropdown list should still be visible.\n"); 347 348 result = SendMessageA(hList, WM_LBUTTONUP, 0, MAKELPARAM(x, y)); 349 ok(!result, "WM_LBUTTONUP was not processed. LastError=%d\n", 350 GetLastError()); 351 ok(GetFocus() == hEdit, 352 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus()); 353 ok(!SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0), 354 "The dropdown list should have been rolled up.\n"); 355 idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0); 356 ok(idx, "Current Selection: expected %d, got %d\n", 4, idx); 357 358 DestroyWindow(hCombo); 359 } 360 361 static void test_changesize( DWORD style) 362 { 363 HWND hCombo = build_combo(style); 364 RECT rc; 365 INT ddheight, clheight, ddwidth, clwidth; 366 /* get initial measurements */ 367 GetClientRect( hCombo, &rc); 368 clheight = rc.bottom - rc.top; 369 clwidth = rc.right - rc.left; 370 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc); 371 ddheight = rc.bottom - rc.top; 372 ddwidth = rc.right - rc.left; 373 /* use MoveWindow to move & resize the combo */ 374 /* first make it slightly smaller */ 375 MoveWindow( hCombo, 10, 10, clwidth - 2, clheight - 2, TRUE); 376 GetClientRect( hCombo, &rc); 377 ok( rc.right - rc.left == clwidth - 2, "clientrect width is %d vs %d\n", 378 rc.right - rc.left, clwidth - 2); 379 ok( rc.bottom - rc.top == clheight, "clientrect height is %d vs %d\n", 380 rc.bottom - rc.top, clheight); 381 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc); 382 ok( rc.right - rc.left == clwidth - 2, "drop-down rect width is %d vs %d\n", 383 rc.right - rc.left, clwidth - 2); 384 ok( rc.bottom - rc.top == ddheight, "drop-down rect height is %d vs %d\n", 385 rc.bottom - rc.top, ddheight); 386 ok( rc.right - rc.left == ddwidth -2, "drop-down rect width is %d vs %d\n", 387 rc.right - rc.left, ddwidth - 2); 388 /* new cx, cy is slightly bigger than the initial values */ 389 MoveWindow( hCombo, 10, 10, clwidth + 2, clheight + 2, TRUE); 390 GetClientRect( hCombo, &rc); 391 ok( rc.right - rc.left == clwidth + 2, "clientrect width is %d vs %d\n", 392 rc.right - rc.left, clwidth + 2); 393 ok( rc.bottom - rc.top == clheight, "clientrect height is %d vs %d\n", 394 rc.bottom - rc.top, clheight); 395 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc); 396 ok( rc.right - rc.left == clwidth + 2, "drop-down rect width is %d vs %d\n", 397 rc.right - rc.left, clwidth + 2); 398 todo_wine { 399 ok( rc.bottom - rc.top == clheight + 2, "drop-down rect height is %d vs %d\n", 400 rc.bottom - rc.top, clheight + 2); 401 } 402 403 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, -1, 0); 404 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); 405 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); 406 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); 407 408 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 0, 0); 409 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); 410 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); 411 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); 412 413 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, clwidth - 1, 0); 414 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); 415 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); 416 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); 417 418 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, clwidth << 1, 0); 419 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1); 420 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); 421 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1); 422 423 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 0, 0); 424 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1); 425 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); 426 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1); 427 428 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 1, 0); 429 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); 430 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); 431 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); 432 433 DestroyWindow(hCombo); 434 } 435 436 static void test_editselection(void) 437 { 438 HWND hCombo; 439 INT start,end; 440 HWND hEdit; 441 COMBOBOXINFO cbInfo; 442 BOOL ret; 443 DWORD len; 444 char edit[20]; 445 446 /* Build a combo */ 447 hCombo = build_combo(CBS_SIMPLE); 448 cbInfo.cbSize = sizeof(COMBOBOXINFO); 449 SetLastError(0xdeadbeef); 450 ret = GetComboBoxInfo(hCombo, &cbInfo); 451 ok(ret, "Failed to get combobox info structure. LastError=%d\n", 452 GetLastError()); 453 hEdit = cbInfo.hwndItem; 454 455 /* Initially combo selection is empty*/ 456 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); 457 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len)); 458 ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len)); 459 460 /* Set some text, and press a key to replace it */ 461 edit[0] = 0x00; 462 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)"Jason1"); 463 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit); 464 ok(strcmp(edit, "Jason1")==0, "Unexpected text retrieved %s\n", edit); 465 466 /* Now what is the selection - still empty */ 467 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end); 468 ok(start==0, "Unexpected start position for selection %d\n", start); 469 ok(end==0, "Unexpected end position for selection %d\n", end); 470 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); 471 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len)); 472 ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len)); 473 474 /* Give it focus, and it gets selected */ 475 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit); 476 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end); 477 ok(start==0, "Unexpected start position for selection %d\n", start); 478 ok(end==6, "Unexpected end position for selection %d\n", end); 479 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); 480 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len)); 481 ok(HIWORD(len)==6, "Unexpected end position for selection %d\n", HIWORD(len)); 482 483 /* Now emulate a key press */ 484 edit[0] = 0x00; 485 SendMessageA(hCombo, WM_CHAR, 'A', 0x1c0001); 486 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit); 487 ok(strcmp(edit, "A")==0, "Unexpected text retrieved %s\n", edit); 488 489 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); 490 ok(LOWORD(len)==1, "Unexpected start position for selection %d\n", LOWORD(len)); 491 ok(HIWORD(len)==1, "Unexpected end position for selection %d\n", HIWORD(len)); 492 493 /* Now what happens when it gets more focus a second time - it doesn't reselect */ 494 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit); 495 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); 496 ok(LOWORD(len)==1, "Unexpected start position for selection %d\n", LOWORD(len)); 497 ok(HIWORD(len)==1, "Unexpected end position for selection %d\n", HIWORD(len)); 498 DestroyWindow(hCombo); 499 500 /* Start again - Build a combo */ 501 hCombo = build_combo(CBS_SIMPLE); 502 cbInfo.cbSize = sizeof(COMBOBOXINFO); 503 SetLastError(0xdeadbeef); 504 ret = GetComboBoxInfo(hCombo, &cbInfo); 505 ok(ret, "Failed to get combobox info structure. LastError=%d\n", 506 GetLastError()); 507 hEdit = cbInfo.hwndItem; 508 509 /* Set some text and give focus so it gets selected */ 510 edit[0] = 0x00; 511 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)"Jason2"); 512 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit); 513 ok(strcmp(edit, "Jason2")==0, "Unexpected text retrieved %s\n", edit); 514 515 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit); 516 517 /* Now what is the selection */ 518 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end); 519 ok(start==0, "Unexpected start position for selection %d\n", start); 520 ok(end==6, "Unexpected end position for selection %d\n", end); 521 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); 522 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len)); 523 ok(HIWORD(len)==6, "Unexpected end position for selection %d\n", HIWORD(len)); 524 525 /* Now change the selection to the apparently invalid start -1, end -1 and 526 show it means no selection (ie start -1) but cursor at end */ 527 SendMessageA(hCombo, CB_SETEDITSEL, 0, -1); 528 edit[0] = 0x00; 529 SendMessageA(hCombo, WM_CHAR, 'A', 0x1c0001); 530 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit); 531 ok(strcmp(edit, "Jason2A")==0, "Unexpected text retrieved %s\n", edit); 532 DestroyWindow(hCombo); 533 } 534 535 static WNDPROC edit_window_proc; 536 static long setsel_start = 1, setsel_end = 1; 537 static HWND hCBN_SetFocus, hCBN_KillFocus; 538 539 static LRESULT CALLBACK combobox_subclass_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 540 { 541 if (msg == EM_SETSEL) 542 { 543 setsel_start = wParam; 544 setsel_end = lParam; 545 } 546 return CallWindowProcA(edit_window_proc, hwnd, msg, wParam, lParam); 547 } 548 549 static LRESULT CALLBACK test_window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) 550 { 551 switch (msg) 552 { 553 case WM_COMMAND: 554 switch (HIWORD(wParam)) 555 { 556 case CBN_SETFOCUS: 557 hCBN_SetFocus = (HWND)lParam; 558 break; 559 case CBN_KILLFOCUS: 560 hCBN_KillFocus = (HWND)lParam; 561 break; 562 } 563 break; 564 case WM_NEXTDLGCTL: 565 SetFocus((HWND)wParam); 566 break; 567 } 568 return CallWindowProcA(old_parent_proc, hwnd, msg, wParam, lParam); 569 } 570 571 static void test_editselection_focus(DWORD style) 572 { 573 HWND hCombo, hEdit, hButton; 574 COMBOBOXINFO cbInfo; 575 BOOL ret; 576 const char wine_test[] = "Wine Test"; 577 char buffer[16] = {0}; 578 DWORD len; 579 580 hCombo = build_combo(style); 581 cbInfo.cbSize = sizeof(COMBOBOXINFO); 582 SetLastError(0xdeadbeef); 583 ret = GetComboBoxInfo(hCombo, &cbInfo); 584 ok(ret, "Failed to get COMBOBOXINFO structure; LastError: %u\n", GetLastError()); 585 hEdit = cbInfo.hwndItem; 586 587 hButton = CreateWindowA("Button", "OK", WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON, 588 5, 50, 100, 20, hMainWnd, NULL, 589 (HINSTANCE)GetWindowLongPtrA(hMainWnd, GWLP_HINSTANCE), NULL); 590 591 old_parent_proc = (WNDPROC)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)test_window_proc); 592 edit_window_proc = (WNDPROC)SetWindowLongPtrA(hEdit, GWLP_WNDPROC, (ULONG_PTR)combobox_subclass_proc); 593 594 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit); 595 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start); 596 todo_wine ok(setsel_end == INT_MAX, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end); 597 ok(hCBN_SetFocus == hCombo, "Wrong handle set by CBN_SETFOCUS; got %p\n", hCBN_SetFocus); 598 ok(GetFocus() == hEdit, "hEdit should have keyboard focus\n"); 599 600 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hButton, TRUE); 601 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start); 602 todo_wine ok(setsel_end == 0, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end); 603 ok(hCBN_KillFocus == hCombo, "Wrong handle set by CBN_KILLFOCUS; got %p\n", hCBN_KillFocus); 604 ok(GetFocus() == hButton, "hButton should have keyboard focus\n"); 605 606 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)wine_test); 607 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hCombo, TRUE); 608 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start); 609 todo_wine ok(setsel_end == INT_MAX, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end); 610 ok(hCBN_SetFocus == hCombo, "Wrong handle set by CBN_SETFOCUS; got %p\n", hCBN_SetFocus); 611 ok(GetFocus() == hEdit, "hEdit should have keyboard focus\n"); 612 SendMessageA(hCombo, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer); 613 ok(!strcmp(buffer, wine_test), "Unexpected text in edit control; got '%s'\n", buffer); 614 615 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hButton, TRUE); 616 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start); 617 todo_wine ok(setsel_end == 0, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end); 618 ok(hCBN_KillFocus == hCombo, "Wrong handle set by CBN_KILLFOCUS; got %p\n", hCBN_KillFocus); 619 ok(GetFocus() == hButton, "hButton should have keyboard focus\n"); 620 len = SendMessageA(hCombo, CB_GETEDITSEL, 0, 0); 621 ok(len == 0, "Unexpected text selection; start: %u, end: %u\n", LOWORD(len), HIWORD(len)); 622 623 SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc); 624 DestroyWindow(hButton); 625 DestroyWindow(hCombo); 626 } 627 628 static void test_listbox_styles(DWORD cb_style) 629 { 630 HWND combo; 631 COMBOBOXINFO info; 632 DWORD style, exstyle, expect_style, expect_exstyle; 633 BOOL ret; 634 635 expect_style = WS_CHILD|WS_CLIPSIBLINGS|LBS_COMBOBOX|LBS_HASSTRINGS|LBS_NOTIFY; 636 if (cb_style == CBS_SIMPLE) 637 { 638 expect_style |= WS_VISIBLE; 639 expect_exstyle = WS_EX_CLIENTEDGE; 640 } 641 else 642 { 643 expect_style |= WS_BORDER; 644 expect_exstyle = WS_EX_TOOLWINDOW; 645 } 646 647 combo = build_combo(cb_style); 648 info.cbSize = sizeof(COMBOBOXINFO); 649 SetLastError(0xdeadbeef); 650 ret = GetComboBoxInfo(combo, &info); 651 ok(ret, "Failed to get combobox info structure.\n"); 652 653 style = GetWindowLongW( info.hwndList, GWL_STYLE ); 654 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE ); 655 ok(style == expect_style, "%08x: got %08x\n", cb_style, style); 656 ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle); 657 658 if (cb_style != CBS_SIMPLE) 659 expect_exstyle |= WS_EX_TOPMOST; 660 661 SendMessageW(combo, CB_SHOWDROPDOWN, TRUE, 0 ); 662 style = GetWindowLongW( info.hwndList, GWL_STYLE ); 663 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE ); 664 ok(style == (expect_style | WS_VISIBLE), "%08x: got %08x\n", cb_style, style); 665 ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle); 666 667 SendMessageW(combo, CB_SHOWDROPDOWN, FALSE, 0 ); 668 style = GetWindowLongW( info.hwndList, GWL_STYLE ); 669 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE ); 670 ok(style == expect_style, "%08x: got %08x\n", cb_style, style); 671 ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle); 672 673 DestroyWindow(combo); 674 } 675 676 static void test_listbox_size(DWORD style) 677 { 678 HWND hCombo, hList; 679 COMBOBOXINFO cbInfo; 680 UINT x, y; 681 BOOL ret; 682 int i, test; 683 const char wine_test[] = "Wine Test"; 684 685 static const struct list_size_info 686 { 687 int num_items; 688 int height_combo; 689 BOOL todo; 690 } info_height[] = { 691 {2, 24, TRUE}, 692 {2, 41, TRUE}, 693 {2, 42, TRUE}, 694 {2, 50, TRUE}, 695 {2, 60}, 696 {2, 80}, 697 {2, 89}, 698 {2, 90}, 699 {2, 100}, 700 701 {10, 24, TRUE}, 702 {10, 41, TRUE}, 703 {10, 42, TRUE}, 704 {10, 50, TRUE}, 705 {10, 60, TRUE}, 706 {10, 80, TRUE}, 707 {10, 89, TRUE}, 708 {10, 90, TRUE}, 709 {10, 100, TRUE}, 710 }; 711 712 for(test = 0; test < sizeof(info_height) / sizeof(info_height[0]); test++) 713 { 714 const struct list_size_info *info_test = &info_height[test]; 715 int height_item; /* Height of a list item */ 716 int height_list; /* Height of the list we got */ 717 int expected_count_list; 718 int expected_height_list; 719 int list_height_nonclient; 720 int list_height_calculated; 721 RECT rect_list_client, rect_list_complete; 722 723 hCombo = CreateWindowA("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|style, 5, 5, 100, 724 info_test->height_combo, hMainWnd, (HMENU)COMBO_ID, NULL, 0); 725 726 cbInfo.cbSize = sizeof(COMBOBOXINFO); 727 SetLastError(0xdeadbeef); 728 ret = GetComboBoxInfo(hCombo, &cbInfo); 729 ok(ret, "Failed to get COMBOBOXINFO structure; LastError: %u\n", GetLastError()); 730 731 hList = cbInfo.hwndList; 732 for (i = 0; i < info_test->num_items; i++) 733 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM) wine_test); 734 735 /* Click on the button to drop down the list */ 736 x = cbInfo.rcButton.left + (cbInfo.rcButton.right-cbInfo.rcButton.left)/2; 737 y = cbInfo.rcButton.top + (cbInfo.rcButton.bottom-cbInfo.rcButton.top)/2; 738 ret = SendMessageA(hCombo, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y)); 739 ok(ret, "WM_LBUTTONDOWN was not processed. LastError=%d\n", 740 GetLastError()); 741 ok(SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0), 742 "The dropdown list should have appeared after clicking the button.\n"); 743 744 GetClientRect(hList, &rect_list_client); 745 GetWindowRect(hList, &rect_list_complete); 746 height_list = rect_list_client.bottom - rect_list_client.top; 747 height_item = (int)SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0); 748 749 list_height_nonclient = (rect_list_complete.bottom - rect_list_complete.top) 750 - (rect_list_client.bottom - rect_list_client.top); 751 752 /* Calculate the expected client size of the listbox popup from the size of the combobox. */ 753 list_height_calculated = info_test->height_combo 754 - (cbInfo.rcItem.bottom + COMBO_YBORDERSIZE()) 755 - list_height_nonclient 756 - 1; 757 758 expected_count_list = list_height_calculated / height_item; 759 if(expected_count_list < 0) 760 expected_count_list = 0; 761 expected_count_list = min(expected_count_list, info_test->num_items); 762 expected_height_list = expected_count_list * height_item; 763 764 todo_wine_if(info_test->todo) 765 ok(expected_height_list == height_list, 766 "Test %d, expected list height to be %d, got %d\n", test, expected_height_list, height_list); 767 768 DestroyWindow(hCombo); 769 } 770 } 771 772 static void test_WS_VSCROLL(void) 773 { 774 HWND hCombo, hList; 775 COMBOBOXINFO info; 776 DWORD style; 777 BOOL ret; 778 int i; 779 780 info.cbSize = sizeof(info); 781 hCombo = build_combo(CBS_DROPDOWNLIST); 782 783 SetLastError(0xdeadbeef); 784 ret = GetComboBoxInfo(hCombo, &info); 785 ok(ret, "Failed to get COMBOBOXINFO structure; LastError: %u\n", GetLastError()); 786 hList = info.hwndList; 787 788 for(i = 0; i < 3; i++) 789 { 790 char buffer[2]; 791 sprintf(buffer, "%d", i); 792 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)buffer); 793 } 794 795 style = GetWindowLongA(info.hwndList, GWL_STYLE); 796 SetWindowLongA(hList, GWL_STYLE, style | WS_VSCROLL); 797 798 SendMessageA(hCombo, CB_SHOWDROPDOWN, TRUE, 0); 799 SendMessageA(hCombo, CB_SHOWDROPDOWN, FALSE, 0); 800 801 style = GetWindowLongA(hList, GWL_STYLE); 802 ok((style & WS_VSCROLL) != 0, "Style does not include WS_VSCROLL\n"); 803 804 DestroyWindow(hCombo); 805 } 806 807 START_TEST(combo) 808 { 809 hMainWnd = CreateWindowA("static", "Test", WS_OVERLAPPEDWINDOW, 10, 10, 300, 300, NULL, NULL, NULL, 0); 810 ShowWindow(hMainWnd, SW_SHOW); 811 812 test_WS_VSCROLL(); 813 test_setfont(CBS_DROPDOWN); 814 test_setfont(CBS_DROPDOWNLIST); 815 test_setitemheight(CBS_DROPDOWN); 816 test_setitemheight(CBS_DROPDOWNLIST); 817 test_CBN_SELCHANGE(); 818 test_WM_LBUTTONDOWN(); 819 test_changesize(CBS_DROPDOWN); 820 test_changesize(CBS_DROPDOWNLIST); 821 test_editselection(); 822 test_editselection_focus(CBS_SIMPLE); 823 test_editselection_focus(CBS_DROPDOWN); 824 test_listbox_styles(CBS_SIMPLE); 825 test_listbox_styles(CBS_DROPDOWN); 826 test_listbox_styles(CBS_DROPDOWNLIST); 827 test_listbox_size(CBS_DROPDOWN); 828 829 DestroyWindow(hMainWnd); 830 } 831