[USER32_WINETEST] Sync with Wine Staging 1.9.4. CORE-10912
[reactos.git] / rostests / winetests / user32 / combo.c
1 /* Unit test suite for combo boxes.
2 *
3 * Copyright 2007 Mikolaj Zalewski
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20 #include <stdarg.h>
21 #include <stdio.h>
22
23 #define STRICT
24 #define WIN32_LEAN_AND_MEAN
25 #include <windows.h>
26
27 #include "wine/test.h"
28
29 #define COMBO_ID 1995
30
31 static HWND hMainWnd;
32
33 #define expect_eq(expr, value, type, fmt); { type val = expr; ok(val == (value), #expr " expected " #fmt " got " #fmt "\n", (value), val); }
34 #define expect_rect(r, _left, _top, _right, _bottom) ok(r.left == _left && r.top == _top && \
35 r.bottom == _bottom && r.right == _right, "Invalid rect (%d,%d) (%d,%d) vs (%d,%d) (%d,%d)\n", \
36 r.left, r.top, r.right, r.bottom, _left, _top, _right, _bottom);
37
38 static HWND build_combo(DWORD style)
39 {
40 return CreateWindowA("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|style, 5, 5, 100, 100, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
41 }
42
43 static int font_height(HFONT hFont)
44 {
45 TEXTMETRICA tm;
46 HFONT hFontOld;
47 HDC hDC;
48
49 hDC = CreateCompatibleDC(NULL);
50 hFontOld = SelectObject(hDC, hFont);
51 GetTextMetricsA(hDC, &tm);
52 SelectObject(hDC, hFontOld);
53 DeleteDC(hDC);
54
55 return tm.tmHeight;
56 }
57
58 static INT CALLBACK is_font_installed_proc(const LOGFONTA *elf, const TEXTMETRICA *tm, DWORD type, LPARAM lParam)
59 {
60 return 0;
61 }
62
63 static BOOL is_font_installed(const char *name)
64 {
65 HDC hdc = GetDC(NULL);
66 BOOL ret = !EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0);
67 ReleaseDC(NULL, hdc);
68 return ret;
69 }
70
71 static void test_setitemheight(DWORD style)
72 {
73 HWND hCombo = build_combo(style);
74 RECT r;
75 int i;
76
77 trace("Style %x\n", style);
78 GetClientRect(hCombo, &r);
79 expect_rect(r, 0, 0, 100, font_height(GetStockObject(SYSTEM_FONT)) + 8);
80 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
81 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
82 todo_wine expect_rect(r, 5, 5, 105, 105);
83
84 for (i = 1; i < 30; i++)
85 {
86 SendMessageA(hCombo, CB_SETITEMHEIGHT, -1, i);
87 GetClientRect(hCombo, &r);
88 expect_eq(r.bottom - r.top, i + 6, int, "%d");
89 }
90
91 DestroyWindow(hCombo);
92 }
93
94 static void test_setfont(DWORD style)
95 {
96 HWND hCombo;
97 HFONT hFont1, hFont2;
98 RECT r;
99 int i;
100
101 if (!is_font_installed("Marlett"))
102 {
103 skip("Marlett font not available\n");
104 return;
105 }
106
107 trace("Style %x\n", style);
108
109 hCombo = build_combo(style);
110 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");
111 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");
112
113 GetClientRect(hCombo, &r);
114 expect_rect(r, 0, 0, 100, font_height(GetStockObject(SYSTEM_FONT)) + 8);
115 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
116 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
117 todo_wine expect_rect(r, 5, 5, 105, 105);
118
119 /* The size of the dropped control is initially equal to the size
120 of the window when it was created. The size of the calculated
121 dropped area changes only by how much the selection area
122 changes, not by how much the list area changes. */
123 if (font_height(hFont1) == 10 && font_height(hFont2) == 8)
124 {
125 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
126 GetClientRect(hCombo, &r);
127 expect_rect(r, 0, 0, 100, 18);
128 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
129 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
130 todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont1)));
131
132 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont2, FALSE);
133 GetClientRect(hCombo, &r);
134 expect_rect(r, 0, 0, 100, 16);
135 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
136 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
137 todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont2)));
138
139 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
140 GetClientRect(hCombo, &r);
141 expect_rect(r, 0, 0, 100, 18);
142 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
143 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
144 todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont1)));
145 }
146 else
147 {
148 ok(0, "Expected Marlett font heights 10/8, got %d/%d\n",
149 font_height(hFont1), font_height(hFont2));
150 }
151
152 for (i = 1; i < 30; i++)
153 {
154 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");
155 int height = font_height(hFont);
156
157 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont, FALSE);
158 GetClientRect(hCombo, &r);
159 expect_eq(r.bottom - r.top, height + 8, int, "%d");
160 SendMessageA(hCombo, WM_SETFONT, 0, FALSE);
161 DeleteObject(hFont);
162 }
163
164 DestroyWindow(hCombo);
165 DeleteObject(hFont1);
166 DeleteObject(hFont2);
167 }
168
169 static LRESULT (CALLBACK *old_parent_proc)(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
170 static LPCSTR expected_edit_text;
171 static LPCSTR expected_list_text;
172 static BOOL selchange_fired;
173
174 static LRESULT CALLBACK parent_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
175 {
176 switch (msg)
177 {
178 case WM_COMMAND:
179 switch (wparam)
180 {
181 case MAKEWPARAM(COMBO_ID, CBN_SELCHANGE):
182 {
183 HWND hCombo = (HWND)lparam;
184 int idx;
185 char list[20], edit[20];
186
187 memset(list, 0, sizeof(list));
188 memset(edit, 0, sizeof(edit));
189
190 idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0);
191 SendMessageA(hCombo, CB_GETLBTEXT, idx, (LPARAM)list);
192 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
193
194 ok(!strcmp(edit, expected_edit_text), "edit: got %s, expected %s\n",
195 edit, expected_edit_text);
196 ok(!strcmp(list, expected_list_text), "list: got %s, expected %s\n",
197 list, expected_list_text);
198
199 selchange_fired = TRUE;
200 }
201 break;
202 }
203 break;
204 }
205
206 return CallWindowProcA(old_parent_proc, hwnd, msg, wparam, lparam);
207 }
208
209 static void test_selection(DWORD style, const char * const text[],
210 const int *edit, const int *list)
211 {
212 INT idx;
213 HWND hCombo;
214
215 hCombo = build_combo(style);
216
217 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)text[0]);
218 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)text[1]);
219 SendMessageA(hCombo, CB_SETCURSEL, -1, 0);
220
221 old_parent_proc = (void *)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)parent_wnd_proc);
222
223 idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0);
224 ok(idx == -1, "expected selection -1, got %d\n", idx);
225
226 /* keyboard navigation */
227
228 expected_list_text = text[list[0]];
229 expected_edit_text = text[edit[0]];
230 selchange_fired = FALSE;
231 SendMessageA(hCombo, WM_KEYDOWN, VK_DOWN, 0);
232 ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
233
234 expected_list_text = text[list[1]];
235 expected_edit_text = text[edit[1]];
236 selchange_fired = FALSE;
237 SendMessageA(hCombo, WM_KEYDOWN, VK_DOWN, 0);
238 ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
239
240 expected_list_text = text[list[2]];
241 expected_edit_text = text[edit[2]];
242 selchange_fired = FALSE;
243 SendMessageA(hCombo, WM_KEYDOWN, VK_UP, 0);
244 ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
245
246 /* programmatic navigation */
247
248 expected_list_text = text[list[3]];
249 expected_edit_text = text[edit[3]];
250 selchange_fired = FALSE;
251 SendMessageA(hCombo, CB_SETCURSEL, list[3], 0);
252 ok(!selchange_fired, "CBN_SELCHANGE sent!\n");
253
254 expected_list_text = text[list[4]];
255 expected_edit_text = text[edit[4]];
256 selchange_fired = FALSE;
257 SendMessageA(hCombo, CB_SETCURSEL, list[4], 0);
258 ok(!selchange_fired, "CBN_SELCHANGE sent!\n");
259
260 SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc);
261 DestroyWindow(hCombo);
262 }
263
264 static void test_CBN_SELCHANGE(void)
265 {
266 static const char * const text[] = { "alpha", "beta", "" };
267 static const int sel_1[] = { 2, 0, 1, 0, 1 };
268 static const int sel_2[] = { 0, 1, 0, 0, 1 };
269
270 test_selection(CBS_SIMPLE, text, sel_1, sel_2);
271 test_selection(CBS_DROPDOWN, text, sel_1, sel_2);
272 test_selection(CBS_DROPDOWNLIST, text, sel_2, sel_2);
273 }
274
275 static void test_WM_LBUTTONDOWN(void)
276 {
277 HWND hCombo, hEdit, hList;
278 COMBOBOXINFO cbInfo;
279 UINT x, y, item_height;
280 LRESULT result;
281 int i, idx;
282 RECT rect;
283 CHAR buffer[3];
284 static const UINT choices[] = {8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72};
285 static const CHAR stringFormat[] = "%2d";
286 BOOL ret;
287 BOOL (WINAPI *pGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
288
289 pGetComboBoxInfo = (void*)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo");
290 if (!pGetComboBoxInfo){
291 win_skip("GetComboBoxInfo is not available\n");
292 return;
293 }
294
295 hCombo = CreateWindowA("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|CBS_DROPDOWN,
296 0, 0, 200, 150, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
297
298 for (i = 0; i < sizeof(choices)/sizeof(UINT); i++){
299 sprintf(buffer, stringFormat, choices[i]);
300 result = SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)buffer);
301 ok(result == i,
302 "Failed to add item %d\n", i);
303 }
304
305 cbInfo.cbSize = sizeof(COMBOBOXINFO);
306 SetLastError(0xdeadbeef);
307 ret = pGetComboBoxInfo(hCombo, &cbInfo);
308 ok(ret, "Failed to get combobox info structure. LastError=%d\n",
309 GetLastError());
310 hEdit = cbInfo.hwndItem;
311 hList = cbInfo.hwndList;
312
313 trace("hMainWnd=%p, hCombo=%p, hList=%p, hEdit=%p\n", hMainWnd, hCombo, hList, hEdit);
314 ok(GetFocus() == hMainWnd, "Focus not on Main Window, instead on %p\n", GetFocus());
315
316 /* Click on the button to drop down the list */
317 x = cbInfo.rcButton.left + (cbInfo.rcButton.right-cbInfo.rcButton.left)/2;
318 y = cbInfo.rcButton.top + (cbInfo.rcButton.bottom-cbInfo.rcButton.top)/2;
319 result = SendMessageA(hCombo, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
320 ok(result, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
321 GetLastError());
322 ok(SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0),
323 "The dropdown list should have appeared after clicking the button.\n");
324
325 ok(GetFocus() == hEdit,
326 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
327 result = SendMessageA(hCombo, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
328 ok(result, "WM_LBUTTONUP was not processed. LastError=%d\n",
329 GetLastError());
330 ok(GetFocus() == hEdit,
331 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
332
333 /* Click on the 5th item in the list */
334 item_height = SendMessageA(hCombo, CB_GETITEMHEIGHT, 0, 0);
335 ok(GetClientRect(hList, &rect), "Failed to get list's client rect.\n");
336 x = rect.left + (rect.right-rect.left)/2;
337 y = item_height/2 + item_height*4;
338 result = SendMessageA(hList, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
339 ok(!result, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
340 GetLastError());
341 ok(GetFocus() == hEdit,
342 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
343
344 result = SendMessageA(hList, WM_MOUSEMOVE, 0, MAKELPARAM(x, y));
345 ok(!result, "WM_MOUSEMOVE was not processed. LastError=%d\n",
346 GetLastError());
347 ok(GetFocus() == hEdit,
348 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
349 ok(SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0),
350 "The dropdown list should still be visible.\n");
351
352 result = SendMessageA(hList, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
353 ok(!result, "WM_LBUTTONUP was not processed. LastError=%d\n",
354 GetLastError());
355 ok(GetFocus() == hEdit,
356 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
357 ok(!SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0),
358 "The dropdown list should have been rolled up.\n");
359 idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0);
360 ok(idx, "Current Selection: expected %d, got %d\n", 4, idx);
361
362 DestroyWindow(hCombo);
363 }
364
365 static void test_changesize( DWORD style)
366 {
367 HWND hCombo = build_combo(style);
368 RECT rc;
369 INT ddheight, clheight, ddwidth, clwidth;
370 /* get initial measurements */
371 GetClientRect( hCombo, &rc);
372 clheight = rc.bottom - rc.top;
373 clwidth = rc.right - rc.left;
374 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
375 ddheight = rc.bottom - rc.top;
376 ddwidth = rc.right - rc.left;
377 /* use MoveWindow to move & resize the combo */
378 /* first make it slightly smaller */
379 MoveWindow( hCombo, 10, 10, clwidth - 2, clheight - 2, TRUE);
380 GetClientRect( hCombo, &rc);
381 ok( rc.right - rc.left == clwidth - 2, "clientrect width is %d vs %d\n",
382 rc.right - rc.left, clwidth - 2);
383 ok( rc.bottom - rc.top == clheight, "clientrect height is %d vs %d\n",
384 rc.bottom - rc.top, clheight);
385 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
386 ok( rc.right - rc.left == clwidth - 2, "drop-down rect width is %d vs %d\n",
387 rc.right - rc.left, clwidth - 2);
388 ok( rc.bottom - rc.top == ddheight, "drop-down rect height is %d vs %d\n",
389 rc.bottom - rc.top, ddheight);
390 ok( rc.right - rc.left == ddwidth -2, "drop-down rect width is %d vs %d\n",
391 rc.right - rc.left, ddwidth - 2);
392 /* new cx, cy is slightly bigger than the initial values */
393 MoveWindow( hCombo, 10, 10, clwidth + 2, clheight + 2, TRUE);
394 GetClientRect( hCombo, &rc);
395 ok( rc.right - rc.left == clwidth + 2, "clientrect width is %d vs %d\n",
396 rc.right - rc.left, clwidth + 2);
397 ok( rc.bottom - rc.top == clheight, "clientrect height is %d vs %d\n",
398 rc.bottom - rc.top, clheight);
399 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
400 ok( rc.right - rc.left == clwidth + 2, "drop-down rect width is %d vs %d\n",
401 rc.right - rc.left, clwidth + 2);
402 todo_wine {
403 ok( rc.bottom - rc.top == clheight + 2, "drop-down rect height is %d vs %d\n",
404 rc.bottom - rc.top, clheight + 2);
405 }
406
407 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, -1, 0);
408 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
409 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
410 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
411
412 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 0, 0);
413 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
414 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
415 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
416
417 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, clwidth - 1, 0);
418 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
419 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
420 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
421
422 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, clwidth << 1, 0);
423 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
424 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
425 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
426
427 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 0, 0);
428 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
429 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
430 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
431
432 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 1, 0);
433 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
434 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
435 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
436
437 DestroyWindow(hCombo);
438 }
439
440 static void test_editselection(void)
441 {
442 HWND hCombo;
443 INT start,end;
444 HWND hEdit;
445 COMBOBOXINFO cbInfo;
446 BOOL ret;
447 DWORD len;
448 BOOL (WINAPI *pGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
449 char edit[20];
450
451 pGetComboBoxInfo = (void*)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo");
452 if (!pGetComboBoxInfo){
453 win_skip("GetComboBoxInfo is not available\n");
454 return;
455 }
456
457 /* Build a combo */
458 hCombo = build_combo(CBS_SIMPLE);
459 cbInfo.cbSize = sizeof(COMBOBOXINFO);
460 SetLastError(0xdeadbeef);
461 ret = pGetComboBoxInfo(hCombo, &cbInfo);
462 ok(ret, "Failed to get combobox info structure. LastError=%d\n",
463 GetLastError());
464 hEdit = cbInfo.hwndItem;
465
466 /* Initially combo selection is empty*/
467 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
468 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
469 ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len));
470
471 /* Set some text, and press a key to replace it */
472 edit[0] = 0x00;
473 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)"Jason1");
474 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
475 ok(strcmp(edit, "Jason1")==0, "Unexpected text retrieved %s\n", edit);
476
477 /* Now what is the selection - still empty */
478 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end);
479 ok(start==0, "Unexpected start position for selection %d\n", start);
480 ok(end==0, "Unexpected end position for selection %d\n", end);
481 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
482 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
483 ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len));
484
485 /* Give it focus, and it gets selected */
486 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
487 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end);
488 ok(start==0, "Unexpected start position for selection %d\n", start);
489 ok(end==6, "Unexpected end position for selection %d\n", end);
490 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
491 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
492 ok(HIWORD(len)==6, "Unexpected end position for selection %d\n", HIWORD(len));
493
494 /* Now emulate a key press */
495 edit[0] = 0x00;
496 SendMessageA(hCombo, WM_CHAR, 'A', 0x1c0001);
497 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
498 ok(strcmp(edit, "A")==0, "Unexpected text retrieved %s\n", edit);
499
500 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
501 ok(LOWORD(len)==1, "Unexpected start position for selection %d\n", LOWORD(len));
502 ok(HIWORD(len)==1, "Unexpected end position for selection %d\n", HIWORD(len));
503
504 /* Now what happens when it gets more focus a second time - it doesn't reselect */
505 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
506 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
507 ok(LOWORD(len)==1, "Unexpected start position for selection %d\n", LOWORD(len));
508 ok(HIWORD(len)==1, "Unexpected end position for selection %d\n", HIWORD(len));
509 DestroyWindow(hCombo);
510
511 /* Start again - Build a combo */
512 hCombo = build_combo(CBS_SIMPLE);
513 cbInfo.cbSize = sizeof(COMBOBOXINFO);
514 SetLastError(0xdeadbeef);
515 ret = pGetComboBoxInfo(hCombo, &cbInfo);
516 ok(ret, "Failed to get combobox info structure. LastError=%d\n",
517 GetLastError());
518 hEdit = cbInfo.hwndItem;
519
520 /* Set some text and give focus so it gets selected */
521 edit[0] = 0x00;
522 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)"Jason2");
523 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
524 ok(strcmp(edit, "Jason2")==0, "Unexpected text retrieved %s\n", edit);
525
526 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
527
528 /* Now what is the selection */
529 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end);
530 ok(start==0, "Unexpected start position for selection %d\n", start);
531 ok(end==6, "Unexpected end position for selection %d\n", end);
532 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
533 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
534 ok(HIWORD(len)==6, "Unexpected end position for selection %d\n", HIWORD(len));
535
536 /* Now change the selection to the apparently invalid start -1, end -1 and
537 show it means no selection (ie start -1) but cursor at end */
538 SendMessageA(hCombo, CB_SETEDITSEL, 0, -1);
539 edit[0] = 0x00;
540 SendMessageA(hCombo, WM_CHAR, 'A', 0x1c0001);
541 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
542 ok(strcmp(edit, "Jason2A")==0, "Unexpected text retrieved %s\n", edit);
543 DestroyWindow(hCombo);
544 }
545
546 static WNDPROC edit_window_proc;
547 static long setsel_start = 1, setsel_end = 1;
548 static HWND hCBN_SetFocus, hCBN_KillFocus;
549
550 static LRESULT CALLBACK combobox_subclass_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
551 {
552 if (msg == EM_SETSEL)
553 {
554 setsel_start = wParam;
555 setsel_end = lParam;
556 }
557 return CallWindowProcA(edit_window_proc, hwnd, msg, wParam, lParam);
558 }
559
560 static LRESULT CALLBACK test_window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
561 {
562 switch (msg)
563 {
564 case WM_COMMAND:
565 switch (HIWORD(wParam))
566 {
567 case CBN_SETFOCUS:
568 hCBN_SetFocus = (HWND)lParam;
569 break;
570 case CBN_KILLFOCUS:
571 hCBN_KillFocus = (HWND)lParam;
572 break;
573 }
574 break;
575 case WM_NEXTDLGCTL:
576 SetFocus((HWND)wParam);
577 break;
578 }
579 return CallWindowProcA(old_parent_proc, hwnd, msg, wParam, lParam);
580 }
581
582 static void test_editselection_focus(DWORD style)
583 {
584 BOOL (WINAPI *pGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
585 HWND hCombo, hEdit, hButton;
586 COMBOBOXINFO cbInfo;
587 BOOL ret;
588 const char wine_test[] = "Wine Test";
589 char buffer[16] = {0};
590 DWORD len;
591
592 pGetComboBoxInfo = (void *)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo");
593 if (!pGetComboBoxInfo)
594 {
595 win_skip("GetComboBoxInfo is not available\n");
596 return;
597 }
598
599 hCombo = build_combo(style);
600 cbInfo.cbSize = sizeof(COMBOBOXINFO);
601 SetLastError(0xdeadbeef);
602 ret = pGetComboBoxInfo(hCombo, &cbInfo);
603 ok(ret, "Failed to get COMBOBOXINFO structure; LastError: %u\n", GetLastError());
604 hEdit = cbInfo.hwndItem;
605
606 hButton = CreateWindowA("Button", "OK", WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,
607 5, 50, 100, 20, hMainWnd, NULL,
608 (HINSTANCE)GetWindowLongPtrA(hMainWnd, GWLP_HINSTANCE), NULL);
609
610 old_parent_proc = (WNDPROC)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)test_window_proc);
611 edit_window_proc = (WNDPROC)SetWindowLongPtrA(hEdit, GWLP_WNDPROC, (ULONG_PTR)combobox_subclass_proc);
612
613 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
614 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
615 todo_wine ok(setsel_end == INT_MAX, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
616 ok(hCBN_SetFocus == hCombo, "Wrong handle set by CBN_SETFOCUS; got %p\n", hCBN_SetFocus);
617 ok(GetFocus() == hEdit, "hEdit should have keyboard focus\n");
618
619 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hButton, TRUE);
620 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
621 todo_wine ok(setsel_end == 0, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
622 ok(hCBN_KillFocus == hCombo, "Wrong handle set by CBN_KILLFOCUS; got %p\n", hCBN_KillFocus);
623 ok(GetFocus() == hButton, "hButton should have keyboard focus\n");
624
625 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)wine_test);
626 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hCombo, TRUE);
627 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
628 todo_wine ok(setsel_end == INT_MAX, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
629 ok(hCBN_SetFocus == hCombo, "Wrong handle set by CBN_SETFOCUS; got %p\n", hCBN_SetFocus);
630 ok(GetFocus() == hEdit, "hEdit should have keyboard focus\n");
631 SendMessageA(hCombo, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
632 ok(!strcmp(buffer, wine_test), "Unexpected text in edit control; got '%s'\n", buffer);
633
634 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hButton, TRUE);
635 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
636 todo_wine ok(setsel_end == 0, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
637 ok(hCBN_KillFocus == hCombo, "Wrong handle set by CBN_KILLFOCUS; got %p\n", hCBN_KillFocus);
638 ok(GetFocus() == hButton, "hButton should have keyboard focus\n");
639 len = SendMessageA(hCombo, CB_GETEDITSEL, 0, 0);
640 ok(len == 0, "Unexpected text selection; start: %u, end: %u\n", LOWORD(len), HIWORD(len));
641
642 SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc);
643 DestroyWindow(hButton);
644 DestroyWindow(hCombo);
645 }
646
647 static void test_listbox_styles(DWORD cb_style)
648 {
649 BOOL (WINAPI *pGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
650 HWND combo;
651 COMBOBOXINFO info;
652 DWORD style, exstyle, expect_style, expect_exstyle;
653 BOOL ret;
654
655 pGetComboBoxInfo = (void*)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo");
656 if (!pGetComboBoxInfo){
657 win_skip("GetComboBoxInfo is not available\n");
658 return;
659 }
660
661 expect_style = WS_CHILD|WS_CLIPSIBLINGS|LBS_COMBOBOX|LBS_HASSTRINGS|LBS_NOTIFY;
662 if (cb_style == CBS_SIMPLE)
663 {
664 expect_style |= WS_VISIBLE;
665 expect_exstyle = WS_EX_CLIENTEDGE;
666 }
667 else
668 {
669 expect_style |= WS_BORDER;
670 expect_exstyle = WS_EX_TOOLWINDOW;
671 }
672
673 combo = build_combo(cb_style);
674 info.cbSize = sizeof(COMBOBOXINFO);
675 SetLastError(0xdeadbeef);
676 ret = pGetComboBoxInfo(combo, &info);
677 ok(ret, "Failed to get combobox info structure.\n");
678
679 style = GetWindowLongW( info.hwndList, GWL_STYLE );
680 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE );
681 ok(style == expect_style, "%08x: got %08x\n", cb_style, style);
682 ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle);
683
684 if (cb_style != CBS_SIMPLE)
685 expect_exstyle |= WS_EX_TOPMOST;
686
687 SendMessageW(combo, CB_SHOWDROPDOWN, TRUE, 0 );
688 style = GetWindowLongW( info.hwndList, GWL_STYLE );
689 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE );
690 ok(style == (expect_style | WS_VISIBLE), "%08x: got %08x\n", cb_style, style);
691 ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle);
692
693 SendMessageW(combo, CB_SHOWDROPDOWN, FALSE, 0 );
694 style = GetWindowLongW( info.hwndList, GWL_STYLE );
695 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE );
696 ok(style == expect_style, "%08x: got %08x\n", cb_style, style);
697 ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle);
698
699 DestroyWindow(combo);
700 }
701
702 START_TEST(combo)
703 {
704 hMainWnd = CreateWindowA("static", "Test", WS_OVERLAPPEDWINDOW, 10, 10, 300, 300, NULL, NULL, NULL, 0);
705 ShowWindow(hMainWnd, SW_SHOW);
706
707 test_setfont(CBS_DROPDOWN);
708 test_setfont(CBS_DROPDOWNLIST);
709 test_setitemheight(CBS_DROPDOWN);
710 test_setitemheight(CBS_DROPDOWNLIST);
711 test_CBN_SELCHANGE();
712 test_WM_LBUTTONDOWN();
713 test_changesize(CBS_DROPDOWN);
714 test_changesize(CBS_DROPDOWNLIST);
715 test_editselection();
716 test_editselection_focus(CBS_SIMPLE);
717 test_editselection_focus(CBS_DROPDOWN);
718 test_listbox_styles(CBS_SIMPLE);
719 test_listbox_styles(CBS_DROPDOWN);
720 test_listbox_styles(CBS_DROPDOWNLIST);
721
722 DestroyWindow(hMainWnd);
723 }