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