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
);
358 static void test_changesize( DWORD style
)
360 HWND hCombo
= build_combo(style
);
362 INT ddheight
, clheight
, ddwidth
, clwidth
;
363 /* get initial measurements */
364 GetClientRect( hCombo
, &rc
);
365 clheight
= rc
.bottom
- rc
.top
;
366 clwidth
= rc
.right
- rc
.left
;
367 SendMessageA(hCombo
, CB_GETDROPPEDCONTROLRECT
, 0, (LPARAM
)&rc
);
368 ddheight
= rc
.bottom
- rc
.top
;
369 ddwidth
= rc
.right
- rc
.left
;
370 /* use MoveWindow to move & resize the combo */
371 /* first make it slightly smaller */
372 MoveWindow( hCombo
, 10, 10, clwidth
- 2, clheight
- 2, TRUE
);
373 GetClientRect( hCombo
, &rc
);
374 ok( rc
.right
- rc
.left
== clwidth
- 2, "clientrect witdh is %d vs %d\n",
375 rc
.right
- rc
.left
, clwidth
- 2);
376 ok( rc
.bottom
- rc
.top
== clheight
, "clientrect height is %d vs %d\n",
377 rc
.bottom
- rc
.top
, clheight
);
378 SendMessageA(hCombo
, CB_GETDROPPEDCONTROLRECT
, 0, (LPARAM
)&rc
);
379 ok( rc
.right
- rc
.left
== clwidth
- 2, "drop-down rect witdh is %d vs %d\n",
380 rc
.right
- rc
.left
, clwidth
- 2);
381 ok( rc
.bottom
- rc
.top
== ddheight
, "drop-down rect height is %d vs %d\n",
382 rc
.bottom
- rc
.top
, ddheight
);
383 /* new cx, cy is slightly bigger than the initial values */
384 MoveWindow( hCombo
, 10, 10, clwidth
+ 2, clheight
+ 2, TRUE
);
385 GetClientRect( hCombo
, &rc
);
386 ok( rc
.right
- rc
.left
== clwidth
+ 2, "clientrect witdh is %d vs %d\n",
387 rc
.right
- rc
.left
, clwidth
+ 2);
388 ok( rc
.bottom
- rc
.top
== clheight
, "clientrect height is %d vs %d\n",
389 rc
.bottom
- rc
.top
, clheight
);
390 SendMessageA(hCombo
, CB_GETDROPPEDCONTROLRECT
, 0, (LPARAM
)&rc
);
391 ok( rc
.right
- rc
.left
== clwidth
+ 2, "drop-down rect witdh is %d vs %d\n",
392 rc
.right
- rc
.left
, clwidth
+ 2);
394 ok( rc
.bottom
- rc
.top
== clheight
+ 2, "drop-down rect height is %d vs %d\n",
395 rc
.bottom
- rc
.top
, clheight
+ 2);
397 DestroyWindow(hCombo
);
402 hMainWnd
= CreateWindow("static", "Test", WS_OVERLAPPEDWINDOW
, 10, 10, 300, 300, NULL
, NULL
, NULL
, 0);
403 ShowWindow(hMainWnd
, SW_SHOW
);
405 test_setfont(CBS_DROPDOWN
);
406 test_setfont(CBS_DROPDOWNLIST
);
407 test_setitemheight(CBS_DROPDOWN
);
408 test_setitemheight(CBS_DROPDOWNLIST
);
409 test_CBN_SELCHANGE();
410 test_WM_LBUTTONDOWN();
411 test_changesize(CBS_DROPDOWN
);
412 test_changesize(CBS_DROPDOWNLIST
);
414 DestroyWindow(hMainWnd
);