winemac: Use unixlib interface for dragdrop.c calls.
[wine.git] / dlls / user32 / tests / combo.c
blob2fc0d1ad9c0fe0fe18eba0be1de1915613f7f5e4
1 /* Unit test suite for combo boxes.
3 * Copyright 2007 Mikolaj Zalewski
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.
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.
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
20 #include <limits.h>
21 #include <stdarg.h>
22 #include <stdio.h>
24 #define STRICT
25 #define WIN32_LEAN_AND_MEAN
26 #include <windows.h>
28 #include "wine/test.h"
30 #define COMBO_ID 1995
32 #define COMBO_YBORDERSIZE() 2
34 static HWND hMainWnd;
36 #define expect_eq(expr, value, type, fmt); { type val = expr; ok(val == (value), #expr " expected " #fmt " got " #fmt "\n", (value), val); }
37 #define expect_rect(r, _left, _top, _right, _bottom) ok(r.left == _left && r.top == _top && \
38 r.bottom == _bottom && r.right == _right, "Invalid rect %s vs (%d,%d)-(%d,%d)\n", \
39 wine_dbgstr_rect(&r), _left, _top, _right, _bottom);
41 static HWND build_combo(DWORD style)
43 return CreateWindowA("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|style, 5, 5, 100, 100, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
46 static int get_font_height(HFONT hFont)
48 TEXTMETRICA tm;
49 HFONT hFontOld;
50 HDC hDC;
52 hDC = CreateCompatibleDC(NULL);
53 hFontOld = SelectObject(hDC, hFont);
54 GetTextMetricsA(hDC, &tm);
55 SelectObject(hDC, hFontOld);
56 DeleteDC(hDC);
58 return tm.tmHeight;
61 static INT CALLBACK is_font_installed_proc(const LOGFONTA *elf, const TEXTMETRICA *tm, DWORD type, LPARAM lParam)
63 return 0;
66 static BOOL is_font_installed(const char *name)
68 HDC hdc = GetDC(NULL);
69 BOOL ret = !EnumFontFamiliesA(hdc, name, is_font_installed_proc, 0);
70 ReleaseDC(NULL, hdc);
71 return ret;
74 static void test_setitemheight(DWORD style)
76 HWND hCombo = build_combo(style);
77 int i, font_height, height;
78 HFONT hFont;
79 RECT r;
81 trace("Style %lx\n", style);
82 GetClientRect(hCombo, &r);
83 expect_rect(r, 0, 0, 100, get_font_height(GetStockObject(SYSTEM_FONT)) + 8);
84 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
85 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
86 todo_wine expect_rect(r, 5, 5, 105, 105);
88 for (i = 1; i < 30; i++)
90 SendMessageA(hCombo, CB_SETITEMHEIGHT, -1, i);
91 GetClientRect(hCombo, &r);
92 expect_eq(r.bottom - r.top, i + 6, int, "%d");
95 DestroyWindow(hCombo);
97 /* Set item height below text height, force resize. */
98 hCombo = build_combo(style);
100 hFont = (HFONT)SendMessageA(hCombo, WM_GETFONT, 0, 0);
101 font_height = get_font_height(hFont);
102 SendMessageA(hCombo, CB_SETITEMHEIGHT, -1, font_height / 2);
103 height = SendMessageA(hCombo, CB_GETITEMHEIGHT, -1, 0);
104 todo_wine
105 ok(height == font_height / 2, "Unexpected item height %d, expected %d.\n", height, font_height / 2);
107 SetWindowPos(hCombo, NULL, 10, 10, 150, 5 * font_height, SWP_SHOWWINDOW);
108 height = SendMessageA(hCombo, CB_GETITEMHEIGHT, -1, 0);
109 ok(height > font_height, "Unexpected item height %d, font height %d.\n", height, font_height);
111 DestroyWindow(hCombo);
114 static void test_setfont(DWORD style)
116 HWND hCombo;
117 HFONT hFont1, hFont2;
118 RECT r;
119 int i;
121 if (!is_font_installed("Marlett"))
123 skip("Marlett font not available\n");
124 return;
127 trace("Style %lx\n", style);
129 hCombo = build_combo(style);
130 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");
131 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");
133 GetClientRect(hCombo, &r);
134 expect_rect(r, 0, 0, 100, get_font_height(GetStockObject(SYSTEM_FONT)) + 8);
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);
139 /* The size of the dropped control is initially equal to the size
140 of the window when it was created. The size of the calculated
141 dropped area changes only by how much the selection area
142 changes, not by how much the list area changes. */
143 if (get_font_height(hFont1) == 10 && get_font_height(hFont2) == 8)
145 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
146 GetClientRect(hCombo, &r);
147 expect_rect(r, 0, 0, 100, 18);
148 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
149 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
150 todo_wine expect_rect(r, 5, 5, 105, 105 - (get_font_height(GetStockObject(SYSTEM_FONT)) - get_font_height(hFont1)));
152 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont2, FALSE);
153 GetClientRect(hCombo, &r);
154 expect_rect(r, 0, 0, 100, 16);
155 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
156 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
157 todo_wine expect_rect(r, 5, 5, 105, 105 - (get_font_height(GetStockObject(SYSTEM_FONT)) - get_font_height(hFont2)));
159 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
160 GetClientRect(hCombo, &r);
161 expect_rect(r, 0, 0, 100, 18);
162 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
163 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
164 todo_wine expect_rect(r, 5, 5, 105, 105 - (get_font_height(GetStockObject(SYSTEM_FONT)) - get_font_height(hFont1)));
166 else
168 ok(0, "Expected Marlett font heights 10/8, got %d/%d\n",
169 get_font_height(hFont1), get_font_height(hFont2));
172 for (i = 1; i < 30; i++)
174 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");
175 int height = get_font_height(hFont);
177 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont, FALSE);
178 GetClientRect(hCombo, &r);
179 expect_eq(r.bottom - r.top, height + 8, int, "%d");
180 SendMessageA(hCombo, WM_SETFONT, 0, FALSE);
181 DeleteObject(hFont);
184 DestroyWindow(hCombo);
185 DeleteObject(hFont1);
186 DeleteObject(hFont2);
189 static LRESULT (CALLBACK *old_parent_proc)(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
190 static LPCSTR expected_edit_text;
191 static LPCSTR expected_list_text;
192 static BOOL selchange_fired;
193 static HWND lparam_for_WM_CTLCOLOR;
194 static HBRUSH brush_red;
196 static LRESULT CALLBACK parent_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
198 switch (msg)
200 case WM_COMMAND:
201 switch (wparam)
203 case MAKEWPARAM(COMBO_ID, CBN_SELCHANGE):
205 HWND hCombo = (HWND)lparam;
206 int idx;
207 char list[20], edit[20];
209 memset(list, 0, sizeof(list));
210 memset(edit, 0, sizeof(edit));
212 idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0);
213 SendMessageA(hCombo, CB_GETLBTEXT, idx, (LPARAM)list);
214 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
216 ok(!strcmp(edit, expected_edit_text), "edit: got %s, expected %s\n",
217 edit, expected_edit_text);
218 ok(!strcmp(list, expected_list_text), "list: got %s, expected %s\n",
219 list, expected_list_text);
221 selchange_fired = TRUE;
223 break;
225 break;
226 case WM_CTLCOLOR:
227 case WM_CTLCOLORMSGBOX:
228 case WM_CTLCOLOREDIT:
229 case WM_CTLCOLORLISTBOX:
230 case WM_CTLCOLORBTN:
231 case WM_CTLCOLORDLG:
232 case WM_CTLCOLORSCROLLBAR:
233 case WM_CTLCOLORSTATIC:
234 if (lparam_for_WM_CTLCOLOR)
236 ok(lparam_for_WM_CTLCOLOR == (HWND)lparam, "Expected %p, got %p\n", lparam_for_WM_CTLCOLOR, (HWND)lparam);
237 return (LRESULT) brush_red;
239 break;
242 return CallWindowProcA(old_parent_proc, hwnd, msg, wparam, lparam);
245 static void test_selection(DWORD style, const char * const text[],
246 const int *edit, const int *list)
248 INT idx;
249 HWND hCombo;
251 hCombo = build_combo(style);
253 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)text[0]);
254 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)text[1]);
255 SendMessageA(hCombo, CB_SETCURSEL, -1, 0);
257 old_parent_proc = (void *)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)parent_wnd_proc);
259 idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0);
260 ok(idx == -1, "expected selection -1, got %d\n", idx);
262 /* keyboard navigation */
264 expected_list_text = text[list[0]];
265 expected_edit_text = text[edit[0]];
266 selchange_fired = FALSE;
267 SendMessageA(hCombo, WM_KEYDOWN, VK_DOWN, 0);
268 ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
270 expected_list_text = text[list[1]];
271 expected_edit_text = text[edit[1]];
272 selchange_fired = FALSE;
273 SendMessageA(hCombo, WM_KEYDOWN, VK_DOWN, 0);
274 ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
276 expected_list_text = text[list[2]];
277 expected_edit_text = text[edit[2]];
278 selchange_fired = FALSE;
279 SendMessageA(hCombo, WM_KEYDOWN, VK_UP, 0);
280 ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
282 /* programmatic navigation */
284 expected_list_text = text[list[3]];
285 expected_edit_text = text[edit[3]];
286 selchange_fired = FALSE;
287 SendMessageA(hCombo, CB_SETCURSEL, list[3], 0);
288 ok(!selchange_fired, "CBN_SELCHANGE sent!\n");
290 expected_list_text = text[list[4]];
291 expected_edit_text = text[edit[4]];
292 selchange_fired = FALSE;
293 SendMessageA(hCombo, CB_SETCURSEL, list[4], 0);
294 ok(!selchange_fired, "CBN_SELCHANGE sent!\n");
296 SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc);
297 DestroyWindow(hCombo);
300 static void test_CBN_SELCHANGE(void)
302 static const char * const text[] = { "alpha", "beta", "" };
303 static const int sel_1[] = { 2, 0, 1, 0, 1 };
304 static const int sel_2[] = { 0, 1, 0, 0, 1 };
306 test_selection(CBS_SIMPLE, text, sel_1, sel_2);
307 test_selection(CBS_DROPDOWN, text, sel_1, sel_2);
308 test_selection(CBS_DROPDOWNLIST, text, sel_2, sel_2);
311 static void test_WM_LBUTTONDOWN(void)
313 HWND hCombo, hEdit, hList;
314 COMBOBOXINFO cbInfo;
315 UINT x, y, item_height;
316 LRESULT result;
317 int i, idx;
318 RECT rect;
319 CHAR buffer[3];
320 static const UINT choices[] = {8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72};
321 static const CHAR stringFormat[] = "%2d";
322 BOOL ret;
324 hCombo = CreateWindowA("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|CBS_DROPDOWN,
325 0, 0, 200, 150, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
327 for (i = 0; i < ARRAY_SIZE(choices); i++){
328 sprintf(buffer, stringFormat, choices[i]);
329 result = SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)buffer);
330 ok(result == i,
331 "Failed to add item %d\n", i);
334 cbInfo.cbSize = sizeof(COMBOBOXINFO);
335 SetLastError(0xdeadbeef);
336 ret = GetComboBoxInfo(hCombo, &cbInfo);
337 ok(ret, "Failed to get combobox info structure. LastError=%ld\n",
338 GetLastError());
339 hEdit = cbInfo.hwndItem;
340 hList = cbInfo.hwndList;
342 trace("hMainWnd=%p, hCombo=%p, hList=%p, hEdit=%p\n", hMainWnd, hCombo, hList, hEdit);
343 ok(GetFocus() == hMainWnd, "Focus not on Main Window, instead on %p\n", GetFocus());
345 /* Click on the button to drop down the list */
346 x = cbInfo.rcButton.left + (cbInfo.rcButton.right-cbInfo.rcButton.left)/2;
347 y = cbInfo.rcButton.top + (cbInfo.rcButton.bottom-cbInfo.rcButton.top)/2;
348 result = SendMessageA(hCombo, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
349 ok(result, "WM_LBUTTONDOWN was not processed. LastError=%ld\n",
350 GetLastError());
351 ok(SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0),
352 "The dropdown list should have appeared after clicking the button.\n");
354 ok(GetFocus() == hEdit,
355 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
356 result = SendMessageA(hCombo, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
357 ok(result, "WM_LBUTTONUP was not processed. LastError=%ld\n",
358 GetLastError());
359 ok(GetFocus() == hEdit,
360 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
362 /* Click on the 5th item in the list */
363 item_height = SendMessageA(hCombo, CB_GETITEMHEIGHT, 0, 0);
364 ok(GetClientRect(hList, &rect), "Failed to get list's client rect.\n");
365 x = rect.left + (rect.right-rect.left)/2;
366 y = item_height/2 + item_height*4;
367 result = SendMessageA(hList, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
368 ok(!result, "WM_LBUTTONDOWN was not processed. LastError=%ld\n",
369 GetLastError());
370 ok(GetFocus() == hEdit,
371 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
373 result = SendMessageA(hList, WM_MOUSEMOVE, 0, MAKELPARAM(x, y));
374 ok(!result, "WM_MOUSEMOVE was not processed. LastError=%ld\n",
375 GetLastError());
376 ok(GetFocus() == hEdit,
377 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
378 ok(SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0),
379 "The dropdown list should still be visible.\n");
381 result = SendMessageA(hList, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
382 ok(!result, "WM_LBUTTONUP was not processed. LastError=%ld\n",
383 GetLastError());
384 ok(GetFocus() == hEdit,
385 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
386 ok(!SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0),
387 "The dropdown list should have been rolled up.\n");
388 idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0);
389 ok(idx, "Current Selection: expected %d, got %d\n", 4, idx);
391 DestroyWindow(hCombo);
394 static void test_changesize( DWORD style)
396 HWND hCombo = build_combo(style);
397 RECT rc;
398 INT ddheight, clheight, ddwidth, clwidth;
399 /* get initial measurements */
400 GetClientRect( hCombo, &rc);
401 clheight = rc.bottom - rc.top;
402 clwidth = rc.right - rc.left;
403 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
404 ddheight = rc.bottom - rc.top;
405 ddwidth = rc.right - rc.left;
406 /* use MoveWindow to move & resize the combo */
407 /* first make it slightly smaller */
408 MoveWindow( hCombo, 10, 10, clwidth - 2, clheight - 2, TRUE);
409 GetClientRect( hCombo, &rc);
410 ok( rc.right - rc.left == clwidth - 2, "clientrect width is %ld vs %d\n",
411 rc.right - rc.left, clwidth - 2);
412 ok( rc.bottom - rc.top == clheight, "clientrect height is %ld vs %d\n",
413 rc.bottom - rc.top, clheight);
414 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
415 ok( rc.right - rc.left == clwidth - 2, "drop-down rect width is %ld vs %d\n",
416 rc.right - rc.left, clwidth - 2);
417 ok( rc.bottom - rc.top == ddheight, "drop-down rect height is %ld vs %d\n",
418 rc.bottom - rc.top, ddheight);
419 ok( rc.right - rc.left == ddwidth -2, "drop-down rect width is %ld vs %d\n",
420 rc.right - rc.left, ddwidth - 2);
421 /* new cx, cy is slightly bigger than the initial values */
422 MoveWindow( hCombo, 10, 10, clwidth + 2, clheight + 2, TRUE);
423 GetClientRect( hCombo, &rc);
424 ok( rc.right - rc.left == clwidth + 2, "clientrect width is %ld vs %d\n",
425 rc.right - rc.left, clwidth + 2);
426 ok( rc.bottom - rc.top == clheight, "clientrect height is %ld vs %d\n",
427 rc.bottom - rc.top, clheight);
428 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
429 ok( rc.right - rc.left == clwidth + 2, "drop-down rect width is %ld vs %d\n",
430 rc.right - rc.left, clwidth + 2);
431 todo_wine {
432 ok( rc.bottom - rc.top == clheight + 2, "drop-down rect height is %ld vs %d\n",
433 rc.bottom - rc.top, clheight + 2);
436 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, -1, 0);
437 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
438 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
439 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
441 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 0, 0);
442 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
443 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
444 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
446 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, clwidth - 1, 0);
447 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
448 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
449 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
451 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, clwidth << 1, 0);
452 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
453 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
454 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
456 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 0, 0);
457 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
458 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
459 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
461 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 1, 0);
462 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
463 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
464 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
466 DestroyWindow(hCombo);
469 static void test_editselection(void)
471 HWND hCombo;
472 INT start,end;
473 HWND hEdit;
474 COMBOBOXINFO cbInfo;
475 BOOL ret;
476 DWORD len;
477 char edit[20];
479 /* Build a combo */
480 hCombo = build_combo(CBS_SIMPLE);
481 cbInfo.cbSize = sizeof(COMBOBOXINFO);
482 SetLastError(0xdeadbeef);
483 ret = GetComboBoxInfo(hCombo, &cbInfo);
484 ok(ret, "Failed to get combobox info structure. LastError=%ld\n",
485 GetLastError());
486 hEdit = cbInfo.hwndItem;
488 /* Initially combo selection is empty*/
489 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
490 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
491 ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len));
493 /* Set some text, and press a key to replace it */
494 edit[0] = 0x00;
495 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)"Jason1");
496 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
497 ok(strcmp(edit, "Jason1")==0, "Unexpected text retrieved %s\n", edit);
499 /* Now what is the selection - still empty */
500 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end);
501 ok(start==0, "Unexpected start position for selection %d\n", start);
502 ok(end==0, "Unexpected end position for selection %d\n", end);
503 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
504 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
505 ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len));
507 /* Give it focus, and it gets selected */
508 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
509 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end);
510 ok(start==0, "Unexpected start position for selection %d\n", start);
511 ok(end==6, "Unexpected end position for selection %d\n", end);
512 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
513 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
514 ok(HIWORD(len)==6, "Unexpected end position for selection %d\n", HIWORD(len));
516 /* Now emulate a key press */
517 edit[0] = 0x00;
518 SendMessageA(hCombo, WM_CHAR, 'A', 0x1c0001);
519 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
520 ok(strcmp(edit, "A")==0, "Unexpected text retrieved %s\n", edit);
522 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
523 ok(LOWORD(len)==1, "Unexpected start position for selection %d\n", LOWORD(len));
524 ok(HIWORD(len)==1, "Unexpected end position for selection %d\n", HIWORD(len));
526 /* Now what happens when it gets more focus a second time - it doesn't reselect */
527 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
528 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
529 ok(LOWORD(len)==1, "Unexpected start position for selection %d\n", LOWORD(len));
530 ok(HIWORD(len)==1, "Unexpected end position for selection %d\n", HIWORD(len));
531 DestroyWindow(hCombo);
533 /* Start again - Build a combo */
534 hCombo = build_combo(CBS_SIMPLE);
535 cbInfo.cbSize = sizeof(COMBOBOXINFO);
536 SetLastError(0xdeadbeef);
537 ret = GetComboBoxInfo(hCombo, &cbInfo);
538 ok(ret, "Failed to get combobox info structure. LastError=%ld\n",
539 GetLastError());
540 hEdit = cbInfo.hwndItem;
542 /* Set some text and give focus so it gets selected */
543 edit[0] = 0x00;
544 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)"Jason2");
545 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
546 ok(strcmp(edit, "Jason2")==0, "Unexpected text retrieved %s\n", edit);
548 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
550 /* Now what is the selection */
551 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end);
552 ok(start==0, "Unexpected start position for selection %d\n", start);
553 ok(end==6, "Unexpected end position for selection %d\n", end);
554 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
555 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
556 ok(HIWORD(len)==6, "Unexpected end position for selection %d\n", HIWORD(len));
558 /* Now change the selection to the apparently invalid start -1, end -1 and
559 show it means no selection (ie start -1) but cursor at end */
560 SendMessageA(hCombo, CB_SETEDITSEL, 0, -1);
561 edit[0] = 0x00;
562 SendMessageA(hCombo, WM_CHAR, 'A', 0x1c0001);
563 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
564 ok(strcmp(edit, "Jason2A")==0, "Unexpected text retrieved %s\n", edit);
565 DestroyWindow(hCombo);
568 static WNDPROC edit_window_proc;
569 static long setsel_start = 1, setsel_end = 1;
570 static HWND hCBN_SetFocus, hCBN_KillFocus;
572 static LRESULT CALLBACK combobox_subclass_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
574 if (msg == EM_SETSEL)
576 setsel_start = wParam;
577 setsel_end = lParam;
579 return CallWindowProcA(edit_window_proc, hwnd, msg, wParam, lParam);
582 static LRESULT CALLBACK test_window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
584 switch (msg)
586 case WM_COMMAND:
587 switch (HIWORD(wParam))
589 case CBN_SETFOCUS:
590 hCBN_SetFocus = (HWND)lParam;
591 break;
592 case CBN_KILLFOCUS:
593 hCBN_KillFocus = (HWND)lParam;
594 break;
596 break;
597 case WM_NEXTDLGCTL:
598 SetFocus((HWND)wParam);
599 break;
601 return CallWindowProcA(old_parent_proc, hwnd, msg, wParam, lParam);
604 static void test_editselection_focus(DWORD style)
606 HWND hCombo, hEdit, hButton;
607 COMBOBOXINFO cbInfo;
608 BOOL ret;
609 const char wine_test[] = "Wine Test";
610 char buffer[16] = {0};
611 DWORD len;
613 hCombo = build_combo(style);
614 cbInfo.cbSize = sizeof(COMBOBOXINFO);
615 SetLastError(0xdeadbeef);
616 ret = GetComboBoxInfo(hCombo, &cbInfo);
617 ok(ret, "Failed to get COMBOBOXINFO structure; LastError: %lu\n", GetLastError());
618 hEdit = cbInfo.hwndItem;
620 hButton = CreateWindowA("Button", "OK", WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,
621 5, 50, 100, 20, hMainWnd, NULL,
622 (HINSTANCE)GetWindowLongPtrA(hMainWnd, GWLP_HINSTANCE), NULL);
624 old_parent_proc = (WNDPROC)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)test_window_proc);
625 edit_window_proc = (WNDPROC)SetWindowLongPtrA(hEdit, GWLP_WNDPROC, (ULONG_PTR)combobox_subclass_proc);
627 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
628 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
629 todo_wine ok(setsel_end == INT_MAX, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
630 ok(hCBN_SetFocus == hCombo, "Wrong handle set by CBN_SETFOCUS; got %p\n", hCBN_SetFocus);
631 ok(GetFocus() == hEdit, "hEdit should have keyboard focus\n");
633 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hButton, TRUE);
634 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
635 todo_wine ok(setsel_end == 0, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
636 ok(hCBN_KillFocus == hCombo, "Wrong handle set by CBN_KILLFOCUS; got %p\n", hCBN_KillFocus);
637 ok(GetFocus() == hButton, "hButton should have keyboard focus\n");
639 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)wine_test);
640 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hCombo, TRUE);
641 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
642 todo_wine ok(setsel_end == INT_MAX, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
643 ok(hCBN_SetFocus == hCombo, "Wrong handle set by CBN_SETFOCUS; got %p\n", hCBN_SetFocus);
644 ok(GetFocus() == hEdit, "hEdit should have keyboard focus\n");
645 SendMessageA(hCombo, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
646 ok(!strcmp(buffer, wine_test), "Unexpected text in edit control; got '%s'\n", buffer);
648 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hButton, TRUE);
649 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
650 todo_wine ok(setsel_end == 0, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
651 ok(hCBN_KillFocus == hCombo, "Wrong handle set by CBN_KILLFOCUS; got %p\n", hCBN_KillFocus);
652 ok(GetFocus() == hButton, "hButton should have keyboard focus\n");
653 len = SendMessageA(hCombo, CB_GETEDITSEL, 0, 0);
654 ok(len == 0, "Unexpected text selection; start: %u, end: %u\n", LOWORD(len), HIWORD(len));
656 SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc);
657 DestroyWindow(hButton);
658 DestroyWindow(hCombo);
661 static void test_listbox_styles(DWORD cb_style)
663 HWND combo;
664 COMBOBOXINFO info;
665 DWORD style, exstyle, expect_style, expect_exstyle;
666 BOOL ret;
668 expect_style = WS_CHILD|WS_CLIPSIBLINGS|LBS_COMBOBOX|LBS_HASSTRINGS|LBS_NOTIFY;
669 if (cb_style == CBS_SIMPLE)
671 expect_style |= WS_VISIBLE;
672 expect_exstyle = WS_EX_CLIENTEDGE;
674 else
676 expect_style |= WS_BORDER;
677 expect_exstyle = WS_EX_TOOLWINDOW;
680 combo = build_combo(cb_style);
681 info.cbSize = sizeof(COMBOBOXINFO);
682 SetLastError(0xdeadbeef);
683 ret = GetComboBoxInfo(combo, &info);
684 ok(ret, "Failed to get combobox info structure.\n");
686 style = GetWindowLongW( info.hwndList, GWL_STYLE );
687 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE );
688 ok(style == expect_style, "%08lx: got %08lx\n", cb_style, style);
689 ok(exstyle == expect_exstyle, "%08lx: got %08lx\n", cb_style, exstyle);
691 if (cb_style != CBS_SIMPLE)
692 expect_exstyle |= WS_EX_TOPMOST;
694 SendMessageW(combo, CB_SHOWDROPDOWN, TRUE, 0 );
695 style = GetWindowLongW( info.hwndList, GWL_STYLE );
696 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE );
697 ok(style == (expect_style | WS_VISIBLE), "%08lx: got %08lx\n", cb_style, style);
698 ok(exstyle == expect_exstyle, "%08lx: got %08lx\n", cb_style, exstyle);
700 SendMessageW(combo, CB_SHOWDROPDOWN, FALSE, 0 );
701 style = GetWindowLongW( info.hwndList, GWL_STYLE );
702 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE );
703 ok(style == expect_style, "%08lx: got %08lx\n", cb_style, style);
704 ok(exstyle == expect_exstyle, "%08lx: got %08lx\n", cb_style, exstyle);
706 DestroyWindow(combo);
709 static void test_listbox_size(DWORD style)
711 HWND hCombo, hList;
712 COMBOBOXINFO cbInfo;
713 UINT x, y;
714 BOOL ret;
715 int i, test;
716 const char wine_test[] = "Wine Test";
718 static const struct list_size_info
720 int num_items;
721 int height_combo;
722 BOOL todo;
723 } info_height[] = {
724 {2, 24, FALSE},
725 {2, 41, TRUE},
726 {2, 42, FALSE},
727 {2, 50, FALSE},
728 {2, 60},
729 {2, 80},
730 {2, 89},
731 {2, 90},
732 {2, 100},
734 {10, 24, FALSE},
735 {10, 41, TRUE},
736 {10, 42, FALSE},
737 {10, 50, FALSE},
738 {10, 60, FALSE},
739 {10, 80, FALSE},
740 {10, 89, TRUE},
741 {10, 90, FALSE},
742 {10, 100, FALSE},
745 for(test = 0; test < ARRAY_SIZE(info_height); test++)
747 const struct list_size_info *info_test = &info_height[test];
748 int height_item; /* Height of a list item */
749 int height_list; /* Height of the list we got */
750 int expected_count_list;
751 int expected_height_list;
752 int list_height_nonclient;
753 int list_height_calculated;
754 RECT rect_list_client, rect_list_complete;
756 hCombo = CreateWindowA("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|style, 5, 5, 100,
757 info_test->height_combo, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
759 cbInfo.cbSize = sizeof(COMBOBOXINFO);
760 SetLastError(0xdeadbeef);
761 ret = GetComboBoxInfo(hCombo, &cbInfo);
762 ok(ret, "Failed to get COMBOBOXINFO structure; LastError: %lu\n", GetLastError());
764 hList = cbInfo.hwndList;
765 for (i = 0; i < info_test->num_items; i++)
766 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM) wine_test);
768 /* Click on the button to drop down the list */
769 x = cbInfo.rcButton.left + (cbInfo.rcButton.right-cbInfo.rcButton.left)/2;
770 y = cbInfo.rcButton.top + (cbInfo.rcButton.bottom-cbInfo.rcButton.top)/2;
771 ret = SendMessageA(hCombo, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
772 ok(ret, "WM_LBUTTONDOWN was not processed. LastError=%ld\n",
773 GetLastError());
774 ok(SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0),
775 "The dropdown list should have appeared after clicking the button.\n");
777 GetClientRect(hList, &rect_list_client);
778 GetWindowRect(hList, &rect_list_complete);
779 height_list = rect_list_client.bottom - rect_list_client.top;
780 height_item = (int)SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0);
782 list_height_nonclient = (rect_list_complete.bottom - rect_list_complete.top)
783 - (rect_list_client.bottom - rect_list_client.top);
785 /* Calculate the expected client size of the listbox popup from the size of the combobox. */
786 list_height_calculated = info_test->height_combo
787 - (cbInfo.rcItem.bottom + COMBO_YBORDERSIZE())
788 - list_height_nonclient
789 - 1;
791 expected_count_list = list_height_calculated / height_item;
792 if(expected_count_list < 0)
793 expected_count_list = 0;
794 expected_count_list = min(expected_count_list, info_test->num_items);
795 expected_height_list = expected_count_list * height_item;
797 todo_wine_if(info_test->todo)
798 ok(expected_height_list == height_list,
799 "Test %d, expected list height to be %d, got %d\n", test, expected_height_list, height_list);
801 DestroyWindow(hCombo);
805 static void test_WS_VSCROLL(void)
807 HWND hCombo, hList;
808 COMBOBOXINFO info;
809 DWORD style;
810 BOOL ret;
811 int i;
813 info.cbSize = sizeof(info);
814 hCombo = build_combo(CBS_DROPDOWNLIST);
816 SetLastError(0xdeadbeef);
817 ret = GetComboBoxInfo(hCombo, &info);
818 ok(ret, "Failed to get COMBOBOXINFO structure; LastError: %lu\n", GetLastError());
819 hList = info.hwndList;
821 for(i = 0; i < 3; i++)
823 char buffer[2];
824 sprintf(buffer, "%d", i);
825 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)buffer);
828 style = GetWindowLongA(info.hwndList, GWL_STYLE);
829 SetWindowLongA(hList, GWL_STYLE, style | WS_VSCROLL);
831 SendMessageA(hCombo, CB_SHOWDROPDOWN, TRUE, 0);
832 SendMessageA(hCombo, CB_SHOWDROPDOWN, FALSE, 0);
834 style = GetWindowLongA(hList, GWL_STYLE);
835 ok((style & WS_VSCROLL) != 0, "Style does not include WS_VSCROLL\n");
837 DestroyWindow(hCombo);
840 static void test_combo_ctlcolor(void)
842 static const int messages[] =
844 WM_CTLCOLOR,
845 WM_CTLCOLORMSGBOX,
846 WM_CTLCOLOREDIT,
847 WM_CTLCOLORLISTBOX,
848 WM_CTLCOLORBTN,
849 WM_CTLCOLORDLG,
850 WM_CTLCOLORSCROLLBAR,
851 WM_CTLCOLORSTATIC,
854 HBRUSH brush, global_brush;
855 unsigned int i, ret;
856 COMBOBOXINFO info;
857 HWND combo;
859 combo = build_combo(CBS_DROPDOWN);
860 ok(!!combo, "Failed to create combo window.\n");
862 old_parent_proc = (void *)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)parent_wnd_proc);
864 info.cbSize = sizeof(COMBOBOXINFO);
865 ret = GetComboBoxInfo(combo, &info);
866 ok(ret, "Failed to get combobox info structure.\n");
868 lparam_for_WM_CTLCOLOR = info.hwndItem;
870 /* Parent returns valid brush handle. */
871 for (i = 0; i < ARRAY_SIZE(messages); ++i)
873 brush = (HBRUSH)SendMessageA(combo, messages[i], 0, (LPARAM)info.hwndItem);
874 ok(brush == brush_red, "%u: unexpected brush %p, expected got %p.\n", i, brush, brush_red);
877 /* Parent returns NULL brush. */
878 global_brush = brush_red;
879 brush_red = NULL;
881 for (i = 0; i < ARRAY_SIZE(messages); ++i)
883 brush = (HBRUSH)SendMessageA(combo, messages[i], 0, (LPARAM)info.hwndItem);
884 ok(!brush, "%u: unexpected brush %p.\n", i, brush);
887 brush_red = global_brush;
889 lparam_for_WM_CTLCOLOR = 0;
891 /* Parent does default processing. */
892 for (i = 0; i < ARRAY_SIZE(messages); ++i)
894 brush = (HBRUSH)SendMessageA(combo, messages[i], 0, (LPARAM)info.hwndItem);
895 ok(!!brush && brush != brush_red, "%u: unexpected brush %p.\n", i, brush);
898 SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc);
899 DestroyWindow(combo);
901 /* Combo without a parent. */
902 combo = CreateWindowA("ComboBox", "Combo", CBS_DROPDOWN, 5, 5, 100, 100, NULL, NULL, NULL, 0);
903 ok(!!combo, "Failed to create combo window.\n");
905 info.cbSize = sizeof(COMBOBOXINFO);
906 ret = GetComboBoxInfo(combo, &info);
907 ok(ret, "Failed to get combobox info structure.\n");
909 for (i = 0; i < ARRAY_SIZE(messages); ++i)
911 brush = (HBRUSH)SendMessageA(combo, messages[i], 0, (LPARAM)info.hwndItem);
912 ok(!!brush && brush != brush_red, "%u: unexpected brush %p.\n", i, brush);
915 DestroyWindow(combo);
918 START_TEST(combo)
920 brush_red = CreateSolidBrush(RGB(255, 0, 0));
921 hMainWnd = CreateWindowA("static", "Test", WS_OVERLAPPEDWINDOW, 10, 10, 300, 300, NULL, NULL, NULL, 0);
922 ShowWindow(hMainWnd, SW_SHOW);
924 test_WS_VSCROLL();
925 test_setfont(CBS_DROPDOWN);
926 test_setfont(CBS_DROPDOWNLIST);
927 test_setitemheight(CBS_DROPDOWN);
928 test_setitemheight(CBS_DROPDOWNLIST);
929 test_CBN_SELCHANGE();
930 test_WM_LBUTTONDOWN();
931 test_changesize(CBS_DROPDOWN);
932 test_changesize(CBS_DROPDOWNLIST);
933 test_editselection();
934 test_editselection_focus(CBS_SIMPLE);
935 test_editselection_focus(CBS_DROPDOWN);
936 test_listbox_styles(CBS_SIMPLE);
937 test_listbox_styles(CBS_DROPDOWN);
938 test_listbox_styles(CBS_DROPDOWNLIST);
939 test_listbox_size(CBS_DROPDOWN);
940 test_combo_ctlcolor();
942 DestroyWindow(hMainWnd);
943 DeleteObject(brush_red);