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