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