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