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