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