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
25 #define WIN32_LEAN_AND_MEAN
28 #include "wine/test.h"
34 #define expect_eq(expr, value, type, fmt); { type val = expr; ok(val == (value), #expr " expected " #fmt " got " #fmt "\n", (value), val); }
35 #define expect_rect(r, _left, _top, _right, _bottom) ok(r.left == _left && r.top == _top && \
36 r.bottom == _bottom && r.right == _right, "Invalid rect (%d,%d) (%d,%d) vs (%d,%d) (%d,%d)\n", \
37 r.left, r.top, r.right, r.bottom, _left, _top, _right, _bottom);
39 static HWND build_combo(DWORD style)
41 return CreateWindow("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|style, 5, 5, 100, 100, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
44 static int font_height(HFONT hFont)
50 hDC = CreateCompatibleDC(NULL);
51 hFontOld = SelectObject(hDC, hFont);
52 GetTextMetrics(hDC, &tm);
53 SelectObject(hDC, hFontOld);
59 static INT CALLBACK is_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *tm, DWORD type, LPARAM lParam)
64 static int is_font_installed(const char *name)
66 HDC hdc = GetDC(NULL);
67 BOOL ret = !EnumFontFamilies(hdc, name, is_font_installed_proc, 0);
72 static void test_setitemheight(DWORD style)
74 HWND hCombo = build_combo(style);
78 trace("Style %x\n", style);
79 GetClientRect(hCombo, &r);
80 expect_rect(r, 0, 0, 100, 24);
81 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
82 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
83 todo_wine expect_rect(r, 5, 5, 105, 105);
85 for (i = 1; i < 30; i++)
87 SendMessage(hCombo, CB_SETITEMHEIGHT, -1, i);
88 GetClientRect(hCombo, &r);
89 expect_eq(r.bottom - r.top, i + 6, int, "%d");
92 DestroyWindow(hCombo);
95 static void test_setfont(DWORD style)
97 HWND hCombo = build_combo(style);
98 HFONT hFont1 = CreateFont(10, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
99 HFONT hFont2 = CreateFont(8, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
103 trace("Style %x\n", style);
104 GetClientRect(hCombo, &r);
105 expect_rect(r, 0, 0, 100, 24);
106 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
107 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
108 todo_wine expect_rect(r, 5, 5, 105, 105);
110 if (!is_font_installed("Marlett"))
112 skip("Marlett font not available\n");
113 DestroyWindow(hCombo);
114 DeleteObject(hFont1);
115 DeleteObject(hFont2);
119 if (font_height(hFont1) == 10 && font_height(hFont2) == 8)
121 SendMessage(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
122 GetClientRect(hCombo, &r);
123 expect_rect(r, 0, 0, 100, 18);
124 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
125 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
126 todo_wine expect_rect(r, 5, 5, 105, 99);
128 SendMessage(hCombo, WM_SETFONT, (WPARAM)hFont2, FALSE);
129 GetClientRect(hCombo, &r);
130 expect_rect(r, 0, 0, 100, 16);
131 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
132 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
133 todo_wine expect_rect(r, 5, 5, 105, 97);
135 SendMessage(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
136 GetClientRect(hCombo, &r);
137 expect_rect(r, 0, 0, 100, 18);
138 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
139 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
140 todo_wine expect_rect(r, 5, 5, 105, 99);
143 skip("Invalid Marlett font heights\n");
145 for (i = 1; i < 30; i++)
147 HFONT hFont = CreateFont(i, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
148 int height = font_height(hFont);
150 SendMessage(hCombo, WM_SETFONT, (WPARAM)hFont, FALSE);
151 GetClientRect(hCombo, &r);
152 expect_eq(r.bottom - r.top, height + 8, int, "%d");
153 SendMessage(hCombo, WM_SETFONT, 0, FALSE);
157 DestroyWindow(hCombo);
158 DeleteObject(hFont1);
159 DeleteObject(hFont2);
162 static LRESULT (CALLBACK *old_parent_proc)(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
163 static LPCSTR expected_edit_text;
164 static LPCSTR expected_list_text;
165 static BOOL selchange_fired;
167 static LRESULT CALLBACK parent_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
174 case MAKEWPARAM(COMBO_ID, CBN_SELCHANGE):
176 HWND hCombo = (HWND)lparam;
178 char list[20], edit[20];
180 memset(list, 0, sizeof(list));
181 memset(edit, 0, sizeof(edit));
183 idx = SendMessage(hCombo, CB_GETCURSEL, 0, 0);
184 SendMessage(hCombo, CB_GETLBTEXT, idx, (LPARAM)list);
185 SendMessage(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
187 ok(!strcmp(edit, expected_edit_text), "edit: got %s, expected %s\n",
188 edit, expected_edit_text);
189 ok(!strcmp(list, expected_list_text), "list: got %s, expected %s\n",
190 list, expected_list_text);
192 selchange_fired = TRUE;
199 return CallWindowProc(old_parent_proc, hwnd, msg, wparam, lparam);
202 static void test_selection(DWORD style, const char * const text[],
203 const int *edit, const int *list)
208 hCombo = build_combo(style);
210 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)text[0]);
211 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)text[1]);
212 SendMessage(hCombo, CB_SETCURSEL, -1, 0);
214 old_parent_proc = (void *)SetWindowLongPtr(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)parent_wnd_proc);
216 idx = SendMessage(hCombo, CB_GETCURSEL, 0, 0);
217 ok(idx == -1, "expected selection -1, got %d\n", idx);
219 /* keyboard navigation */
221 expected_list_text = text[list[0]];
222 expected_edit_text = text[edit[0]];
223 selchange_fired = FALSE;
224 SendMessage(hCombo, WM_KEYDOWN, VK_DOWN, 0);
225 ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
227 expected_list_text = text[list[1]];
228 expected_edit_text = text[edit[1]];
229 selchange_fired = FALSE;
230 SendMessage(hCombo, WM_KEYDOWN, VK_DOWN, 0);
231 ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
233 expected_list_text = text[list[2]];
234 expected_edit_text = text[edit[2]];
235 selchange_fired = FALSE;
236 SendMessage(hCombo, WM_KEYDOWN, VK_UP, 0);
237 ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
239 /* programmatic navigation */
241 expected_list_text = text[list[3]];
242 expected_edit_text = text[edit[3]];
243 selchange_fired = FALSE;
244 SendMessage(hCombo, CB_SETCURSEL, list[3], 0);
245 ok(!selchange_fired, "CBN_SELCHANGE sent!\n");
247 expected_list_text = text[list[4]];
248 expected_edit_text = text[edit[4]];
249 selchange_fired = FALSE;
250 SendMessage(hCombo, CB_SETCURSEL, list[4], 0);
251 ok(!selchange_fired, "CBN_SELCHANGE sent!\n");
253 SetWindowLongPtr(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc);
254 DestroyWindow(hCombo);
257 static void test_CBN_SELCHANGE(void)
259 static const char * const text[] = { "alpha", "beta", "" };
260 static const int sel_1[] = { 2, 0, 1, 0, 1 };
261 static const int sel_2[] = { 0, 1, 0, 0, 1 };
263 test_selection(CBS_SIMPLE, text, sel_1, sel_2);
264 test_selection(CBS_DROPDOWN, text, sel_1, sel_2);
265 test_selection(CBS_DROPDOWNLIST, text, sel_2, sel_2);
268 static void test_WM_LBUTTONDOWN(void)
270 HWND hCombo, hEdit, hList;
272 UINT x, y, item_height;
277 static const UINT choices[] = {8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72};
278 static const CHAR stringFormat[] = "%2d";
280 BOOL (WINAPI *pGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
282 pGetComboBoxInfo = (void*)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo");
283 if (!pGetComboBoxInfo){
284 win_skip("GetComboBoxInfo is not available\n");
288 hCombo = CreateWindow("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|CBS_DROPDOWN,
289 0, 0, 200, 150, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
291 for (i = 0; i < sizeof(choices)/sizeof(UINT); i++){
292 sprintf(buffer, stringFormat, choices[i]);
293 result = SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)buffer);
295 "Failed to add item %d\n", i);
298 cbInfo.cbSize = sizeof(COMBOBOXINFO);
299 SetLastError(0xdeadbeef);
300 ret = pGetComboBoxInfo(hCombo, &cbInfo);
301 ok(ret, "Failed to get combobox info structure. LastError=%d\n",
303 hEdit = cbInfo.hwndItem;
304 hList = cbInfo.hwndList;
306 trace("hMainWnd=%p, hCombo=%p, hList=%p, hEdit=%p\n", hMainWnd, hCombo, hList, hEdit);
307 ok(GetFocus() == hMainWnd, "Focus not on Main Window, instead on %p\n", GetFocus());
309 /* Click on the button to drop down the list */
310 x = cbInfo.rcButton.left + (cbInfo.rcButton.right-cbInfo.rcButton.left)/2;
311 y = cbInfo.rcButton.top + (cbInfo.rcButton.bottom-cbInfo.rcButton.top)/2;
312 result = SendMessage(hCombo, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
313 ok(result, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
315 ok(SendMessage(hCombo, CB_GETDROPPEDSTATE, 0, 0),
316 "The dropdown list should have appeared after clicking the button.\n");
318 ok(GetFocus() == hEdit,
319 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
320 result = SendMessage(hCombo, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
321 ok(result, "WM_LBUTTONUP was not processed. LastError=%d\n",
323 ok(GetFocus() == hEdit,
324 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
326 /* Click on the 5th item in the list */
327 item_height = SendMessage(hCombo, CB_GETITEMHEIGHT, 0, 0);
328 ok(GetClientRect(hList, &rect), "Failed to get list's client rect.\n");
329 x = rect.left + (rect.right-rect.left)/2;
330 y = item_height/2 + item_height*4;
331 result = SendMessage(hList, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
332 ok(!result, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
334 ok(GetFocus() == hEdit,
335 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
337 result = SendMessage(hList, WM_MOUSEMOVE, 0, MAKELPARAM(x, y));
338 ok(!result, "WM_MOUSEMOVE was not processed. LastError=%d\n",
340 ok(GetFocus() == hEdit,
341 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
342 ok(SendMessage(hCombo, CB_GETDROPPEDSTATE, 0, 0),
343 "The dropdown list should still be visible.\n");
345 result = SendMessage(hList, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
346 ok(!result, "WM_LBUTTONUP was not processed. LastError=%d\n",
348 ok(GetFocus() == hEdit,
349 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
350 ok(!SendMessage(hCombo, CB_GETDROPPEDSTATE, 0, 0),
351 "The dropdown list should have been rolled up.\n");
352 idx = SendMessage(hCombo, CB_GETCURSEL, 0, 0);
353 ok(idx, "Current Selection: expected %d, got %d\n", 4, idx);
355 DestroyWindow(hCombo);
360 hMainWnd = CreateWindow("static", "Test", WS_OVERLAPPEDWINDOW, 10, 10, 300, 300, NULL, NULL, NULL, 0);
361 ShowWindow(hMainWnd, SW_SHOW);
363 test_setfont(CBS_DROPDOWN);
364 test_setfont(CBS_DROPDOWNLIST);
365 test_setitemheight(CBS_DROPDOWN);
366 test_setitemheight(CBS_DROPDOWNLIST);
367 test_CBN_SELCHANGE();
368 test_WM_LBUTTONDOWN();
370 DestroyWindow(hMainWnd);