Revert "user32/combo: Set listbox popup height correctly and add tests.".
[wine.git] / dlls / user32 / tests / combo.c
blob72e8777d2078f11397caa70d92d20f633532eadf
1 /* Unit test suite for combo boxes.
3 * Copyright 2007 Mikolaj Zalewski
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.
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.
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
20 #include <limits.h>
21 #include <stdarg.h>
22 #include <stdio.h>
24 #define STRICT
25 #define WIN32_LEAN_AND_MEAN
26 #include <windows.h>
28 #include "wine/test.h"
30 #define COMBO_ID 1995
32 #define COMBO_YBORDERSIZE() 2
34 static HWND hMainWnd;
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);
41 static HWND build_combo(DWORD style)
43 return CreateWindowA("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|style, 5, 5, 100, 100, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
46 static int font_height(HFONT hFont)
48 TEXTMETRICA tm;
49 HFONT hFontOld;
50 HDC hDC;
52 hDC = CreateCompatibleDC(NULL);
53 hFontOld = SelectObject(hDC, hFont);
54 GetTextMetricsA(hDC, &tm);
55 SelectObject(hDC, hFontOld);
56 DeleteDC(hDC);
58 return tm.tmHeight;
61 static INT CALLBACK is_font_installed_proc(const LOGFONTA *elf, const TEXTMETRICA *tm, DWORD type, LPARAM lParam)
63 return 0;
66 static BOOL is_font_installed(const char *name)
68 HDC hdc = GetDC(NULL);
69 BOOL ret = !EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0);
70 ReleaseDC(NULL, hdc);
71 return ret;
74 static void test_setitemheight(DWORD style)
76 HWND hCombo = build_combo(style);
77 RECT r;
78 int i;
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);
87 for (i = 1; i < 30; i++)
89 SendMessageA(hCombo, CB_SETITEMHEIGHT, -1, i);
90 GetClientRect(hCombo, &r);
91 expect_eq(r.bottom - r.top, i + 6, int, "%d");
94 DestroyWindow(hCombo);
97 static void test_setfont(DWORD style)
99 HWND hCombo;
100 HFONT hFont1, hFont2;
101 RECT r;
102 int i;
104 if (!is_font_installed("Marlett"))
106 skip("Marlett font not available\n");
107 return;
110 trace("Style %x\n", style);
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");
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);
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)
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)));
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)));
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)));
149 else
151 ok(0, "Expected Marlett font heights 10/8, got %d/%d\n",
152 font_height(hFont1), font_height(hFont2));
155 for (i = 1; i < 30; i++)
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);
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);
167 DestroyWindow(hCombo);
168 DeleteObject(hFont1);
169 DeleteObject(hFont2);
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;
177 static LRESULT CALLBACK parent_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
179 switch (msg)
181 case WM_COMMAND:
182 switch (wparam)
184 case MAKEWPARAM(COMBO_ID, CBN_SELCHANGE):
186 HWND hCombo = (HWND)lparam;
187 int idx;
188 char list[20], edit[20];
190 memset(list, 0, sizeof(list));
191 memset(edit, 0, sizeof(edit));
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);
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);
202 selchange_fired = TRUE;
204 break;
206 break;
209 return CallWindowProcA(old_parent_proc, hwnd, msg, wparam, lparam);
212 static void test_selection(DWORD style, const char * const text[],
213 const int *edit, const int *list)
215 INT idx;
216 HWND hCombo;
218 hCombo = build_combo(style);
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);
224 old_parent_proc = (void *)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)parent_wnd_proc);
226 idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0);
227 ok(idx == -1, "expected selection -1, got %d\n", idx);
229 /* keyboard navigation */
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");
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");
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");
249 /* programmatic navigation */
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");
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");
263 SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc);
264 DestroyWindow(hCombo);
267 static void test_CBN_SELCHANGE(void)
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 };
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);
278 static void test_WM_LBUTTONDOWN(void)
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 BOOL (WINAPI *pGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
292 pGetComboBoxInfo = (void*)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo");
293 if (!pGetComboBoxInfo){
294 win_skip("GetComboBoxInfo is not available\n");
295 return;
298 hCombo = CreateWindowA("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|CBS_DROPDOWN,
299 0, 0, 200, 150, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
301 for (i = 0; i < sizeof(choices)/sizeof(UINT); i++){
302 sprintf(buffer, stringFormat, choices[i]);
303 result = SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)buffer);
304 ok(result == i,
305 "Failed to add item %d\n", i);
308 cbInfo.cbSize = sizeof(COMBOBOXINFO);
309 SetLastError(0xdeadbeef);
310 ret = pGetComboBoxInfo(hCombo, &cbInfo);
311 ok(ret, "Failed to get combobox info structure. LastError=%d\n",
312 GetLastError());
313 hEdit = cbInfo.hwndItem;
314 hList = cbInfo.hwndList;
316 trace("hMainWnd=%p, hCombo=%p, hList=%p, hEdit=%p\n", hMainWnd, hCombo, hList, hEdit);
317 ok(GetFocus() == hMainWnd, "Focus not on Main Window, instead on %p\n", GetFocus());
319 /* Click on the button to drop down the list */
320 x = cbInfo.rcButton.left + (cbInfo.rcButton.right-cbInfo.rcButton.left)/2;
321 y = cbInfo.rcButton.top + (cbInfo.rcButton.bottom-cbInfo.rcButton.top)/2;
322 result = SendMessageA(hCombo, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
323 ok(result, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
324 GetLastError());
325 ok(SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0),
326 "The dropdown list should have appeared after clicking the button.\n");
328 ok(GetFocus() == hEdit,
329 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
330 result = SendMessageA(hCombo, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
331 ok(result, "WM_LBUTTONUP was not processed. LastError=%d\n",
332 GetLastError());
333 ok(GetFocus() == hEdit,
334 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
336 /* Click on the 5th item in the list */
337 item_height = SendMessageA(hCombo, CB_GETITEMHEIGHT, 0, 0);
338 ok(GetClientRect(hList, &rect), "Failed to get list's client rect.\n");
339 x = rect.left + (rect.right-rect.left)/2;
340 y = item_height/2 + item_height*4;
341 result = SendMessageA(hList, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
342 ok(!result, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
343 GetLastError());
344 ok(GetFocus() == hEdit,
345 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
347 result = SendMessageA(hList, WM_MOUSEMOVE, 0, MAKELPARAM(x, y));
348 ok(!result, "WM_MOUSEMOVE was not processed. LastError=%d\n",
349 GetLastError());
350 ok(GetFocus() == hEdit,
351 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
352 ok(SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0),
353 "The dropdown list should still be visible.\n");
355 result = SendMessageA(hList, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
356 ok(!result, "WM_LBUTTONUP was not processed. LastError=%d\n",
357 GetLastError());
358 ok(GetFocus() == hEdit,
359 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
360 ok(!SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0),
361 "The dropdown list should have been rolled up.\n");
362 idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0);
363 ok(idx, "Current Selection: expected %d, got %d\n", 4, idx);
365 DestroyWindow(hCombo);
368 static void test_changesize( DWORD style)
370 HWND hCombo = build_combo(style);
371 RECT rc;
372 INT ddheight, clheight, ddwidth, clwidth;
373 /* get initial measurements */
374 GetClientRect( hCombo, &rc);
375 clheight = rc.bottom - rc.top;
376 clwidth = rc.right - rc.left;
377 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
378 ddheight = rc.bottom - rc.top;
379 ddwidth = rc.right - rc.left;
380 /* use MoveWindow to move & resize the combo */
381 /* first make it slightly smaller */
382 MoveWindow( hCombo, 10, 10, clwidth - 2, clheight - 2, TRUE);
383 GetClientRect( hCombo, &rc);
384 ok( rc.right - rc.left == clwidth - 2, "clientrect width is %d vs %d\n",
385 rc.right - rc.left, clwidth - 2);
386 ok( rc.bottom - rc.top == clheight, "clientrect height is %d vs %d\n",
387 rc.bottom - rc.top, clheight);
388 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
389 ok( rc.right - rc.left == clwidth - 2, "drop-down rect width is %d vs %d\n",
390 rc.right - rc.left, clwidth - 2);
391 ok( rc.bottom - rc.top == ddheight, "drop-down rect height is %d vs %d\n",
392 rc.bottom - rc.top, ddheight);
393 ok( rc.right - rc.left == ddwidth -2, "drop-down rect width is %d vs %d\n",
394 rc.right - rc.left, ddwidth - 2);
395 /* new cx, cy is slightly bigger than the initial values */
396 MoveWindow( hCombo, 10, 10, clwidth + 2, clheight + 2, TRUE);
397 GetClientRect( hCombo, &rc);
398 ok( rc.right - rc.left == clwidth + 2, "clientrect width is %d vs %d\n",
399 rc.right - rc.left, clwidth + 2);
400 ok( rc.bottom - rc.top == clheight, "clientrect height is %d vs %d\n",
401 rc.bottom - rc.top, clheight);
402 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
403 ok( rc.right - rc.left == clwidth + 2, "drop-down rect width is %d vs %d\n",
404 rc.right - rc.left, clwidth + 2);
405 todo_wine {
406 ok( rc.bottom - rc.top == clheight + 2, "drop-down rect height is %d vs %d\n",
407 rc.bottom - rc.top, clheight + 2);
410 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, -1, 0);
411 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
412 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
413 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
415 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 0, 0);
416 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
417 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
418 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
420 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, clwidth - 1, 0);
421 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
422 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
423 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
425 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, clwidth << 1, 0);
426 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
427 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
428 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
430 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 0, 0);
431 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
432 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
433 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
435 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 1, 0);
436 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
437 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
438 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
440 DestroyWindow(hCombo);
443 static void test_editselection(void)
445 HWND hCombo;
446 INT start,end;
447 HWND hEdit;
448 COMBOBOXINFO cbInfo;
449 BOOL ret;
450 DWORD len;
451 BOOL (WINAPI *pGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
452 char edit[20];
454 pGetComboBoxInfo = (void*)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo");
455 if (!pGetComboBoxInfo){
456 win_skip("GetComboBoxInfo is not available\n");
457 return;
460 /* Build a combo */
461 hCombo = build_combo(CBS_SIMPLE);
462 cbInfo.cbSize = sizeof(COMBOBOXINFO);
463 SetLastError(0xdeadbeef);
464 ret = pGetComboBoxInfo(hCombo, &cbInfo);
465 ok(ret, "Failed to get combobox info structure. LastError=%d\n",
466 GetLastError());
467 hEdit = cbInfo.hwndItem;
469 /* Initially combo selection is empty*/
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));
474 /* Set some text, and press a key to replace it */
475 edit[0] = 0x00;
476 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)"Jason1");
477 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
478 ok(strcmp(edit, "Jason1")==0, "Unexpected text retrieved %s\n", edit);
480 /* Now what is the selection - still empty */
481 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end);
482 ok(start==0, "Unexpected start position for selection %d\n", start);
483 ok(end==0, "Unexpected end position for selection %d\n", end);
484 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
485 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
486 ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len));
488 /* Give it focus, and it gets selected */
489 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
490 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end);
491 ok(start==0, "Unexpected start position for selection %d\n", start);
492 ok(end==6, "Unexpected end position for selection %d\n", end);
493 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
494 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
495 ok(HIWORD(len)==6, "Unexpected end position for selection %d\n", HIWORD(len));
497 /* Now emulate a key press */
498 edit[0] = 0x00;
499 SendMessageA(hCombo, WM_CHAR, 'A', 0x1c0001);
500 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
501 ok(strcmp(edit, "A")==0, "Unexpected text retrieved %s\n", edit);
503 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
504 ok(LOWORD(len)==1, "Unexpected start position for selection %d\n", LOWORD(len));
505 ok(HIWORD(len)==1, "Unexpected end position for selection %d\n", HIWORD(len));
507 /* Now what happens when it gets more focus a second time - it doesn't reselect */
508 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
509 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
510 ok(LOWORD(len)==1, "Unexpected start position for selection %d\n", LOWORD(len));
511 ok(HIWORD(len)==1, "Unexpected end position for selection %d\n", HIWORD(len));
512 DestroyWindow(hCombo);
514 /* Start again - Build a combo */
515 hCombo = build_combo(CBS_SIMPLE);
516 cbInfo.cbSize = sizeof(COMBOBOXINFO);
517 SetLastError(0xdeadbeef);
518 ret = pGetComboBoxInfo(hCombo, &cbInfo);
519 ok(ret, "Failed to get combobox info structure. LastError=%d\n",
520 GetLastError());
521 hEdit = cbInfo.hwndItem;
523 /* Set some text and give focus so it gets selected */
524 edit[0] = 0x00;
525 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)"Jason2");
526 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
527 ok(strcmp(edit, "Jason2")==0, "Unexpected text retrieved %s\n", edit);
529 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
531 /* Now what is the selection */
532 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end);
533 ok(start==0, "Unexpected start position for selection %d\n", start);
534 ok(end==6, "Unexpected end position for selection %d\n", end);
535 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
536 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
537 ok(HIWORD(len)==6, "Unexpected end position for selection %d\n", HIWORD(len));
539 /* Now change the selection to the apparently invalid start -1, end -1 and
540 show it means no selection (ie start -1) but cursor at end */
541 SendMessageA(hCombo, CB_SETEDITSEL, 0, -1);
542 edit[0] = 0x00;
543 SendMessageA(hCombo, WM_CHAR, 'A', 0x1c0001);
544 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
545 ok(strcmp(edit, "Jason2A")==0, "Unexpected text retrieved %s\n", edit);
546 DestroyWindow(hCombo);
549 static WNDPROC edit_window_proc;
550 static long setsel_start = 1, setsel_end = 1;
551 static HWND hCBN_SetFocus, hCBN_KillFocus;
553 static LRESULT CALLBACK combobox_subclass_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
555 if (msg == EM_SETSEL)
557 setsel_start = wParam;
558 setsel_end = lParam;
560 return CallWindowProcA(edit_window_proc, hwnd, msg, wParam, lParam);
563 static LRESULT CALLBACK test_window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
565 switch (msg)
567 case WM_COMMAND:
568 switch (HIWORD(wParam))
570 case CBN_SETFOCUS:
571 hCBN_SetFocus = (HWND)lParam;
572 break;
573 case CBN_KILLFOCUS:
574 hCBN_KillFocus = (HWND)lParam;
575 break;
577 break;
578 case WM_NEXTDLGCTL:
579 SetFocus((HWND)wParam);
580 break;
582 return CallWindowProcA(old_parent_proc, hwnd, msg, wParam, lParam);
585 static void test_editselection_focus(DWORD style)
587 BOOL (WINAPI *pGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
588 HWND hCombo, hEdit, hButton;
589 COMBOBOXINFO cbInfo;
590 BOOL ret;
591 const char wine_test[] = "Wine Test";
592 char buffer[16] = {0};
593 DWORD len;
595 pGetComboBoxInfo = (void *)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo");
596 if (!pGetComboBoxInfo)
598 win_skip("GetComboBoxInfo is not available\n");
599 return;
602 hCombo = build_combo(style);
603 cbInfo.cbSize = sizeof(COMBOBOXINFO);
604 SetLastError(0xdeadbeef);
605 ret = pGetComboBoxInfo(hCombo, &cbInfo);
606 ok(ret, "Failed to get COMBOBOXINFO structure; LastError: %u\n", GetLastError());
607 hEdit = cbInfo.hwndItem;
609 hButton = CreateWindowA("Button", "OK", WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,
610 5, 50, 100, 20, hMainWnd, NULL,
611 (HINSTANCE)GetWindowLongPtrA(hMainWnd, GWLP_HINSTANCE), NULL);
613 old_parent_proc = (WNDPROC)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)test_window_proc);
614 edit_window_proc = (WNDPROC)SetWindowLongPtrA(hEdit, GWLP_WNDPROC, (ULONG_PTR)combobox_subclass_proc);
616 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
617 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
618 todo_wine ok(setsel_end == INT_MAX, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
619 ok(hCBN_SetFocus == hCombo, "Wrong handle set by CBN_SETFOCUS; got %p\n", hCBN_SetFocus);
620 ok(GetFocus() == hEdit, "hEdit should have keyboard focus\n");
622 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hButton, TRUE);
623 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
624 todo_wine ok(setsel_end == 0, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
625 ok(hCBN_KillFocus == hCombo, "Wrong handle set by CBN_KILLFOCUS; got %p\n", hCBN_KillFocus);
626 ok(GetFocus() == hButton, "hButton should have keyboard focus\n");
628 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)wine_test);
629 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hCombo, TRUE);
630 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
631 todo_wine ok(setsel_end == INT_MAX, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
632 ok(hCBN_SetFocus == hCombo, "Wrong handle set by CBN_SETFOCUS; got %p\n", hCBN_SetFocus);
633 ok(GetFocus() == hEdit, "hEdit should have keyboard focus\n");
634 SendMessageA(hCombo, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
635 ok(!strcmp(buffer, wine_test), "Unexpected text in edit control; got '%s'\n", buffer);
637 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hButton, TRUE);
638 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
639 todo_wine ok(setsel_end == 0, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
640 ok(hCBN_KillFocus == hCombo, "Wrong handle set by CBN_KILLFOCUS; got %p\n", hCBN_KillFocus);
641 ok(GetFocus() == hButton, "hButton should have keyboard focus\n");
642 len = SendMessageA(hCombo, CB_GETEDITSEL, 0, 0);
643 ok(len == 0, "Unexpected text selection; start: %u, end: %u\n", LOWORD(len), HIWORD(len));
645 SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc);
646 DestroyWindow(hButton);
647 DestroyWindow(hCombo);
650 static void test_listbox_styles(DWORD cb_style)
652 BOOL (WINAPI *pGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
653 HWND combo;
654 COMBOBOXINFO info;
655 DWORD style, exstyle, expect_style, expect_exstyle;
656 BOOL ret;
658 pGetComboBoxInfo = (void*)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo");
659 if (!pGetComboBoxInfo){
660 win_skip("GetComboBoxInfo is not available\n");
661 return;
664 expect_style = WS_CHILD|WS_CLIPSIBLINGS|LBS_COMBOBOX|LBS_HASSTRINGS|LBS_NOTIFY;
665 if (cb_style == CBS_SIMPLE)
667 expect_style |= WS_VISIBLE;
668 expect_exstyle = WS_EX_CLIENTEDGE;
670 else
672 expect_style |= WS_BORDER;
673 expect_exstyle = WS_EX_TOOLWINDOW;
676 combo = build_combo(cb_style);
677 info.cbSize = sizeof(COMBOBOXINFO);
678 SetLastError(0xdeadbeef);
679 ret = pGetComboBoxInfo(combo, &info);
680 ok(ret, "Failed to get combobox info structure.\n");
682 style = GetWindowLongW( info.hwndList, GWL_STYLE );
683 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE );
684 ok(style == expect_style, "%08x: got %08x\n", cb_style, style);
685 ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle);
687 if (cb_style != CBS_SIMPLE)
688 expect_exstyle |= WS_EX_TOPMOST;
690 SendMessageW(combo, CB_SHOWDROPDOWN, TRUE, 0 );
691 style = GetWindowLongW( info.hwndList, GWL_STYLE );
692 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE );
693 ok(style == (expect_style | WS_VISIBLE), "%08x: got %08x\n", cb_style, style);
694 ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle);
696 SendMessageW(combo, CB_SHOWDROPDOWN, FALSE, 0 );
697 style = GetWindowLongW( info.hwndList, GWL_STYLE );
698 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE );
699 ok(style == expect_style, "%08x: got %08x\n", cb_style, style);
700 ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle);
702 DestroyWindow(combo);
705 static void test_listbox_size(DWORD style)
707 BOOL (WINAPI *pGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
708 HWND hCombo, hList;
709 COMBOBOXINFO cbInfo;
710 UINT x, y;
711 BOOL ret;
712 int i, test;
713 const char wine_test[] = "Wine Test";
715 static const struct list_size_info
717 int num_items;
718 int height_combo;
719 BOOL todo;
720 } info_height[] = {
721 {2, 24, TRUE},
722 {2, 41, TRUE},
723 {2, 42, TRUE},
724 {2, 50, TRUE},
725 {2, 60},
726 {2, 80},
727 {2, 89},
728 {2, 90},
729 {2, 100},
731 {10, 24, TRUE},
732 {10, 41, TRUE},
733 {10, 42, TRUE},
734 {10, 50, TRUE},
735 {10, 60, TRUE},
736 {10, 80, TRUE},
737 {10, 89, TRUE},
738 {10, 90, TRUE},
739 {10, 100, TRUE},
742 pGetComboBoxInfo = (void *)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo");
743 if (!pGetComboBoxInfo)
745 win_skip("GetComboBoxInfo is not available\n");
746 return;
749 for(test = 0; test < sizeof(info_height) / sizeof(info_height[0]); test++)
751 const struct list_size_info *info_test = &info_height[test];
752 int height_item; /* Height of a list item */
753 int height_list; /* Height of the list we got */
754 int expected_count_list;
755 int expected_height_list;
756 int list_height_nonclient;
757 int list_height_calculated;
758 RECT rect_list_client, rect_list_complete;
760 hCombo = CreateWindowA("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|style, 5, 5, 100,
761 info_test->height_combo, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
763 cbInfo.cbSize = sizeof(COMBOBOXINFO);
764 SetLastError(0xdeadbeef);
765 ret = pGetComboBoxInfo(hCombo, &cbInfo);
766 ok(ret, "Failed to get COMBOBOXINFO structure; LastError: %u\n", GetLastError());
768 hList = cbInfo.hwndList;
769 for (i = 0; i < info_test->num_items; i++)
770 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM) wine_test);
772 /* Click on the button to drop down the list */
773 x = cbInfo.rcButton.left + (cbInfo.rcButton.right-cbInfo.rcButton.left)/2;
774 y = cbInfo.rcButton.top + (cbInfo.rcButton.bottom-cbInfo.rcButton.top)/2;
775 ret = SendMessageA(hCombo, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
776 ok(ret, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
777 GetLastError());
778 ok(SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0),
779 "The dropdown list should have appeared after clicking the button.\n");
781 GetClientRect(hList, &rect_list_client);
782 GetWindowRect(hList, &rect_list_complete);
783 height_list = rect_list_client.bottom - rect_list_client.top;
784 height_item = (int)SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0);
786 list_height_nonclient = (rect_list_complete.bottom - rect_list_complete.top)
787 - (rect_list_client.bottom - rect_list_client.top);
789 /* Calculate the expected client size of the listbox popup from the size of the combobox. */
790 list_height_calculated = info_test->height_combo
791 - (cbInfo.rcItem.bottom + COMBO_YBORDERSIZE())
792 - list_height_nonclient
793 - 1;
795 expected_count_list = list_height_calculated / height_item;
796 if(expected_count_list < 0)
797 expected_count_list = 0;
798 expected_count_list = min(expected_count_list, info_test->num_items);
799 expected_height_list = expected_count_list * height_item;
801 todo_wine_if(info_test->todo)
802 ok(expected_height_list == height_list,
803 "Test %d, expected list height to be %d, got %d\n", test, expected_height_list, height_list);
805 DestroyWindow(hCombo);
809 void test_WS_VSCROLL(void)
811 BOOL (WINAPI *pGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
812 HWND hCombo, hList;
813 COMBOBOXINFO info;
814 DWORD style;
815 BOOL ret;
816 int i;
818 pGetComboBoxInfo = (void *)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo");
819 if (!pGetComboBoxInfo)
821 win_skip("GetComboBoxInfo is not available\n");
822 return;
824 info.cbSize = sizeof(info);
825 hCombo = build_combo(CBS_DROPDOWNLIST);
827 SetLastError(0xdeadbeef);
828 ret = pGetComboBoxInfo(hCombo, &info);
829 ok(ret, "Failed to get COMBOBOXINFO structure; LastError: %u\n", GetLastError());
830 hList = info.hwndList;
832 for(i = 0; i < 3; i++)
834 char buffer[2];
835 sprintf(buffer, "%d", i);
836 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)buffer);
839 style = GetWindowLongA(info.hwndList, GWL_STYLE);
840 SetWindowLongA(hList, GWL_STYLE, style | WS_VSCROLL);
842 SendMessageA(hCombo, CB_SHOWDROPDOWN, TRUE, 0);
843 SendMessageA(hCombo, CB_SHOWDROPDOWN, FALSE, 0);
845 style = GetWindowLongA(hList, GWL_STYLE);
846 ok((style & WS_VSCROLL) != 0, "Style does not include WS_VSCROLL\n");
848 DestroyWindow(hCombo);
851 START_TEST(combo)
853 hMainWnd = CreateWindowA("static", "Test", WS_OVERLAPPEDWINDOW, 10, 10, 300, 300, NULL, NULL, NULL, 0);
854 ShowWindow(hMainWnd, SW_SHOW);
856 test_WS_VSCROLL();
857 test_setfont(CBS_DROPDOWN);
858 test_setfont(CBS_DROPDOWNLIST);
859 test_setitemheight(CBS_DROPDOWN);
860 test_setitemheight(CBS_DROPDOWNLIST);
861 test_CBN_SELCHANGE();
862 test_WM_LBUTTONDOWN();
863 test_changesize(CBS_DROPDOWN);
864 test_changesize(CBS_DROPDOWNLIST);
865 test_editselection();
866 test_editselection_focus(CBS_SIMPLE);
867 test_editselection_focus(CBS_DROPDOWN);
868 test_listbox_styles(CBS_SIMPLE);
869 test_listbox_styles(CBS_DROPDOWN);
870 test_listbox_styles(CBS_DROPDOWNLIST);
871 test_listbox_size(CBS_DROPDOWN);
873 DestroyWindow(hMainWnd);