1 /* Unit test suite for comboex control.
3 * Copyright 2005 Jason Edmeades
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
24 #include "wine/test.h"
27 #define EDITBOX_SEQ_INDEX 0
28 #define NUM_MSG_SEQUENCES 1
32 #define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
34 static struct msg_sequence
*sequences
[NUM_MSG_SEQUENCES
];
36 static HWND hComboExParentWnd
;
37 static HINSTANCE hMainHinst
;
38 static const char ComboExTestClass
[] = "ComboExTestClass";
40 static BOOL (WINAPI
*pSetWindowSubclass
)(HWND
, SUBCLASSPROC
, UINT_PTR
, DWORD_PTR
);
43 static char *textBuffer
= NULL
;
45 static HWND
createComboEx(DWORD style
) {
46 return CreateWindowExA(0, WC_COMBOBOXEXA
, NULL
, style
, 0, 0, 300, 300,
47 hComboExParentWnd
, NULL
, hMainHinst
, NULL
);
50 static LONG
addItem(HWND cbex
, int idx
, LPTSTR text
) {
51 COMBOBOXEXITEM cbexItem
;
52 memset(&cbexItem
, 0x00, sizeof(cbexItem
));
53 cbexItem
.mask
= CBEIF_TEXT
;
55 cbexItem
.pszText
= text
;
56 cbexItem
.cchTextMax
= 0;
57 return SendMessage(cbex
, CBEM_INSERTITEM
, 0, (LPARAM
)&cbexItem
);
60 static LONG
setItem(HWND cbex
, int idx
, LPTSTR text
) {
61 COMBOBOXEXITEM cbexItem
;
62 memset(&cbexItem
, 0x00, sizeof(cbexItem
));
63 cbexItem
.mask
= CBEIF_TEXT
;
65 cbexItem
.pszText
= text
;
66 cbexItem
.cchTextMax
= 0;
67 return SendMessage(cbex
, CBEM_SETITEM
, 0, (LPARAM
)&cbexItem
);
70 static LONG
delItem(HWND cbex
, int idx
) {
71 return SendMessage(cbex
, CBEM_DELETEITEM
, idx
, 0);
74 static LONG
getItem(HWND cbex
, int idx
, COMBOBOXEXITEM
*cbItem
) {
75 memset(cbItem
, 0x00, sizeof(COMBOBOXEXITEM
));
76 cbItem
->mask
= CBEIF_TEXT
;
77 cbItem
->pszText
= textBuffer
;
79 cbItem
->cchTextMax
= 100;
80 return SendMessage(cbex
, CBEM_GETITEM
, 0, (LPARAM
)cbItem
);
83 static LRESULT WINAPI
editbox_subclass_proc(HWND hwnd
, UINT message
, WPARAM wParam
, LPARAM lParam
)
85 WNDPROC oldproc
= (WNDPROC
)GetWindowLongPtrA(hwnd
, GWLP_USERDATA
);
86 static LONG defwndproc_counter
= 0;
90 msg
.message
= message
;
91 msg
.flags
= sent
|wparam
|lparam
;
92 if (defwndproc_counter
) msg
.flags
|= defwinproc
;
97 if (message
!= WM_PAINT
&&
98 message
!= WM_ERASEBKGND
&&
99 message
!= WM_NCPAINT
&&
100 message
!= WM_NCHITTEST
&&
101 message
!= WM_GETTEXT
&&
102 message
!= WM_GETICON
&&
103 message
!= WM_DEVICECHANGE
)
105 add_message(sequences
, EDITBOX_SEQ_INDEX
, &msg
);
108 defwndproc_counter
++;
109 ret
= CallWindowProcA(oldproc
, hwnd
, message
, wParam
, lParam
);
110 defwndproc_counter
--;
114 static HWND
subclass_editbox(HWND hwndComboEx
)
119 hwnd
= (HWND
)SendMessage(hwndComboEx
, CBEM_GETEDITCONTROL
, 0, 0);
120 oldproc
= (WNDPROC
)SetWindowLongPtrA(hwnd
, GWLP_WNDPROC
,
121 (LONG_PTR
)editbox_subclass_proc
);
122 SetWindowLongPtrA(hwnd
, GWLP_USERDATA
, (LONG_PTR
)oldproc
);
127 static void test_comboboxex(void) {
130 COMBOBOXEXITEM cbexItem
;
131 static TCHAR first_item
[] = {'F','i','r','s','t',' ','I','t','e','m',0},
132 second_item
[] = {'S','e','c','o','n','d',' ','I','t','e','m',0},
133 third_item
[] = {'T','h','i','r','d',' ','I','t','e','m',0},
134 middle_item
[] = {'B','e','t','w','e','e','n',' ','F','i','r','s','t',' ','a','n','d',' ',
135 'S','e','c','o','n','d',' ','I','t','e','m','s',0},
136 replacement_item
[] = {'B','e','t','w','e','e','n',' ','F','i','r','s','t',' ','a','n','d',' ',
137 'S','e','c','o','n','d',' ','I','t','e','m','s',0},
138 out_of_range_item
[] = {'O','u','t',' ','o','f',' ','R','a','n','g','e',' ','I','t','e','m',0};
140 /* Allocate space for result */
141 textBuffer
= HeapAlloc(GetProcessHeap(), 0, MAX_CHARS
);
143 /* Basic comboboxex test */
144 myHwnd
= createComboEx(WS_BORDER
| WS_VISIBLE
| WS_CHILD
| CBS_DROPDOWN
);
146 /* Add items onto the end of the combobox */
147 res
= addItem(myHwnd
, -1, first_item
);
148 ok(res
== 0, "Adding simple item failed (%d)\n", res
);
149 res
= addItem(myHwnd
, -1, second_item
);
150 ok(res
== 1, "Adding simple item failed (%d)\n", res
);
151 res
= addItem(myHwnd
, 2, third_item
);
152 ok(res
== 2, "Adding simple item failed (%d)\n", res
);
153 res
= addItem(myHwnd
, 1, middle_item
);
154 ok(res
== 1, "Inserting simple item failed (%d)\n", res
);
156 /* Add an item completely out of range */
157 res
= addItem(myHwnd
, 99, out_of_range_item
);
158 ok(res
== -1, "Adding using out of range index worked unexpectedly (%d)\n", res
);
159 res
= addItem(myHwnd
, 5, out_of_range_item
);
160 ok(res
== -1, "Adding using out of range index worked unexpectedly (%d)\n", res
);
161 /* Removed: Causes traps on Windows XP
162 res = addItem(myHwnd, -2, "Out Of Range Item");
163 ok(res == -1, "Adding out of range worked unexpectedly (%ld)\n", res);
166 /* Get an item completely out of range */
167 res
= getItem(myHwnd
, 99, &cbexItem
);
168 ok(res
== 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res
, cbexItem
.pszText
);
169 res
= getItem(myHwnd
, 4, &cbexItem
);
170 ok(res
== 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res
, cbexItem
.pszText
);
171 res
= getItem(myHwnd
, -2, &cbexItem
);
172 ok(res
== 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res
, cbexItem
.pszText
);
174 /* Get an item in range */
175 res
= getItem(myHwnd
, 0, &cbexItem
);
176 ok(res
!= 0, "Getting item using valid index failed unexpectedly (%d)\n", res
);
177 ok(strcmp(first_item
, cbexItem
.pszText
) == 0, "Getting item returned wrong string (%s)\n", cbexItem
.pszText
);
179 res
= getItem(myHwnd
, 1, &cbexItem
);
180 ok(res
!= 0, "Getting item using valid index failed unexpectedly (%d)\n", res
);
181 ok(strcmp(middle_item
, cbexItem
.pszText
) == 0, "Getting item returned wrong string (%s)\n", cbexItem
.pszText
);
183 res
= getItem(myHwnd
, 2, &cbexItem
);
184 ok(res
!= 0, "Getting item using valid index failed unexpectedly (%d)\n", res
);
185 ok(strcmp(second_item
, cbexItem
.pszText
) == 0, "Getting item returned wrong string (%s)\n", cbexItem
.pszText
);
187 res
= getItem(myHwnd
, 3, &cbexItem
);
188 ok(res
!= 0, "Getting item using valid index failed unexpectedly (%d)\n", res
);
189 ok(strcmp(third_item
, cbexItem
.pszText
) == 0, "Getting item returned wrong string (%s)\n", cbexItem
.pszText
);
191 /* Set an item completely out of range */
192 res
= setItem(myHwnd
, 99, replacement_item
);
193 ok(res
== 0, "Setting item using out of range index worked unexpectedly (%d)\n", res
);
194 res
= setItem(myHwnd
, 4, replacement_item
);
195 ok(res
== 0, "Setting item using out of range index worked unexpectedly (%d)\n", res
);
196 res
= setItem(myHwnd
, -2, replacement_item
);
197 ok(res
== 0, "Setting item using out of range index worked unexpectedly (%d)\n", res
);
199 /* Set an item in range */
200 res
= setItem(myHwnd
, 0, replacement_item
);
201 ok(res
!= 0, "Setting first item failed (%d)\n", res
);
202 res
= setItem(myHwnd
, 3, replacement_item
);
203 ok(res
!= 0, "Setting last item failed (%d)\n", res
);
205 /* Remove items completely out of range (4 items in control at this point) */
206 res
= delItem(myHwnd
, -1);
207 ok(res
== CB_ERR
, "Deleting using out of range index worked unexpectedly (%d)\n", res
);
208 res
= delItem(myHwnd
, 4);
209 ok(res
== CB_ERR
, "Deleting using out of range index worked unexpectedly (%d)\n", res
);
211 /* Remove items in range (4 items in control at this point) */
212 res
= delItem(myHwnd
, 3);
213 ok(res
== 3, "Deleting using out of range index failed (%d)\n", res
);
214 res
= delItem(myHwnd
, 0);
215 ok(res
== 2, "Deleting using out of range index failed (%d)\n", res
);
216 res
= delItem(myHwnd
, 0);
217 ok(res
== 1, "Deleting using out of range index failed (%d)\n", res
);
218 res
= delItem(myHwnd
, 0);
219 ok(res
== 0, "Deleting using out of range index failed (%d)\n", res
);
221 /* Remove from an empty box */
222 res
= delItem(myHwnd
, 0);
223 ok(res
== CB_ERR
, "Deleting using out of range index worked unexpectedly (%d)\n", res
);
227 HeapFree(GetProcessHeap(), 0, textBuffer
);
228 DestroyWindow(myHwnd
);
231 static void test_WM_LBUTTONDOWN(void)
233 HWND hComboEx
, hCombo
, hEdit
, hList
;
235 UINT x
, y
, item_height
;
240 static const UINT choices
[] = {8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72};
241 static const WCHAR stringFormat
[] = {'%','2','d','\0'};
242 BOOL (WINAPI
*pGetComboBoxInfo
)(HWND
, PCOMBOBOXINFO
);
244 pGetComboBoxInfo
= (void*)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo");
245 if (!pGetComboBoxInfo
){
246 win_skip("GetComboBoxInfo is not available\n");
250 hComboEx
= CreateWindowExA(0, WC_COMBOBOXEXA
, NULL
,
251 WS_VISIBLE
|WS_CHILD
|CBS_DROPDOWN
, 0, 0, 200, 150,
252 hComboExParentWnd
, NULL
, hMainHinst
, NULL
);
254 for (i
= 0; i
< sizeof(choices
)/sizeof(UINT
); i
++){
255 COMBOBOXEXITEMW cbexItem
;
256 wsprintfW(buffer
, stringFormat
, choices
[i
]);
258 memset(&cbexItem
, 0x00, sizeof(cbexItem
));
259 cbexItem
.mask
= CBEIF_TEXT
;
261 cbexItem
.pszText
= buffer
;
262 cbexItem
.cchTextMax
= 0;
263 ok(SendMessageW(hComboEx
, CBEM_INSERTITEMW
, 0, (LPARAM
)&cbexItem
) >= 0,
264 "Failed to add item %d\n", i
);
267 hCombo
= (HWND
)SendMessage(hComboEx
, CBEM_GETCOMBOCONTROL
, 0, 0);
268 hEdit
= (HWND
)SendMessage(hComboEx
, CBEM_GETEDITCONTROL
, 0, 0);
270 cbInfo
.cbSize
= sizeof(COMBOBOXINFO
);
271 result
= pGetComboBoxInfo(hCombo
, &cbInfo
);
272 ok(result
, "Failed to get combobox info structure. LastError=%d\n",
274 hList
= cbInfo
.hwndList
;
276 trace("hWnd=%p, hComboEx=%p, hCombo=%p, hList=%p, hEdit=%p\n",
277 hComboExParentWnd
, hComboEx
, hCombo
, hList
, hEdit
);
278 ok(GetFocus() == hComboExParentWnd
,
279 "Focus not on Main Window, instead on %p\n", GetFocus());
281 /* Click on the button to drop down the list */
282 x
= cbInfo
.rcButton
.left
+ (cbInfo
.rcButton
.right
-cbInfo
.rcButton
.left
)/2;
283 y
= cbInfo
.rcButton
.top
+ (cbInfo
.rcButton
.bottom
-cbInfo
.rcButton
.top
)/2;
284 result
= SendMessage(hCombo
, WM_LBUTTONDOWN
, 0, MAKELPARAM(x
, y
));
285 ok(result
, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
287 ok(GetFocus() == hCombo
||
288 broken(GetFocus() != hCombo
), /* win98 */
289 "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n",
291 ok(SendMessage(hComboEx
, CB_GETDROPPEDSTATE
, 0, 0),
292 "The dropdown list should have appeared after clicking the button.\n");
293 idx
= SendMessage(hCombo
, CB_GETTOPINDEX
, 0, 0);
294 ok(idx
== 0, "For TopIndex expected %d, got %d\n", 0, idx
);
296 result
= SendMessage(hCombo
, WM_LBUTTONUP
, 0, MAKELPARAM(x
, y
));
297 ok(result
, "WM_LBUTTONUP was not processed. LastError=%d\n",
299 ok(GetFocus() == hCombo
||
300 broken(GetFocus() != hCombo
), /* win98 */
301 "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n",
304 /* Click on the 5th item in the list */
305 item_height
= SendMessage(hCombo
, CB_GETITEMHEIGHT
, 0, 0);
306 ok(GetClientRect(hList
, &rect
), "Failed to get list's client rect.\n");
307 x
= rect
.left
+ (rect
.right
-rect
.left
)/2;
308 y
= item_height
/2 + item_height
*4;
309 result
= SendMessage(hList
, WM_MOUSEMOVE
, 0, MAKELPARAM(x
, y
));
310 ok(!result
, "WM_MOUSEMOVE was not processed. LastError=%d\n",
312 ok(GetFocus() == hCombo
||
313 broken(GetFocus() != hCombo
), /* win98 */
314 "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n",
317 result
= SendMessage(hList
, WM_LBUTTONDOWN
, 0, MAKELPARAM(x
, y
));
318 ok(!result
, "WM_LBUTTONDOWN was not processed. LastError=%d\n",
320 ok(GetFocus() == hCombo
||
321 broken(GetFocus() != hCombo
), /* win98 */
322 "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n",
324 ok(SendMessage(hComboEx
, CB_GETDROPPEDSTATE
, 0, 0),
325 "The dropdown list should still be visible.\n");
327 result
= SendMessage(hList
, WM_LBUTTONUP
, 0, MAKELPARAM(x
, y
));
328 ok(!result
, "WM_LBUTTONUP was not processed. LastError=%d\n",
330 todo_wine
ok(GetFocus() == hEdit
||
331 broken(GetFocus() == hCombo
), /* win98 */
332 "Focus not on ComboBoxEx's Edit Control, instead on %p\n",
335 result
= SendMessage(hCombo
, CB_GETDROPPEDSTATE
, 0, 0);
337 broken(result
!= 0), /* win98 */
338 "The dropdown list should have been rolled up.\n");
339 idx
= SendMessage(hComboEx
, CB_GETCURSEL
, 0, 0);
341 broken(idx
== -1), /* win98 */
342 "Current Selection: expected %d, got %d\n", 4, idx
);
344 DestroyWindow(hComboEx
);
347 static void test_CB_GETLBTEXT(void)
351 COMBOBOXEXITEMA item
;
354 hCombo
= createComboEx(WS_BORDER
| WS_VISIBLE
| WS_CHILD
| CBS_DROPDOWN
);
356 /* set text to null */
357 addItem(hCombo
, 0, NULL
);
360 item
.mask
= CBEIF_TEXT
;
364 ret
= SendMessage(hCombo
, CBEM_GETITEMA
, 0, (LPARAM
)&item
);
365 ok(ret
!= 0, "CBEM_GETITEM failed\n");
366 ok(buff
[0] == 0, "\n");
368 ret
= SendMessage(hCombo
, CB_GETLBTEXTLEN
, 0, 0);
369 ok(ret
== 0, "Expected zero length\n");
371 ret
= SendMessage(hCombo
, CB_GETLBTEXTLEN
, 0, 0);
372 ok(ret
== 0, "Expected zero length\n");
375 ret
= SendMessage(hCombo
, CB_GETLBTEXT
, 0, (LPARAM
)buff
);
376 ok(ret
== 0, "Expected zero length\n");
377 ok(buff
[0] == 0, "Expected null terminator as a string, got %s\n", buff
);
379 DestroyWindow(hCombo
);
382 static LRESULT CALLBACK
ComboExTestWndProc(HWND hWnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
391 return DefWindowProcA(hWnd
, msg
, wParam
, lParam
);
397 static int init(void)
400 BOOL (WINAPI
*pInitCommonControlsEx
)(const INITCOMMONCONTROLSEX
*);
402 INITCOMMONCONTROLSEX iccex
;
404 hComctl32
= GetModuleHandleA("comctl32.dll");
405 pInitCommonControlsEx
= (void*)GetProcAddress(hComctl32
, "InitCommonControlsEx");
406 if (!pInitCommonControlsEx
)
408 win_skip("InitCommonControlsEx() is missing. Skipping the tests\n");
411 iccex
.dwSize
= sizeof(iccex
);
412 iccex
.dwICC
= ICC_USEREX_CLASSES
;
413 pInitCommonControlsEx(&iccex
);
415 pSetWindowSubclass
= (void*)GetProcAddress(hComctl32
, (LPSTR
)410);
417 wc
.style
= CS_HREDRAW
| CS_VREDRAW
;
420 wc
.hInstance
= GetModuleHandleA(NULL
);
422 wc
.hCursor
= LoadCursorA(NULL
, IDC_ARROW
);
423 wc
.hbrBackground
= GetSysColorBrush(COLOR_WINDOW
);
424 wc
.lpszMenuName
= NULL
;
425 wc
.lpszClassName
= ComboExTestClass
;
426 wc
.lpfnWndProc
= ComboExTestWndProc
;
429 hComboExParentWnd
= CreateWindowExA(0, ComboExTestClass
, "ComboEx test", WS_OVERLAPPEDWINDOW
|WS_VISIBLE
,
430 CW_USEDEFAULT
, CW_USEDEFAULT
, 680, 260, NULL
, NULL
, GetModuleHandleA(NULL
), 0);
431 assert(hComboExParentWnd
!= NULL
);
433 hMainHinst
= GetModuleHandleA(NULL
);
437 static void cleanup(void)
441 PostMessageA(hComboExParentWnd
, WM_CLOSE
, 0, 0);
442 while (GetMessageA(&msg
,0,0,0)) {
443 TranslateMessage(&msg
);
444 DispatchMessageA(&msg
);
447 DestroyWindow(hComboExParentWnd
);
448 UnregisterClassA(ComboExTestClass
, GetModuleHandleA(NULL
));
451 static void test_comboboxex_subclass(void)
453 HWND hComboEx
, hCombo
, hEdit
;
455 hComboEx
= createComboEx(WS_BORDER
| WS_VISIBLE
| WS_CHILD
| CBS_DROPDOWN
);
457 hCombo
= (HWND
)SendMessage(hComboEx
, CBEM_GETCOMBOCONTROL
, 0, 0);
458 ok(hCombo
!= NULL
, "Failed to get internal combo\n");
459 hEdit
= (HWND
)SendMessage(hComboEx
, CBEM_GETEDITCONTROL
, 0, 0);
460 ok(hEdit
!= NULL
, "Failed to get internal edit\n");
462 if (pSetWindowSubclass
)
464 ok(GetPropA(hCombo
, "CC32SubclassInfo") != NULL
, "Expected CC32SubclassInfo property\n");
465 ok(GetPropA(hEdit
, "CC32SubclassInfo") != NULL
, "Expected CC32SubclassInfo property\n");
468 DestroyWindow(hComboEx
);
471 static const struct message test_setitem_edit_seq
[] = {
472 { WM_SETTEXT
, sent
|id
, 0, 0, EDITBOX_ID
},
473 { EM_SETSEL
, sent
|id
|wparam
|lparam
, 0, 0, EDITBOX_ID
},
474 { EM_SETSEL
, sent
|id
|wparam
|lparam
, 0, -1, EDITBOX_ID
},
478 static void test_get_set_item(void)
480 char textA
[] = "test";
482 COMBOBOXEXITEMA item
;
485 hComboEx
= createComboEx(WS_BORDER
| WS_VISIBLE
| WS_CHILD
| CBS_DROPDOWN
);
487 subclass_editbox(hComboEx
);
489 flush_sequences(sequences
, NUM_MSG_SEQUENCES
);
491 memset(&item
, 0, sizeof(item
));
492 item
.mask
= CBEIF_TEXT
;
493 item
.pszText
= textA
;
495 ret
= SendMessage(hComboEx
, CBEM_SETITEMA
, 0, (LPARAM
)&item
);
498 ok_sequence(sequences
, EDITBOX_SEQ_INDEX
, test_setitem_edit_seq
, "set item data for edit", FALSE
);
501 item
.mask
= CBEIF_LPARAM
;
503 item
.lParam
= 0xdeadbeef;
504 ret
= SendMessage(hComboEx
, CBEM_GETITEMA
, 0, (LPARAM
)&item
);
506 ok(item
.lParam
== 0, "Expected zero, got %ld\n", item
.lParam
);
508 item
.lParam
= 0xdeadbeef;
509 ret
= SendMessage(hComboEx
, CBEM_SETITEMA
, 0, (LPARAM
)&item
);
513 ret
= SendMessage(hComboEx
, CBEM_GETITEMA
, 0, (LPARAM
)&item
);
515 ok(item
.lParam
== 0xdeadbeef, "Expected 0xdeadbeef, got %ld\n", item
.lParam
);
517 DestroyWindow(hComboEx
);
525 init_msg_sequences(sequences
, NUM_MSG_SEQUENCES
);
528 test_WM_LBUTTONDOWN();
530 test_comboboxex_subclass();