sync user32 winetest with wine 1.1.28
[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 <assert.h>
21 #include <stdarg.h>
22 #include <stdio.h>
23
24 #define STRICT
25 #define WIN32_LEAN_AND_MEAN
26 #include <windows.h>
27
28 #include "wine/test.h"
29
30 #define COMBO_ID 1995
31
32 static HWND hMainWnd;
33
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);
38
39 static HWND build_combo(DWORD style)
40 {
41 return CreateWindow("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|style, 5, 5, 100, 100, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
42 }
43
44 static int font_height(HFONT hFont)
45 {
46 TEXTMETRIC tm;
47 HFONT hFontOld;
48 HDC hDC;
49
50 hDC = CreateCompatibleDC(NULL);
51 hFontOld = SelectObject(hDC, hFont);
52 GetTextMetrics(hDC, &tm);
53 SelectObject(hDC, hFontOld);
54 DeleteDC(hDC);
55
56 return tm.tmHeight;
57 }
58
59 static INT CALLBACK is_font_installed_proc(const LOGFONT *elf, const TEXTMETRIC *tm, DWORD type, LPARAM lParam)
60 {
61 return 0;
62 }
63
64 static int is_font_installed(const char *name)
65 {
66 HDC hdc = GetDC(NULL);
67 BOOL ret = !EnumFontFamilies(hdc, name, is_font_installed_proc, 0);
68 ReleaseDC(NULL, hdc);
69 return ret;
70 }
71
72 static void test_setitemheight(DWORD style)
73 {
74 HWND hCombo = build_combo(style);
75 RECT r;
76 int i;
77
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);
84
85 for (i = 1; i < 30; i++)
86 {
87 SendMessage(hCombo, CB_SETITEMHEIGHT, -1, i);
88 GetClientRect(hCombo, &r);
89 expect_eq(r.bottom - r.top, i + 6, int, "%d");
90 }
91
92 DestroyWindow(hCombo);
93 }
94
95 static void test_setfont(DWORD style)
96 {
97 HWND hCombo;
98 HFONT hFont1, hFont2;
99 RECT r;
100 int i;
101
102 if (!is_font_installed("Marlett"))
103 {
104 skip("Marlett font not available\n");
105 return;
106 }
107
108 trace("Style %x\n", style);
109
110 hCombo = build_combo(style);
111 hFont1 = CreateFont(10, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
112 hFont2 = CreateFont(8, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
113
114 GetClientRect(hCombo, &r);
115 expect_rect(r, 0, 0, 100, 24);
116 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
117 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
118 todo_wine expect_rect(r, 5, 5, 105, 105);
119
120 if (font_height(hFont1) == 10 && font_height(hFont2) == 8)
121 {
122 SendMessage(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
123 GetClientRect(hCombo, &r);
124 expect_rect(r, 0, 0, 100, 18);
125 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
126 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
127 todo_wine expect_rect(r, 5, 5, 105, 99);
128
129 SendMessage(hCombo, WM_SETFONT, (WPARAM)hFont2, FALSE);
130 GetClientRect(hCombo, &r);
131 expect_rect(r, 0, 0, 100, 16);
132 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
133 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
134 todo_wine expect_rect(r, 5, 5, 105, 97);
135
136 SendMessage(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
137 GetClientRect(hCombo, &r);
138 expect_rect(r, 0, 0, 100, 18);
139 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
140 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
141 todo_wine expect_rect(r, 5, 5, 105, 99);
142 }
143 else
144 {
145 ok(0, "Expected Marlett font heights 10/8, got %d/%d\n",
146 font_height(hFont1), font_height(hFont2));
147 }
148
149 for (i = 1; i < 30; i++)
150 {
151 HFONT hFont = CreateFont(i, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
152 int height = font_height(hFont);
153
154 SendMessage(hCombo, WM_SETFONT, (WPARAM)hFont, FALSE);
155 GetClientRect(hCombo, &r);
156 expect_eq(r.bottom - r.top, height + 8, int, "%d");
157 SendMessage(hCombo, WM_SETFONT, 0, FALSE);
158 DeleteObject(hFont);
159 }
160
161 DestroyWindow(hCombo);
162 DeleteObject(hFont1);
163 DeleteObject(hFont2);
164 }
165
166 static LRESULT (CALLBACK *old_parent_proc)(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
167 static LPCSTR expected_edit_text;
168 static LPCSTR expected_list_text;
169 static BOOL selchange_fired;
170
171 static LRESULT CALLBACK parent_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
172 {
173 switch (msg)
174 {
175 case WM_COMMAND:
176 switch (wparam)
177 {
178 case MAKEWPARAM(COMBO_ID, CBN_SELCHANGE):
179 {
180 HWND hCombo = (HWND)lparam;
181 int idx;
182 char list[20], edit[20];
183
184 memset(list, 0, sizeof(list));
185 memset(edit, 0, sizeof(edit));
186
187 idx = SendMessage(hCombo, CB_GETCURSEL, 0, 0);
188 SendMessage(hCombo, CB_GETLBTEXT, idx, (LPARAM)list);
189 SendMessage(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
190
191 ok(!strcmp(edit, expected_edit_text), "edit: got %s, expected %s\n",
192 edit, expected_edit_text);
193 ok(!strcmp(list, expected_list_text), "list: got %s, expected %s\n",
194 list, expected_list_text);
195
196 selchange_fired = TRUE;
197 }
198 break;
199 }
200 break;
201 }
202
203 return CallWindowProc(old_parent_proc, hwnd, msg, wparam, lparam);
204 }
205
206 static void test_selection(DWORD style, const char * const text[],
207 const int *edit, const int *list)
208 {
209 INT idx;
210 HWND hCombo;
211
212 hCombo = build_combo(style);
213
214 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)text[0]);
215 SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)text[1]);
216 SendMessage(hCombo, CB_SETCURSEL, -1, 0);
217
218 old_parent_proc = (void *)SetWindowLongPtr(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)parent_wnd_proc);
219
220 idx = SendMessage(hCombo, CB_GETCURSEL, 0, 0);
221 ok(idx == -1, "expected selection -1, got %d\n", idx);
222
223 /* keyboard navigation */
224
225 expected_list_text = text[list[0]];
226 expected_edit_text = text[edit[0]];
227 selchange_fired = FALSE;
228 SendMessage(hCombo, WM_KEYDOWN, VK_DOWN, 0);
229 ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
230
231 expected_list_text = text[list[1]];
232 expected_edit_text = text[edit[1]];
233 selchange_fired = FALSE;
234 SendMessage(hCombo, WM_KEYDOWN, VK_DOWN, 0);
235 ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
236
237 expected_list_text = text[list[2]];
238 expected_edit_text = text[edit[2]];
239 selchange_fired = FALSE;
240 SendMessage(hCombo, WM_KEYDOWN, VK_UP, 0);
241 ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
242
243 /* programmatic navigation */
244
245 expected_list_text = text[list[3]];
246 expected_edit_text = text[edit[3]];
247 selchange_fired = FALSE;
248 SendMessage(hCombo, CB_SETCURSEL, list[3], 0);
249 ok(!selchange_fired, "CBN_SELCHANGE sent!\n");
250
251 expected_list_text = text[list[4]];
252 expected_edit_text = text[edit[4]];
253 selchange_fired = FALSE;
254 SendMessage(hCombo, CB_SETCURSEL, list[4], 0);
255 ok(!selchange_fired, "CBN_SELCHANGE sent!\n");
256
257 SetWindowLongPtr(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc);
258 DestroyWindow(hCombo);
259 }
260
261 static void test_CBN_SELCHANGE(void)
262 {
263 static const char * const text[] = { "alpha", "beta", "" };
264 static const int sel_1[] = { 2, 0, 1, 0, 1 };
265 static const int sel_2[] = { 0, 1, 0, 0, 1 };
266
267 test_selection(CBS_SIMPLE, text, sel_1, sel_2);
268 test_selection(CBS_DROPDOWN, text, sel_1, sel_2);
269 test_selection(CBS_DROPDOWNLIST, text, sel_2, sel_2);
270 }
271
272 static void test_WM_LBUTTONDOWN(void)
273 {
274 HWND hCombo, hEdit, hList;
275 COMBOBOXINFO cbInfo;
276 UINT x, y, item_height;
277 LRESULT result;
278 int i, idx;
279 RECT rect;
280 CHAR buffer[3];
281 static const UINT choices[] = {8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72};
282 static const CHAR stringFormat[] = "%2d";
283 BOOL ret;
284 BOOL (WINAPI *pGetComboBoxInfo)(HWND, PCOMBOBOXINFO);
285
286 pGetComboBoxInfo = (void*)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo");
287 if (!pGetComboBoxInfo){
288 win_skip("GetComboBoxInfo is not available\n");
289 return;
290 }
291
292 hCombo = CreateWindow("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|CBS_DROPDOWN,
293 0, 0, 200, 150, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
294
295 for (i = 0; i < sizeof(choices)/sizeof(UINT); i++){
296 sprintf(buffer, stringFormat, choices[i]);
297 result = SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)buffer);
298 ok(result == i,
299 "Failed to add item %d\n", i);
300 }
301
302 cbInfo.cbSize = sizeof(COMBOBOXINFO);
303 SetLastError(0xdeadbeef);
304 ret = pGetComboBoxInfo(hCombo, &cbInfo);
305 ok(ret, "Failed to get combobox info structure. LastError=%d\n",
306 GetLastError());
307 hEdit = cbInfo.hwndItem;
308 hList = cbInfo.hwndList;
309
310 trace("hMainWnd=%p, hCombo=%p, hList=%p, hEdit=%p\n", hMainWnd, hCombo, hList, hEdit);
311 ok(GetFocus() == hMainWnd, "Focus not on Main Window, instead on %p\n", GetFocus());
312
313 /* Click on the button to drop down the list */
314 x = cbInfo.rcButton.left + (cbInfo.rcButton.right-cbInfo.rcButton.left)/2;
315 y = cbInfo.rcButton.top + (cbInfo.rcButton.bottom-cbInfo.rcButton.top)/2;
316 result = SendMessage(hCombo, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
317 ok(result, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
318 GetLastError());
319 ok(SendMessage(hCombo, CB_GETDROPPEDSTATE, 0, 0),
320 "The dropdown list should have appeared after clicking the button.\n");
321
322 ok(GetFocus() == hEdit,
323 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
324 result = SendMessage(hCombo, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
325 ok(result, "WM_LBUTTONUP was not processed. LastError=%d\n",
326 GetLastError());
327 ok(GetFocus() == hEdit,
328 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
329
330 /* Click on the 5th item in the list */
331 item_height = SendMessage(hCombo, CB_GETITEMHEIGHT, 0, 0);
332 ok(GetClientRect(hList, &rect), "Failed to get list's client rect.\n");
333 x = rect.left + (rect.right-rect.left)/2;
334 y = item_height/2 + item_height*4;
335 result = SendMessage(hList, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
336 ok(!result, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
337 GetLastError());
338 ok(GetFocus() == hEdit,
339 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
340
341 result = SendMessage(hList, WM_MOUSEMOVE, 0, MAKELPARAM(x, y));
342 ok(!result, "WM_MOUSEMOVE was not processed. LastError=%d\n",
343 GetLastError());
344 ok(GetFocus() == hEdit,
345 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
346 ok(SendMessage(hCombo, CB_GETDROPPEDSTATE, 0, 0),
347 "The dropdown list should still be visible.\n");
348
349 result = SendMessage(hList, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
350 ok(!result, "WM_LBUTTONUP was not processed. LastError=%d\n",
351 GetLastError());
352 ok(GetFocus() == hEdit,
353 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
354 ok(!SendMessage(hCombo, CB_GETDROPPEDSTATE, 0, 0),
355 "The dropdown list should have been rolled up.\n");
356 idx = SendMessage(hCombo, CB_GETCURSEL, 0, 0);
357 ok(idx, "Current Selection: expected %d, got %d\n", 4, idx);
358
359 DestroyWindow(hCombo);
360 }
361
362 static void test_changesize( DWORD style)
363 {
364 HWND hCombo = build_combo(style);
365 RECT rc;
366 INT ddheight, clheight, ddwidth, clwidth;
367 /* get initial measurements */
368 GetClientRect( hCombo, &rc);
369 clheight = rc.bottom - rc.top;
370 clwidth = rc.right - rc.left;
371 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
372 ddheight = rc.bottom - rc.top;
373 ddwidth = rc.right - rc.left;
374 /* use MoveWindow to move & resize the combo */
375 /* first make it slightly smaller */
376 MoveWindow( hCombo, 10, 10, clwidth - 2, clheight - 2, TRUE);
377 GetClientRect( hCombo, &rc);
378 ok( rc.right - rc.left == clwidth - 2, "clientrect witdh is %d vs %d\n",
379 rc.right - rc.left, clwidth - 2);
380 ok( rc.bottom - rc.top == clheight, "clientrect height is %d vs %d\n",
381 rc.bottom - rc.top, clheight);
382 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
383 ok( rc.right - rc.left == clwidth - 2, "drop-down rect witdh is %d vs %d\n",
384 rc.right - rc.left, clwidth - 2);
385 ok( rc.bottom - rc.top == ddheight, "drop-down rect height is %d vs %d\n",
386 rc.bottom - rc.top, ddheight);
387 /* new cx, cy is slightly bigger than the initial values */
388 MoveWindow( hCombo, 10, 10, clwidth + 2, clheight + 2, TRUE);
389 GetClientRect( hCombo, &rc);
390 ok( rc.right - rc.left == clwidth + 2, "clientrect witdh is %d vs %d\n",
391 rc.right - rc.left, clwidth + 2);
392 ok( rc.bottom - rc.top == clheight, "clientrect height is %d vs %d\n",
393 rc.bottom - rc.top, clheight);
394 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
395 ok( rc.right - rc.left == clwidth + 2, "drop-down rect witdh is %d vs %d\n",
396 rc.right - rc.left, clwidth + 2);
397 todo_wine {
398 ok( rc.bottom - rc.top == clheight + 2, "drop-down rect height is %d vs %d\n",
399 rc.bottom - rc.top, clheight + 2);
400 }
401 DestroyWindow(hCombo);
402 }
403
404 START_TEST(combo)
405 {
406 hMainWnd = CreateWindow("static", "Test", WS_OVERLAPPEDWINDOW, 10, 10, 300, 300, NULL, NULL, NULL, 0);
407 ShowWindow(hMainWnd, SW_SHOW);
408
409 test_setfont(CBS_DROPDOWN);
410 test_setfont(CBS_DROPDOWNLIST);
411 test_setitemheight(CBS_DROPDOWN);
412 test_setitemheight(CBS_DROPDOWNLIST);
413 test_CBN_SELCHANGE();
414 test_WM_LBUTTONDOWN();
415 test_changesize(CBS_DROPDOWN);
416 test_changesize(CBS_DROPDOWNLIST);
417
418 DestroyWindow(hMainWnd);
419 }