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