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
)
102 if (!is_font_installed("Marlett"))
104 skip("Marlett font not available\n");
108 trace("Style %x\n", style
);
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");
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);
120 if (font_height(hFont1
) == 10 && font_height(hFont2
) == 8)
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);
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);
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);
145 ok(0, "Expected Marlett font heights 10/8, got %d/%d\n",
146 font_height(hFont1
), font_height(hFont2
));
149 for (i
= 1; i
< 30; i
++)
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
);
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
);
161 DestroyWindow(hCombo
);
162 DeleteObject(hFont1
);
163 DeleteObject(hFont2
);
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
;
171 static LRESULT CALLBACK
parent_wnd_proc(HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
178 case MAKEWPARAM(COMBO_ID
, CBN_SELCHANGE
):
180 HWND hCombo
= (HWND
)lparam
;
182 char list
[20], edit
[20];
184 memset(list
, 0, sizeof(list
));
185 memset(edit
, 0, sizeof(edit
));
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
);
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
);
196 selchange_fired
= TRUE
;
203 return CallWindowProc(old_parent_proc
, hwnd
, msg
, wparam
, lparam
);
206 static void test_selection(DWORD style
, const char * const text
[],
207 const int *edit
, const int *list
)
212 hCombo
= build_combo(style
);
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);
218 old_parent_proc
= (void *)SetWindowLongPtr(hMainWnd
, GWLP_WNDPROC
, (ULONG_PTR
)parent_wnd_proc
);
220 idx
= SendMessage(hCombo
, CB_GETCURSEL
, 0, 0);
221 ok(idx
== -1, "expected selection -1, got %d\n", idx
);
223 /* keyboard navigation */
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");
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");
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");
243 /* programmatic navigation */
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");
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");
257 SetWindowLongPtr(hMainWnd
, GWLP_WNDPROC
, (ULONG_PTR
)old_parent_proc
);
258 DestroyWindow(hCombo
);
261 static void test_CBN_SELCHANGE(void)
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 };
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
);
272 static void test_WM_LBUTTONDOWN(void)
274 HWND hCombo
, hEdit
, hList
;
276 UINT x
, y
, item_height
;
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";
284 BOOL (WINAPI
*pGetComboBoxInfo
)(HWND
, PCOMBOBOXINFO
);
286 pGetComboBoxInfo
= (void*)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo");
287 if (!pGetComboBoxInfo
){
288 win_skip("GetComboBoxInfo is not available\n");
292 hCombo
= CreateWindow("ComboBox", "Combo", WS_VISIBLE
|WS_CHILD
|CBS_DROPDOWN
,
293 0, 0, 200, 150, hMainWnd
, (HMENU
)COMBO_ID
, NULL
, 0);
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
);
299 "Failed to add item %d\n", i
);
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",
307 hEdit
= cbInfo
.hwndItem
;
308 hList
= cbInfo
.hwndList
;
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());
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",
319 ok(SendMessage(hCombo
, CB_GETDROPPEDSTATE
, 0, 0),
320 "The dropdown list should have appeared after clicking the button.\n");
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",
327 ok(GetFocus() == hEdit
,
328 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
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",
338 ok(GetFocus() == hEdit
,
339 "Focus not on ComboBox's Edit Control, instead on %p\n", GetFocus());
341 result
= SendMessage(hList
, WM_MOUSEMOVE
, 0, MAKELPARAM(x
, y
));
342 ok(!result
, "WM_MOUSEMOVE was not processed. LastError=%d\n",
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");
349 result
= SendMessage(hList
, WM_LBUTTONUP
, 0, MAKELPARAM(x
, y
));
350 ok(!result
, "WM_LBUTTONUP was not processed. LastError=%d\n",
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
);
359 DestroyWindow(hCombo
);
362 static void test_changesize( DWORD style
)
364 HWND hCombo
= build_combo(style
);
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);
398 ok( rc
.bottom
- rc
.top
== clheight
+ 2, "drop-down rect height is %d vs %d\n",
399 rc
.bottom
- rc
.top
, clheight
+ 2);
401 DestroyWindow(hCombo
);
406 hMainWnd
= CreateWindow("static", "Test", WS_OVERLAPPEDWINDOW
, 10, 10, 300, 300, NULL
, NULL
, NULL
, 0);
407 ShowWindow(hMainWnd
, SW_SHOW
);
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
);
418 DestroyWindow(hMainWnd
);