comctl32/tests: Use CRT allocation functions.
[wine.git] / dlls / comctl32 / tests / combo.c
blob4a58b8763a4858a2a8fe1b6bffc520f893c7c678
1 /* Unit test suite for ComboBox and ComboBoxEx32 controls.
3 * Copyright 2005 Jason Edmeades
4 * Copyright 2007 Mikolaj Zalewski
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <limits.h>
22 #include <stdio.h>
23 #include <windows.h>
24 #include <commctrl.h>
26 #include "wine/test.h"
27 #include "v6util.h"
28 #include "msg.h"
30 enum message_seq_index
32 EDITBOX_SEQ_INDEX = 0,
33 PARENT_SEQ_INDEX,
34 NUM_MSG_SEQUENCES,
37 #define EDITBOX_ID 0
38 #define COMBO_ID 1995
40 #define expect_rect(r, _left, _top, _right, _bottom) ok(r.left == _left && r.top == _top && \
41 r.bottom == _bottom && r.right == _right, "Invalid rect %s vs (%d,%d)-(%d,%d)\n", \
42 wine_dbgstr_rect(&r), _left, _top, _right, _bottom);
45 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
47 static HWND hComboExParentWnd, hMainWnd;
48 static HINSTANCE hMainHinst;
49 static const char ComboExTestClass[] = "ComboExTestClass";
51 static HBRUSH brush_red;
53 static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
55 #define MAX_CHARS 100
56 static char *textBuffer = NULL;
58 static BOOL received_end_edit = FALSE;
60 static void get_combobox_info(HWND hwnd, COMBOBOXINFO *info)
62 BOOL ret;
64 info->cbSize = sizeof(*info);
65 ret = GetComboBoxInfo(hwnd, info);
66 ok(ret, "Failed to get combobox info structure, error %ld\n", GetLastError());
69 static HWND createComboEx(DWORD style) {
70 return CreateWindowExA(0, WC_COMBOBOXEXA, NULL, style, 0, 0, 300, 300,
71 hComboExParentWnd, NULL, hMainHinst, NULL);
74 static LONG addItem(HWND cbex, int idx, const char *text) {
75 COMBOBOXEXITEMA cbexItem;
76 memset(&cbexItem, 0x00, sizeof(cbexItem));
77 cbexItem.mask = CBEIF_TEXT;
78 cbexItem.iItem = idx;
79 cbexItem.pszText = (char*)text;
80 cbexItem.cchTextMax = 0;
81 return SendMessageA(cbex, CBEM_INSERTITEMA, 0, (LPARAM)&cbexItem);
84 static LONG setItem(HWND cbex, int idx, const char *text) {
85 COMBOBOXEXITEMA cbexItem;
86 memset(&cbexItem, 0x00, sizeof(cbexItem));
87 cbexItem.mask = CBEIF_TEXT;
88 cbexItem.iItem = idx;
89 cbexItem.pszText = (char*)text;
90 cbexItem.cchTextMax = 0;
91 return SendMessageA(cbex, CBEM_SETITEMA, 0, (LPARAM)&cbexItem);
94 static LONG delItem(HWND cbex, int idx) {
95 return SendMessageA(cbex, CBEM_DELETEITEM, idx, 0);
98 static LONG getItem(HWND cbex, int idx, COMBOBOXEXITEMA *cbItem) {
99 memset(cbItem, 0x00, sizeof(COMBOBOXEXITEMA));
100 cbItem->mask = CBEIF_TEXT;
101 cbItem->pszText = textBuffer;
102 cbItem->iItem = idx;
103 cbItem->cchTextMax = 100;
104 return SendMessageA(cbex, CBEM_GETITEMA, 0, (LPARAM)cbItem);
107 static LRESULT WINAPI editbox_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
109 WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
110 static LONG defwndproc_counter = 0;
111 struct message msg = { 0 };
112 LRESULT ret;
114 msg.message = message;
115 msg.flags = sent|wparam|lparam;
116 if (defwndproc_counter) msg.flags |= defwinproc;
117 msg.wParam = wParam;
118 msg.lParam = lParam;
119 msg.id = EDITBOX_ID;
121 if (message != WM_PAINT &&
122 message != WM_ERASEBKGND &&
123 message != WM_NCPAINT &&
124 message != WM_NCHITTEST &&
125 message != WM_GETTEXT &&
126 message != WM_GETICON &&
127 message != WM_DEVICECHANGE)
129 add_message(sequences, EDITBOX_SEQ_INDEX, &msg);
132 defwndproc_counter++;
133 ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
134 defwndproc_counter--;
135 return ret;
138 static HWND subclass_editbox(HWND hwndComboEx)
140 WNDPROC oldproc;
141 HWND hwnd;
143 hwnd = (HWND)SendMessageA(hwndComboEx, CBEM_GETEDITCONTROL, 0, 0);
144 oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC,
145 (LONG_PTR)editbox_subclass_proc);
146 SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc);
148 return hwnd;
151 static void test_comboex(void)
153 HWND myHwnd = 0;
154 LONG res;
155 COMBOBOXEXITEMA cbexItem;
156 static const char *first_item = "First Item",
157 *second_item = "Second Item",
158 *third_item = "Third Item",
159 *middle_item = "Between First and Second Items",
160 *replacement_item = "Between First and Second Items",
161 *out_of_range_item = "Out of Range Item";
163 /* Allocate space for result */
164 textBuffer = malloc(MAX_CHARS);
166 /* Basic comboboxex test */
167 myHwnd = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
169 /* Add items onto the end of the combobox */
170 res = addItem(myHwnd, -1, first_item);
171 ok(res == 0, "Adding simple item failed (%ld)\n", res);
172 res = addItem(myHwnd, -1, second_item);
173 ok(res == 1, "Adding simple item failed (%ld)\n", res);
174 res = addItem(myHwnd, 2, third_item);
175 ok(res == 2, "Adding simple item failed (%ld)\n", res);
176 res = addItem(myHwnd, 1, middle_item);
177 ok(res == 1, "Inserting simple item failed (%ld)\n", res);
179 /* Add an item completely out of range */
180 res = addItem(myHwnd, 99, out_of_range_item);
181 ok(res == -1, "Adding using out of range index worked unexpectedly (%ld)\n", res);
182 res = addItem(myHwnd, 5, out_of_range_item);
183 ok(res == -1, "Adding using out of range index worked unexpectedly (%ld)\n", res);
184 /* Removed: Causes traps on Windows XP
185 res = addItem(myHwnd, -2, "Out Of Range Item");
186 ok(res == -1, "Adding out of range worked unexpectedly (%ld)\n", res);
189 /* Get an item completely out of range */
190 res = getItem(myHwnd, 99, &cbexItem);
191 ok(res == 0, "Getting item using out of range index worked unexpectedly (%ld, %s)\n", res, cbexItem.pszText);
192 res = getItem(myHwnd, 4, &cbexItem);
193 ok(res == 0, "Getting item using out of range index worked unexpectedly (%ld, %s)\n", res, cbexItem.pszText);
194 res = getItem(myHwnd, -2, &cbexItem);
195 ok(res == 0, "Getting item using out of range index worked unexpectedly (%ld, %s)\n", res, cbexItem.pszText);
197 /* Get an item in range */
198 res = getItem(myHwnd, 0, &cbexItem);
199 ok(res != 0, "Getting item using valid index failed unexpectedly (%ld)\n", res);
200 ok(strcmp(first_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText);
202 res = getItem(myHwnd, 1, &cbexItem);
203 ok(res != 0, "Getting item using valid index failed unexpectedly (%ld)\n", res);
204 ok(strcmp(middle_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText);
206 res = getItem(myHwnd, 2, &cbexItem);
207 ok(res != 0, "Getting item using valid index failed unexpectedly (%ld)\n", res);
208 ok(strcmp(second_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText);
210 res = getItem(myHwnd, 3, &cbexItem);
211 ok(res != 0, "Getting item using valid index failed unexpectedly (%ld)\n", res);
212 ok(strcmp(third_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText);
214 /* Set an item completely out of range */
215 res = setItem(myHwnd, 99, replacement_item);
216 ok(res == 0, "Setting item using out of range index worked unexpectedly (%ld)\n", res);
217 res = setItem(myHwnd, 4, replacement_item);
218 ok(res == 0, "Setting item using out of range index worked unexpectedly (%ld)\n", res);
219 res = setItem(myHwnd, -2, replacement_item);
220 ok(res == 0, "Setting item using out of range index worked unexpectedly (%ld)\n", res);
222 /* Set an item in range */
223 res = setItem(myHwnd, 0, replacement_item);
224 ok(res != 0, "Setting first item failed (%ld)\n", res);
225 res = setItem(myHwnd, 3, replacement_item);
226 ok(res != 0, "Setting last item failed (%ld)\n", res);
228 /* Remove items completely out of range (4 items in control at this point) */
229 res = delItem(myHwnd, -1);
230 ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%ld)\n", res);
231 res = delItem(myHwnd, 4);
232 ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%ld)\n", res);
234 /* Remove items in range (4 items in control at this point) */
235 res = delItem(myHwnd, 3);
236 ok(res == 3, "Deleting using out of range index failed (%ld)\n", res);
237 res = delItem(myHwnd, 0);
238 ok(res == 2, "Deleting using out of range index failed (%ld)\n", res);
239 res = delItem(myHwnd, 0);
240 ok(res == 1, "Deleting using out of range index failed (%ld)\n", res);
241 res = delItem(myHwnd, 0);
242 ok(res == 0, "Deleting using out of range index failed (%ld)\n", res);
244 /* Remove from an empty box */
245 res = delItem(myHwnd, 0);
246 ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%ld)\n", res);
249 /* Cleanup */
250 free(textBuffer);
251 DestroyWindow(myHwnd);
254 static void test_comboex_WM_LBUTTONDOWN(void)
256 HWND hComboEx, hCombo, hEdit, hList;
257 COMBOBOXINFO cbInfo;
258 UINT x, y, item_height;
259 LRESULT result;
260 UINT i;
261 int idx;
262 RECT rect;
263 WCHAR buffer[3];
264 static const UINT choices[] = {8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72};
266 hComboEx = CreateWindowExA(0, WC_COMBOBOXEXA, NULL,
267 WS_VISIBLE|WS_CHILD|CBS_DROPDOWN, 0, 0, 200, 150,
268 hComboExParentWnd, NULL, hMainHinst, NULL);
270 for (i = 0; i < ARRAY_SIZE(choices); i++)
272 COMBOBOXEXITEMW cbexItem;
273 wsprintfW(buffer, L"%2d", choices[i]);
275 memset(&cbexItem, 0x00, sizeof(cbexItem));
276 cbexItem.mask = CBEIF_TEXT;
277 cbexItem.iItem = i;
278 cbexItem.pszText = buffer;
279 cbexItem.cchTextMax = 0;
280 ok(SendMessageW(hComboEx, CBEM_INSERTITEMW, 0, (LPARAM)&cbexItem) >= 0,
281 "Failed to add item %d\n", i);
284 hCombo = (HWND)SendMessageA(hComboEx, CBEM_GETCOMBOCONTROL, 0, 0);
285 hEdit = (HWND)SendMessageA(hComboEx, CBEM_GETEDITCONTROL, 0, 0);
287 get_combobox_info(hCombo, &cbInfo);
288 hList = cbInfo.hwndList;
290 ok(GetFocus() == hComboExParentWnd,
291 "Focus not on Main Window, instead on %p\n", GetFocus());
293 /* Click on the button to drop down the list */
294 x = cbInfo.rcButton.left + (cbInfo.rcButton.right-cbInfo.rcButton.left)/2;
295 y = cbInfo.rcButton.top + (cbInfo.rcButton.bottom-cbInfo.rcButton.top)/2;
296 result = SendMessageA(hCombo, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
297 ok(result, "WM_LBUTTONDOWN was not processed. LastError=%ld\n",
298 GetLastError());
299 ok(GetFocus() == hCombo ||
300 broken(GetFocus() != hCombo), /* win98 */
301 "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n",
302 GetFocus());
303 ok(SendMessageA(hComboEx, CB_GETDROPPEDSTATE, 0, 0),
304 "The dropdown list should have appeared after clicking the button.\n");
305 idx = SendMessageA(hCombo, CB_GETTOPINDEX, 0, 0);
306 ok(idx == 0, "For TopIndex expected %d, got %d\n", 0, idx);
308 result = SendMessageA(hCombo, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
309 ok(result, "WM_LBUTTONUP was not processed. LastError=%ld\n",
310 GetLastError());
311 ok(GetFocus() == hCombo ||
312 broken(GetFocus() != hCombo), /* win98 */
313 "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n",
314 GetFocus());
316 /* Click on the 5th item in the list */
317 item_height = SendMessageA(hCombo, CB_GETITEMHEIGHT, 0, 0);
318 ok(GetClientRect(hList, &rect), "Failed to get list's client rect.\n");
319 x = rect.left + (rect.right-rect.left)/2;
320 y = item_height/2 + item_height*4;
321 result = SendMessageA(hList, WM_MOUSEMOVE, 0, MAKELPARAM(x, y));
322 ok(!result, "WM_MOUSEMOVE was not processed. LastError=%ld\n",
323 GetLastError());
324 ok(GetFocus() == hCombo ||
325 broken(GetFocus() != hCombo), /* win98 */
326 "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n",
327 GetFocus());
329 result = SendMessageA(hList, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y));
330 ok(!result, "WM_LBUTTONDOWN was not processed. LastError=%ld\n",
331 GetLastError());
332 ok(GetFocus() == hCombo ||
333 broken(GetFocus() != hCombo), /* win98 */
334 "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n",
335 GetFocus());
336 ok(SendMessageA(hComboEx, CB_GETDROPPEDSTATE, 0, 0),
337 "The dropdown list should still be visible.\n");
339 result = SendMessageA(hList, WM_LBUTTONUP, 0, MAKELPARAM(x, y));
340 ok(!result, "WM_LBUTTONUP was not processed. LastError=%ld\n",
341 GetLastError());
342 todo_wine ok(GetFocus() == hEdit ||
343 broken(GetFocus() == hCombo), /* win98 */
344 "Focus not on ComboBoxEx's Edit Control, instead on %p\n",
345 GetFocus());
347 result = SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0);
348 ok(!result ||
349 broken(result != 0), /* win98 */
350 "The dropdown list should have been rolled up.\n");
351 idx = SendMessageA(hComboEx, CB_GETCURSEL, 0, 0);
352 ok(idx == 4 ||
353 broken(idx == -1), /* win98 */
354 "Current Selection: expected %d, got %d\n", 4, idx);
355 ok(received_end_edit, "Expected to receive a CBEN_ENDEDIT message\n");
357 SetFocus( hComboExParentWnd );
358 ok( GetFocus() == hComboExParentWnd, "got %p\n", GetFocus() );
359 SetFocus( hComboEx );
360 ok( GetFocus() == hEdit, "got %p\n", GetFocus() );
362 DestroyWindow(hComboEx);
365 static void test_comboex_CB_GETLBTEXT(void)
367 HWND hCombo;
368 CHAR buff[1];
369 COMBOBOXEXITEMA item;
370 LRESULT ret;
372 hCombo = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
374 /* set text to null */
375 addItem(hCombo, 0, NULL);
377 buff[0] = 'a';
378 item.mask = CBEIF_TEXT;
379 item.iItem = 0;
380 item.pszText = buff;
381 item.cchTextMax = 1;
382 ret = SendMessageA(hCombo, CBEM_GETITEMA, 0, (LPARAM)&item);
383 ok(ret != 0, "CBEM_GETITEM failed\n");
384 ok(buff[0] == 0, "\n");
386 ret = SendMessageA(hCombo, CB_GETLBTEXTLEN, 0, 0);
387 ok(ret == 0, "Expected zero length\n");
389 ret = SendMessageA(hCombo, CB_GETLBTEXTLEN, 0, 0);
390 ok(ret == 0, "Expected zero length\n");
392 buff[0] = 'a';
393 ret = SendMessageA(hCombo, CB_GETLBTEXT, 0, (LPARAM)buff);
394 ok(ret == 0, "Expected zero length\n");
395 ok(buff[0] == 0, "Expected null terminator as a string, got %s\n", buff);
397 DestroyWindow(hCombo);
400 static void test_comboex_WM_WINDOWPOSCHANGING(void)
402 HWND hCombo;
403 WINDOWPOS wp;
404 RECT rect;
405 int combo_height;
406 int ret;
408 hCombo = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
409 ok(hCombo != NULL, "createComboEx failed\n");
410 ret = GetWindowRect(hCombo, &rect);
411 ok(ret, "GetWindowRect failed\n");
412 combo_height = rect.bottom - rect.top;
413 ok(combo_height > 0, "wrong combo height\n");
415 /* Test height > combo_height */
416 wp.x = rect.left;
417 wp.y = rect.top;
418 wp.cx = (rect.right - rect.left);
419 wp.cy = combo_height * 2;
420 wp.flags = 0;
421 wp.hwnd = hCombo;
422 wp.hwndInsertAfter = NULL;
424 ret = SendMessageA(hCombo, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp);
425 ok(ret == 0, "expected 0, got %x\n", ret);
426 ok(wp.cy == combo_height,
427 "Expected height %d, got %d\n", combo_height, wp.cy);
429 /* Test height < combo_height */
430 wp.x = rect.left;
431 wp.y = rect.top;
432 wp.cx = (rect.right - rect.left);
433 wp.cy = combo_height / 2;
434 wp.flags = 0;
435 wp.hwnd = hCombo;
436 wp.hwndInsertAfter = NULL;
438 ret = SendMessageA(hCombo, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp);
439 ok(ret == 0, "expected 0, got %x\n", ret);
440 ok(wp.cy == combo_height,
441 "Expected height %d, got %d\n", combo_height, wp.cy);
443 ret = DestroyWindow(hCombo);
444 ok(ret, "DestroyWindow failed\n");
447 struct di_context
449 unsigned int mask;
450 BOOL set_CBEIF_DI_SETITEM;
453 static struct di_context di_context;
455 static LRESULT ComboExTestOnNotify(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
457 struct message msg;
458 NMHDR *hdr = (NMHDR*)lParam;
460 msg.message = message;
461 msg.flags = sent|wparam|lparam;
462 msg.wParam = wParam;
463 msg.lParam = lParam;
464 if (hdr) msg.id = hdr->code;
466 add_message(sequences, PARENT_SEQ_INDEX, &msg);
468 switch (hdr->code)
470 case CBEN_ENDEDITA:
472 NMCBEENDEDITA *edit_info = (NMCBEENDEDITA*)hdr;
473 if(edit_info->iWhy==CBENF_DROPDOWN){
474 received_end_edit = TRUE;
476 break;
478 case CBEN_ENDEDITW:
480 NMCBEENDEDITW *edit_info = (NMCBEENDEDITW*)hdr;
481 if(edit_info->iWhy==CBENF_DROPDOWN){
482 received_end_edit = TRUE;
484 break;
486 case CBEN_GETDISPINFOA:
487 case CBEN_GETDISPINFOW:
489 NMCOMBOBOXEXA *item = (NMCOMBOBOXEXA *)hdr;
491 di_context.mask = item->ceItem.mask;
493 if (item->ceItem.mask & CBEIF_IMAGE)
495 ok(item->ceItem.iImage == I_IMAGECALLBACK, "Unexpected iImage %d.\n", item->ceItem.iImage);
496 item->ceItem.iImage = 123;
499 if (item->ceItem.mask & CBEIF_TEXT)
501 ok(item->ceItem.pszText && item->ceItem.pszText != LPSTR_TEXTCALLBACKA,
502 "Unexpected pszText %p.\n", item->ceItem.pszText);
503 ok(item->ceItem.cchTextMax == 0, "Unexpected cchTextMax %d.\n", item->ceItem.cchTextMax);
506 if (item->ceItem.mask & CBEIF_SELECTEDIMAGE)
507 ok(item->ceItem.iSelectedImage == I_IMAGECALLBACK, "Unexpected iSelectedImage %d.\n",
508 item->ceItem.iSelectedImage);
510 if (item->ceItem.mask & CBEIF_OVERLAY)
511 ok(item->ceItem.iOverlay == I_IMAGECALLBACK, "Unexpected iOverlay %d.\n",
512 item->ceItem.iOverlay);
514 if (item->ceItem.mask & CBEIF_INDENT)
515 ok(item->ceItem.iIndent == 0, "Unexpected iIndent %d.\n", item->ceItem.iIndent);
517 if (di_context.set_CBEIF_DI_SETITEM)
518 item->ceItem.mask |= CBEIF_DI_SETITEM;
520 break;
523 return 0;
526 static LRESULT CALLBACK ComboExTestWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
528 switch(msg) {
530 case WM_DESTROY:
531 PostQuitMessage(0);
532 break;
533 case WM_NOTIFY:
534 return ComboExTestOnNotify(hWnd,msg,wParam,lParam);
535 default:
536 return DefWindowProcA(hWnd, msg, wParam, lParam);
539 return 0L;
542 static void init_functions(void)
544 HMODULE hComCtl32 = LoadLibraryA("comctl32.dll");
546 #define X(f) p##f = (void*)GetProcAddress(hComCtl32, #f);
547 #define X2(f, ord) p##f = (void*)GetProcAddress(hComCtl32, (const char *)ord);
548 X2(SetWindowSubclass, 410);
549 #undef X
550 #undef X2
553 static BOOL init(void)
555 WNDCLASSA wc;
557 wc.style = CS_HREDRAW | CS_VREDRAW;
558 wc.cbClsExtra = 0;
559 wc.cbWndExtra = 0;
560 wc.hInstance = GetModuleHandleA(NULL);
561 wc.hIcon = NULL;
562 wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW);
563 wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
564 wc.lpszMenuName = NULL;
565 wc.lpszClassName = ComboExTestClass;
566 wc.lpfnWndProc = ComboExTestWndProc;
567 RegisterClassA(&wc);
569 brush_red = CreateSolidBrush(RGB(255, 0, 0));
571 hMainWnd = CreateWindowA(WC_STATICA, "Test", WS_OVERLAPPEDWINDOW, 10, 10, 300, 300, NULL, NULL, NULL, 0);
572 ShowWindow(hMainWnd, SW_SHOW);
574 hComboExParentWnd = CreateWindowExA(0, ComboExTestClass, "ComboEx test", WS_OVERLAPPEDWINDOW|WS_VISIBLE,
575 CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, GetModuleHandleA(NULL), 0);
576 ok(hComboExParentWnd != NULL, "failed to create parent window\n");
578 hMainHinst = GetModuleHandleA(NULL);
580 return hComboExParentWnd != NULL;
583 static void cleanup(void)
585 MSG msg;
587 PostMessageA(hComboExParentWnd, WM_CLOSE, 0, 0);
588 while (GetMessageA(&msg,0,0,0)) {
589 TranslateMessage(&msg);
590 DispatchMessageA(&msg);
593 DestroyWindow(hComboExParentWnd);
594 UnregisterClassA(ComboExTestClass, GetModuleHandleA(NULL));
596 DestroyWindow(hMainWnd);
597 DeleteObject(brush_red);
600 static void test_comboex_subclass(void)
602 HWND hComboEx, hCombo, hEdit;
604 hComboEx = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
606 hCombo = (HWND)SendMessageA(hComboEx, CBEM_GETCOMBOCONTROL, 0, 0);
607 ok(hCombo != NULL, "Failed to get internal combo\n");
608 hEdit = (HWND)SendMessageA(hComboEx, CBEM_GETEDITCONTROL, 0, 0);
609 ok(hEdit != NULL, "Failed to get internal edit\n");
611 if (pSetWindowSubclass)
613 ok(GetPropA(hCombo, "CC32SubclassInfo") != NULL, "Expected CC32SubclassInfo property\n");
614 ok(GetPropA(hEdit, "CC32SubclassInfo") != NULL, "Expected CC32SubclassInfo property\n");
617 DestroyWindow(hComboEx);
620 static const struct message test_setitem_edit_seq[] = {
621 { WM_SETTEXT, sent|id, 0, 0, EDITBOX_ID },
622 { EM_SETSEL, sent|id|wparam|lparam, 0, 0, EDITBOX_ID },
623 { EM_SETSEL, sent|id|wparam|lparam, 0, -1, EDITBOX_ID },
624 { 0 }
627 static void test_comboex_get_set_item(void)
629 char textA[] = "test";
630 HWND hComboEx;
631 COMBOBOXEXITEMA item;
632 DWORD ret;
634 hComboEx = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
636 subclass_editbox(hComboEx);
638 flush_sequences(sequences, NUM_MSG_SEQUENCES);
640 memset(&item, 0, sizeof(item));
641 item.mask = CBEIF_TEXT;
642 item.pszText = textA;
643 item.iItem = -1;
644 ret = SendMessageA(hComboEx, CBEM_SETITEMA, 0, (LPARAM)&item);
645 ok(ret == 1, "Unexpected return value %ld.\n", ret);
647 ok_sequence(sequences, EDITBOX_SEQ_INDEX, test_setitem_edit_seq, "set item data for edit", FALSE);
649 /* get/set lParam */
650 item.mask = CBEIF_LPARAM;
651 item.iItem = -1;
652 item.lParam = 0xdeadbeef;
653 ret = SendMessageA(hComboEx, CBEM_GETITEMA, 0, (LPARAM)&item);
654 ok(ret == 1, "Unexpected return value %ld.\n", ret);
655 ok(item.lParam == 0, "Expected zero, got %Ix\n", item.lParam);
657 item.lParam = 0x1abe11ed;
658 ret = SendMessageA(hComboEx, CBEM_SETITEMA, 0, (LPARAM)&item);
659 ok(ret == 1, "Unexpected return value %ld.\n", ret);
661 item.lParam = 0;
662 ret = SendMessageA(hComboEx, CBEM_GETITEMA, 0, (LPARAM)&item);
663 ok(ret == 1, "Unexpected return value %ld.\n", ret);
664 ok(item.lParam == 0x1abe11ed, "Expected 0x1abe11ed, got %Ix\n", item.lParam);
666 DestroyWindow(hComboEx);
669 static HWND create_combobox(DWORD style)
671 return CreateWindowA(WC_COMBOBOXA, "Combo", WS_VISIBLE|WS_CHILD|style, 5, 5, 100, 100, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
674 static int get_font_height(HFONT hFont)
676 TEXTMETRICA tm;
677 HFONT hFontOld;
678 HDC hDC;
680 hDC = CreateCompatibleDC(NULL);
681 hFontOld = SelectObject(hDC, hFont);
682 GetTextMetricsA(hDC, &tm);
683 SelectObject(hDC, hFontOld);
684 DeleteDC(hDC);
686 return tm.tmHeight;
689 static void test_combo_setitemheight(DWORD style)
691 HWND hCombo = create_combobox(style);
692 int i, font_height, height;
693 HFONT hFont;
694 RECT r;
696 GetClientRect(hCombo, &r);
697 expect_rect(r, 0, 0, 100, get_font_height(GetStockObject(SYSTEM_FONT)) + 8);
698 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
699 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
700 todo_wine expect_rect(r, 5, 5, 105, 105);
702 for (i = 1; i < 30; i++)
704 SendMessageA(hCombo, CB_SETITEMHEIGHT, -1, i);
705 GetClientRect(hCombo, &r);
706 ok((r.bottom - r.top) == (i + 6), "Unexpected client rect height.\n");
709 DestroyWindow(hCombo);
711 /* Set item height below text height, force resize. */
712 hCombo = create_combobox(style);
714 hFont = (HFONT)SendMessageA(hCombo, WM_GETFONT, 0, 0);
715 font_height = get_font_height(hFont);
716 SendMessageA(hCombo, CB_SETITEMHEIGHT, -1, font_height / 2);
717 height = SendMessageA(hCombo, CB_GETITEMHEIGHT, -1, 0);
718 todo_wine
719 ok(height == font_height / 2, "Unexpected item height %d, expected %d.\n", height, font_height / 2);
721 SetWindowPos(hCombo, NULL, 10, 10, 150, 5 * font_height, SWP_SHOWWINDOW);
722 height = SendMessageA(hCombo, CB_GETITEMHEIGHT, -1, 0);
723 ok(height > font_height, "Unexpected item height %d, font height %d.\n", height, font_height);
725 DestroyWindow(hCombo);
728 static void test_combo_setfont(DWORD style)
730 HFONT hFont1, hFont2;
731 HWND hCombo;
732 RECT r;
733 int i;
735 hCombo = create_combobox(style);
736 hFont1 = CreateFontA(10, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
737 hFont2 = CreateFontA(8, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
739 GetClientRect(hCombo, &r);
740 expect_rect(r, 0, 0, 100, get_font_height(GetStockObject(SYSTEM_FONT)) + 8);
741 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
742 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
743 todo_wine expect_rect(r, 5, 5, 105, 105);
745 /* The size of the dropped control is initially equal to the size
746 of the window when it was created. The size of the calculated
747 dropped area changes only by how much the selection area
748 changes, not by how much the list area changes. */
749 if (get_font_height(hFont1) == 10 && get_font_height(hFont2) == 8)
751 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
752 GetClientRect(hCombo, &r);
753 expect_rect(r, 0, 0, 100, 18);
754 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
755 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
756 todo_wine expect_rect(r, 5, 5, 105, 105 - (get_font_height(GetStockObject(SYSTEM_FONT)) - get_font_height(hFont1)));
758 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont2, FALSE);
759 GetClientRect(hCombo, &r);
760 expect_rect(r, 0, 0, 100, 16);
761 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
762 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
763 todo_wine expect_rect(r, 5, 5, 105, 105 - (get_font_height(GetStockObject(SYSTEM_FONT)) - get_font_height(hFont2)));
765 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
766 GetClientRect(hCombo, &r);
767 expect_rect(r, 0, 0, 100, 18);
768 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
769 MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
770 todo_wine expect_rect(r, 5, 5, 105, 105 - (get_font_height(GetStockObject(SYSTEM_FONT)) - get_font_height(hFont1)));
772 else
774 ok(0, "Expected Marlett font heights 10/8, got %d/%d\n",
775 get_font_height(hFont1), get_font_height(hFont2));
778 for (i = 1; i < 30; i++)
780 HFONT hFont = CreateFontA(i, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
781 int height = get_font_height(hFont);
783 SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont, FALSE);
784 GetClientRect(hCombo, &r);
785 ok((r.bottom - r.top) == (height + 8), "Unexpected client rect height.\n");
786 SendMessageA(hCombo, WM_SETFONT, 0, FALSE);
787 DeleteObject(hFont);
790 DestroyWindow(hCombo);
791 DeleteObject(hFont1);
792 DeleteObject(hFont2);
795 static LRESULT (CALLBACK *old_parent_proc)(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
796 static LPCSTR expected_edit_text;
797 static LPCSTR expected_list_text;
798 static BOOL selchange_fired;
799 static HWND lparam_for_WM_CTLCOLOR;
801 static LRESULT CALLBACK parent_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
803 switch (msg)
805 case WM_COMMAND:
806 switch (wparam)
808 case MAKEWPARAM(COMBO_ID, CBN_SELCHANGE):
810 HWND hCombo = (HWND)lparam;
811 char list[20], edit[20];
812 int idx;
814 memset(list, 0, sizeof(list));
815 memset(edit, 0, sizeof(edit));
817 idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0);
818 SendMessageA(hCombo, CB_GETLBTEXT, idx, (LPARAM)list);
819 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
821 ok(!strcmp(edit, expected_edit_text), "edit: got %s, expected %s\n",
822 edit, expected_edit_text);
823 ok(!strcmp(list, expected_list_text), "list: got %s, expected %s\n",
824 list, expected_list_text);
826 selchange_fired = TRUE;
828 break;
830 break;
831 case WM_CTLCOLOR:
832 case WM_CTLCOLORMSGBOX:
833 case WM_CTLCOLOREDIT:
834 case WM_CTLCOLORLISTBOX:
835 case WM_CTLCOLORBTN:
836 case WM_CTLCOLORDLG:
837 case WM_CTLCOLORSCROLLBAR:
838 case WM_CTLCOLORSTATIC:
839 if (lparam_for_WM_CTLCOLOR)
841 ok(lparam_for_WM_CTLCOLOR == (HWND)lparam, "Expected %p, got %p\n", lparam_for_WM_CTLCOLOR, (HWND)lparam);
842 return (LRESULT) brush_red;
844 break;
847 return CallWindowProcA(old_parent_proc, hwnd, msg, wparam, lparam);
850 static void test_selection(DWORD style, const char * const text[], const int *edit, const int *list)
852 HWND hCombo;
853 INT idx;
855 hCombo = create_combobox(style);
857 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)text[0]);
858 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)text[1]);
859 SendMessageA(hCombo, CB_SETCURSEL, -1, 0);
861 old_parent_proc = (void *)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)parent_wnd_proc);
863 idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0);
864 ok(idx == -1, "expected selection -1, got %d\n", idx);
866 /* keyboard navigation */
868 expected_list_text = text[list[0]];
869 expected_edit_text = text[edit[0]];
870 selchange_fired = FALSE;
871 SendMessageA(hCombo, WM_KEYDOWN, VK_DOWN, 0);
872 ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
874 expected_list_text = text[list[1]];
875 expected_edit_text = text[edit[1]];
876 selchange_fired = FALSE;
877 SendMessageA(hCombo, WM_KEYDOWN, VK_DOWN, 0);
878 ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
880 expected_list_text = text[list[2]];
881 expected_edit_text = text[edit[2]];
882 selchange_fired = FALSE;
883 SendMessageA(hCombo, WM_KEYDOWN, VK_UP, 0);
884 ok(selchange_fired, "CBN_SELCHANGE not sent!\n");
886 /* programmatic navigation */
888 expected_list_text = text[list[3]];
889 expected_edit_text = text[edit[3]];
890 selchange_fired = FALSE;
891 SendMessageA(hCombo, CB_SETCURSEL, list[3], 0);
892 ok(!selchange_fired, "CBN_SELCHANGE sent!\n");
894 expected_list_text = text[list[4]];
895 expected_edit_text = text[edit[4]];
896 selchange_fired = FALSE;
897 SendMessageA(hCombo, CB_SETCURSEL, list[4], 0);
898 ok(!selchange_fired, "CBN_SELCHANGE sent!\n");
900 SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc);
901 DestroyWindow(hCombo);
904 static void test_combo_CBN_SELCHANGE(void)
906 static const char * const text[] = { "alpha", "beta", "" };
907 static const int sel_1[] = { 2, 0, 1, 0, 1 };
908 static const int sel_2[] = { 0, 1, 0, 0, 1 };
910 test_selection(CBS_SIMPLE, text, sel_1, sel_2);
911 test_selection(CBS_DROPDOWN, text, sel_1, sel_2);
912 test_selection(CBS_DROPDOWNLIST, text, sel_2, sel_2);
915 static void test_combo_changesize(DWORD style)
917 INT ddheight, clheight, ddwidth, clwidth;
918 HWND hCombo;
919 RECT rc;
921 hCombo = create_combobox(style);
923 /* get initial measurements */
924 GetClientRect( hCombo, &rc);
925 clheight = rc.bottom - rc.top;
926 clwidth = rc.right - rc.left;
927 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
928 ddheight = rc.bottom - rc.top;
929 ddwidth = rc.right - rc.left;
930 /* use MoveWindow to move & resize the combo */
931 /* first make it slightly smaller */
932 MoveWindow( hCombo, 10, 10, clwidth - 2, clheight - 2, TRUE);
933 GetClientRect( hCombo, &rc);
934 ok( rc.right - rc.left == clwidth - 2, "clientrect width is %ld vs %d\n",
935 rc.right - rc.left, clwidth - 2);
936 ok( rc.bottom - rc.top == clheight, "clientrect height is %ld vs %d\n",
937 rc.bottom - rc.top, clheight);
938 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
939 ok( rc.right - rc.left == clwidth - 2, "drop-down rect width is %ld vs %d\n",
940 rc.right - rc.left, clwidth - 2);
941 ok( rc.bottom - rc.top == ddheight, "drop-down rect height is %ld vs %d\n",
942 rc.bottom - rc.top, ddheight);
943 ok( rc.right - rc.left == ddwidth -2, "drop-down rect width is %ld vs %d\n",
944 rc.right - rc.left, ddwidth - 2);
945 /* new cx, cy is slightly bigger than the initial values */
946 MoveWindow( hCombo, 10, 10, clwidth + 2, clheight + 2, TRUE);
947 GetClientRect( hCombo, &rc);
948 ok( rc.right - rc.left == clwidth + 2, "clientrect width is %ld vs %d\n",
949 rc.right - rc.left, clwidth + 2);
950 ok( rc.bottom - rc.top == clheight, "clientrect height is %ld vs %d\n",
951 rc.bottom - rc.top, clheight);
952 SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc);
953 ok( rc.right - rc.left == clwidth + 2, "drop-down rect width is %ld vs %d\n",
954 rc.right - rc.left, clwidth + 2);
955 todo_wine {
956 ok( rc.bottom - rc.top == clheight + 2, "drop-down rect height is %ld vs %d\n",
957 rc.bottom - rc.top, clheight + 2);
960 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, -1, 0);
961 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
962 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
963 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
965 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 0, 0);
966 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
967 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
968 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
970 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, clwidth - 1, 0);
971 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
972 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
973 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
975 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, clwidth << 1, 0);
976 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
977 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
978 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
980 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 0, 0);
981 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
982 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
983 ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1);
985 ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 1, 0);
986 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
987 ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0);
988 ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2);
990 DestroyWindow(hCombo);
993 static void test_combo_editselection(void)
995 COMBOBOXINFO cbInfo;
996 INT start, end;
997 char edit[20];
998 HWND hCombo;
999 HWND hEdit;
1000 DWORD len;
1002 /* Build a combo */
1003 hCombo = create_combobox(CBS_SIMPLE);
1005 get_combobox_info(hCombo, &cbInfo);
1006 hEdit = cbInfo.hwndItem;
1008 /* Initially combo selection is empty*/
1009 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
1010 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
1011 ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len));
1013 /* Set some text, and press a key to replace it */
1014 edit[0] = 0x00;
1015 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)"Jason1");
1016 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
1017 ok(strcmp(edit, "Jason1")==0, "Unexpected text retrieved %s\n", edit);
1019 /* Now what is the selection - still empty */
1020 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end);
1021 ok(start==0, "Unexpected start position for selection %d\n", start);
1022 ok(end==0, "Unexpected end position for selection %d\n", end);
1023 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
1024 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
1025 ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len));
1027 /* Give it focus, and it gets selected */
1028 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
1029 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end);
1030 ok(start==0, "Unexpected start position for selection %d\n", start);
1031 ok(end==6, "Unexpected end position for selection %d\n", end);
1032 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
1033 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
1034 ok(HIWORD(len)==6, "Unexpected end position for selection %d\n", HIWORD(len));
1036 /* Now emulate a key press */
1037 edit[0] = 0x00;
1038 SendMessageA(hCombo, WM_CHAR, 'A', 0x1c0001);
1039 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
1040 ok(strcmp(edit, "A")==0, "Unexpected text retrieved %s\n", edit);
1042 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
1043 ok(LOWORD(len)==1, "Unexpected start position for selection %d\n", LOWORD(len));
1044 ok(HIWORD(len)==1, "Unexpected end position for selection %d\n", HIWORD(len));
1046 /* Now what happens when it gets more focus a second time - it doesn't reselect */
1047 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
1048 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
1049 ok(LOWORD(len)==1, "Unexpected start position for selection %d\n", LOWORD(len));
1050 ok(HIWORD(len)==1, "Unexpected end position for selection %d\n", HIWORD(len));
1051 DestroyWindow(hCombo);
1053 /* Start again - Build a combo */
1054 hCombo = create_combobox(CBS_SIMPLE);
1055 get_combobox_info(hCombo, &cbInfo);
1056 hEdit = cbInfo.hwndItem;
1058 /* Set some text and give focus so it gets selected */
1059 edit[0] = 0x00;
1060 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)"Jason2");
1061 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
1062 ok(strcmp(edit, "Jason2")==0, "Unexpected text retrieved %s\n", edit);
1064 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
1066 /* Now what is the selection */
1067 SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end);
1068 ok(start==0, "Unexpected start position for selection %d\n", start);
1069 ok(end==6, "Unexpected end position for selection %d\n", end);
1070 len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0);
1071 ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len));
1072 ok(HIWORD(len)==6, "Unexpected end position for selection %d\n", HIWORD(len));
1074 /* Now change the selection to the apparently invalid start -1, end -1 and
1075 show it means no selection (ie start -1) but cursor at end */
1076 SendMessageA(hCombo, CB_SETEDITSEL, 0, -1);
1077 edit[0] = 0x00;
1078 SendMessageA(hCombo, WM_CHAR, 'A', 0x1c0001);
1079 SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit);
1080 ok(strcmp(edit, "Jason2A")==0, "Unexpected text retrieved %s\n", edit);
1081 DestroyWindow(hCombo);
1084 static WNDPROC edit_window_proc;
1085 static long setsel_start = 1, setsel_end = 1;
1086 static HWND hCBN_SetFocus, hCBN_KillFocus;
1088 static LRESULT CALLBACK combobox_subclass_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1090 if (msg == EM_SETSEL)
1092 setsel_start = wParam;
1093 setsel_end = lParam;
1095 return CallWindowProcA(edit_window_proc, hwnd, msg, wParam, lParam);
1098 static LRESULT CALLBACK test_window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1100 switch (msg)
1102 case WM_COMMAND:
1103 switch (HIWORD(wParam))
1105 case CBN_SETFOCUS:
1106 hCBN_SetFocus = (HWND)lParam;
1107 break;
1108 case CBN_KILLFOCUS:
1109 hCBN_KillFocus = (HWND)lParam;
1110 break;
1112 break;
1113 case WM_NEXTDLGCTL:
1114 SetFocus((HWND)wParam);
1115 break;
1117 return CallWindowProcA(old_parent_proc, hwnd, msg, wParam, lParam);
1120 static void test_combo_editselection_focus(DWORD style)
1122 static const char wine_test[] = "Wine Test";
1123 HWND hCombo, hEdit, hButton;
1124 char buffer[16] = {0};
1125 COMBOBOXINFO cbInfo;
1126 DWORD len;
1128 hCombo = create_combobox(style);
1129 get_combobox_info(hCombo, &cbInfo);
1130 hEdit = cbInfo.hwndItem;
1132 hButton = CreateWindowA(WC_BUTTONA, "OK", WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,
1133 5, 50, 100, 20, hMainWnd, NULL,
1134 (HINSTANCE)GetWindowLongPtrA(hMainWnd, GWLP_HINSTANCE), NULL);
1136 old_parent_proc = (WNDPROC)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)test_window_proc);
1137 edit_window_proc = (WNDPROC)SetWindowLongPtrA(hEdit, GWLP_WNDPROC, (ULONG_PTR)combobox_subclass_proc);
1139 SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit);
1140 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
1141 todo_wine ok(setsel_end == INT_MAX, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
1142 ok(hCBN_SetFocus == hCombo, "Wrong handle set by CBN_SETFOCUS; got %p\n", hCBN_SetFocus);
1143 ok(GetFocus() == hEdit, "hEdit should have keyboard focus\n");
1145 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hButton, TRUE);
1146 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
1147 todo_wine ok(setsel_end == 0, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
1148 ok(hCBN_KillFocus == hCombo, "Wrong handle set by CBN_KILLFOCUS; got %p\n", hCBN_KillFocus);
1149 ok(GetFocus() == hButton, "hButton should have keyboard focus\n");
1151 SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)wine_test);
1152 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hCombo, TRUE);
1153 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
1154 todo_wine ok(setsel_end == INT_MAX, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
1155 ok(hCBN_SetFocus == hCombo, "Wrong handle set by CBN_SETFOCUS; got %p\n", hCBN_SetFocus);
1156 ok(GetFocus() == hEdit, "hEdit should have keyboard focus\n");
1157 SendMessageA(hCombo, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer);
1158 ok(!strcmp(buffer, wine_test), "Unexpected text in edit control; got '%s'\n", buffer);
1160 SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hButton, TRUE);
1161 ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start);
1162 todo_wine ok(setsel_end == 0, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end);
1163 ok(hCBN_KillFocus == hCombo, "Wrong handle set by CBN_KILLFOCUS; got %p\n", hCBN_KillFocus);
1164 ok(GetFocus() == hButton, "hButton should have keyboard focus\n");
1165 len = SendMessageA(hCombo, CB_GETEDITSEL, 0, 0);
1166 ok(len == 0, "Unexpected text selection; start: %u, end: %u\n", LOWORD(len), HIWORD(len));
1168 SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc);
1169 DestroyWindow(hButton);
1170 DestroyWindow(hCombo);
1173 static void test_combo_listbox_styles(DWORD cb_style)
1175 DWORD style, exstyle, expect_style, expect_exstyle;
1176 COMBOBOXINFO info;
1177 HWND combo;
1179 expect_style = WS_CHILD|WS_CLIPSIBLINGS|LBS_COMBOBOX|LBS_HASSTRINGS|LBS_NOTIFY;
1180 if (cb_style == CBS_SIMPLE)
1182 expect_style |= WS_VISIBLE;
1183 expect_exstyle = WS_EX_CLIENTEDGE;
1185 else
1187 expect_style |= WS_BORDER;
1188 expect_exstyle = WS_EX_TOOLWINDOW;
1191 combo = create_combobox(cb_style);
1192 get_combobox_info(combo, &info);
1194 style = GetWindowLongW( info.hwndList, GWL_STYLE );
1195 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE );
1196 ok(style == expect_style, "%08lx: got %08lx\n", cb_style, style);
1197 ok(exstyle == expect_exstyle, "%08lx: got %08lx\n", cb_style, exstyle);
1199 if (cb_style != CBS_SIMPLE)
1200 expect_exstyle |= WS_EX_TOPMOST;
1202 SendMessageW(combo, CB_SHOWDROPDOWN, TRUE, 0 );
1203 style = GetWindowLongW( info.hwndList, GWL_STYLE );
1204 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE );
1205 ok(style == (expect_style | WS_VISIBLE), "%08lx: got %08lx\n", cb_style, style);
1206 ok(exstyle == expect_exstyle, "%08lx: got %08lx\n", cb_style, exstyle);
1208 SendMessageW(combo, CB_SHOWDROPDOWN, FALSE, 0 );
1209 style = GetWindowLongW( info.hwndList, GWL_STYLE );
1210 exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE );
1211 ok(style == expect_style, "%08lx: got %08lx\n", cb_style, style);
1212 ok(exstyle == expect_exstyle, "%08lx: got %08lx\n", cb_style, exstyle);
1214 DestroyWindow(combo);
1217 static void test_combo_WS_VSCROLL(void)
1219 HWND hCombo, hList;
1220 COMBOBOXINFO info;
1221 DWORD style;
1222 int i;
1224 hCombo = create_combobox(CBS_DROPDOWNLIST);
1226 get_combobox_info(hCombo, &info);
1227 hList = info.hwndList;
1229 for (i = 0; i < 3; i++)
1231 char buffer[2];
1232 sprintf(buffer, "%d", i);
1233 SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)buffer);
1236 style = GetWindowLongA(info.hwndList, GWL_STYLE);
1237 SetWindowLongA(hList, GWL_STYLE, style | WS_VSCROLL);
1239 SendMessageA(hCombo, CB_SHOWDROPDOWN, TRUE, 0);
1240 SendMessageA(hCombo, CB_SHOWDROPDOWN, FALSE, 0);
1242 style = GetWindowLongA(hList, GWL_STYLE);
1243 ok((style & WS_VSCROLL) != 0, "Style does not include WS_VSCROLL\n");
1245 DestroyWindow(hCombo);
1248 static void test_combo_dropdown_size(DWORD style)
1250 static const char wine_test[] = "Wine Test";
1251 HWND hCombo, hList;
1252 COMBOBOXINFO cbInfo;
1253 int i, test, ret;
1255 static const struct list_size_info
1257 int num_items;
1258 int height_combo;
1259 int limit;
1260 } info_height[] = {
1261 {33, 50, -1},
1262 {35, 100, 40},
1263 {15, 50, 3},
1266 for (test = 0; test < ARRAY_SIZE(info_height); test++)
1268 const struct list_size_info *info_test = &info_height[test];
1269 int height_item; /* Height of a list item */
1270 int height_list; /* Height of the list we got */
1271 int expected_height_list;
1272 RECT rect_list_client;
1273 int min_visible_expected;
1275 winetest_push_context("Test %d", test);
1276 hCombo = CreateWindowA(WC_COMBOBOXA, "Combo", CBS_DROPDOWN | WS_VISIBLE | WS_CHILD | style, 5, 5, 100,
1277 info_test->height_combo, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
1279 min_visible_expected = SendMessageA(hCombo, CB_GETMINVISIBLE, 0, 0);
1280 ok(min_visible_expected == 30, "Unexpected number of items %d.\n", min_visible_expected);
1282 cbInfo.cbSize = sizeof(COMBOBOXINFO);
1283 ret = SendMessageA(hCombo, CB_GETCOMBOBOXINFO, 0, (LPARAM)&cbInfo);
1284 ok(ret, "Failed to get combo info, %d\n", ret);
1286 hList = cbInfo.hwndList;
1287 for (i = 0; i < info_test->num_items; i++)
1289 ret = SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM) wine_test);
1290 ok(ret == i, "Failed to add string %d, returned %d.\n", i, ret);
1293 if (info_test->limit != -1)
1295 int min_visible_actual;
1296 min_visible_expected = info_test->limit;
1298 ret = SendMessageA(hCombo, CB_SETMINVISIBLE, min_visible_expected, 0);
1299 ok(ret, "Failed to set visible limit.\n");
1300 min_visible_actual = SendMessageA(hCombo, CB_GETMINVISIBLE, 0, 0);
1301 ok(min_visible_expected == min_visible_actual, "unexpected number of items %d.\n",
1302 min_visible_actual);
1305 ret = SendMessageA(hCombo, CB_SHOWDROPDOWN, TRUE,0);
1306 ok(ret, "Failed to show dropdown.\n");
1307 ret = SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0);
1308 ok(ret, "Unexpected dropped state.\n");
1310 GetClientRect(hList, &rect_list_client);
1311 height_list = rect_list_client.bottom - rect_list_client.top;
1312 height_item = (int)SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0);
1314 if (style & CBS_NOINTEGRALHEIGHT)
1316 RECT rect_list_complete;
1317 int list_height_nonclient;
1318 int list_height_calculated;
1319 int edit_padding_size = cbInfo.rcItem.top; /* edit client rect top is the padding it has to its parent
1320 We assume it's the same on the bottom */
1322 GetWindowRect(hList, &rect_list_complete);
1324 list_height_nonclient = (rect_list_complete.bottom - rect_list_complete.top)
1325 - (rect_list_client.bottom - rect_list_client.top);
1327 /* Calculate the expected client size of the listbox popup from the size of the combobox. */
1328 list_height_calculated = info_test->height_combo /* Take height we created combobox with */
1329 - (cbInfo.rcItem.bottom - cbInfo.rcItem.top) /* Subtract size of edit control */
1330 - list_height_nonclient /* Subtract list nonclient area */
1331 - edit_padding_size * 2; /* subtract space around the edit control */
1333 expected_height_list = min(list_height_calculated, height_item * info_test->num_items);
1334 if (expected_height_list < 0)
1335 expected_height_list = 0;
1337 ok(expected_height_list == height_list, "expected list height to be %d, got %d\n",
1338 expected_height_list, height_list);
1340 else
1342 expected_height_list = min(info_test->num_items, min_visible_expected) * height_item;
1344 ok(expected_height_list == height_list, "expected list height to be %d, got %d\n",
1345 expected_height_list, height_list);
1348 DestroyWindow(hCombo);
1349 winetest_pop_context();
1353 static void test_combo_ctlcolor(void)
1355 static const int messages[] =
1357 WM_CTLCOLOR,
1358 WM_CTLCOLORMSGBOX,
1359 WM_CTLCOLOREDIT,
1360 WM_CTLCOLORLISTBOX,
1361 WM_CTLCOLORBTN,
1362 WM_CTLCOLORDLG,
1363 WM_CTLCOLORSCROLLBAR,
1364 WM_CTLCOLORSTATIC,
1367 HBRUSH brush, global_brush;
1368 COMBOBOXINFO info;
1369 unsigned int i;
1370 HWND combo;
1372 combo = create_combobox(CBS_DROPDOWN);
1373 ok(!!combo, "Failed to create combo window.\n");
1375 old_parent_proc = (void *)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)parent_wnd_proc);
1377 get_combobox_info(combo, &info);
1379 lparam_for_WM_CTLCOLOR = info.hwndItem;
1381 /* Parent returns valid brush handle. */
1382 for (i = 0; i < ARRAY_SIZE(messages); ++i)
1384 brush = (HBRUSH)SendMessageA(combo, messages[i], 0, (LPARAM)info.hwndItem);
1385 ok(brush == brush_red, "%u: unexpected brush %p, expected got %p.\n", i, brush, brush_red);
1388 /* Parent returns NULL brush. */
1389 global_brush = brush_red;
1390 brush_red = NULL;
1392 for (i = 0; i < ARRAY_SIZE(messages); ++i)
1394 brush = (HBRUSH)SendMessageA(combo, messages[i], 0, (LPARAM)info.hwndItem);
1395 ok(!brush, "%u: unexpected brush %p.\n", i, brush);
1398 brush_red = global_brush;
1400 lparam_for_WM_CTLCOLOR = 0;
1402 /* Parent does default processing. */
1403 for (i = 0; i < ARRAY_SIZE(messages); ++i)
1405 brush = (HBRUSH)SendMessageA(combo, messages[i], 0, (LPARAM)info.hwndItem);
1406 ok(!!brush && brush != brush_red, "%u: unexpected brush %p.\n", i, brush);
1409 SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc);
1410 DestroyWindow(combo);
1412 /* Combo without a parent. */
1413 combo = CreateWindowA(WC_COMBOBOXA, "Combo", CBS_DROPDOWN, 5, 5, 100, 100, NULL, NULL, NULL, 0);
1414 ok(!!combo, "Failed to create combo window.\n");
1416 get_combobox_info(combo, &info);
1418 for (i = 0; i < ARRAY_SIZE(messages); ++i)
1420 brush = (HBRUSH)SendMessageA(combo, messages[i], 0, (LPARAM)info.hwndItem);
1421 ok(!brush, "%u: unexpected brush %p.\n", i, brush);
1424 DestroyWindow(combo);
1427 static const struct message getdisp_parent_seq[] =
1429 { WM_NOTIFY, sent|id, 0, 0, CBEN_GETDISPINFOA },
1430 { 0 }
1433 static const struct message empty_seq[] =
1435 { 0 }
1438 static void test_comboex_CBEN_GETDISPINFO(void)
1440 static const unsigned int test_masks[] =
1442 CBEIF_TEXT,
1443 CBEIF_IMAGE,
1444 CBEIF_INDENT,
1445 CBEIF_OVERLAY,
1446 CBEIF_SELECTEDIMAGE,
1447 CBEIF_IMAGE | CBEIF_INDENT,
1449 COMBOBOXEXITEMA item;
1450 unsigned int i;
1451 HWND combo;
1452 DWORD res;
1454 combo = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN);
1455 ok(!!combo, "Failed to create control window.\n");
1457 /* All possible callback fields. */
1458 memset(&item, 0, sizeof(item));
1459 item.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_INDENT | CBEIF_OVERLAY | CBEIF_SELECTEDIMAGE;
1460 item.pszText = LPSTR_TEXTCALLBACKA;
1461 item.iImage = I_IMAGECALLBACK;
1462 item.iSelectedImage = I_IMAGECALLBACK;
1463 item.iOverlay = I_IMAGECALLBACK;
1464 item.iIndent = I_INDENTCALLBACK;
1466 res = SendMessageA(combo, CBEM_INSERTITEMA, 0, (LPARAM)&item);
1467 ok(!res, "Unexpected return value %lu.\n", res);
1469 for (i = 0; i < ARRAY_SIZE(test_masks); ++i)
1471 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1473 memset(&item, 0, sizeof(item));
1474 item.mask = test_masks[i];
1475 res = SendMessageA(combo, CBEM_GETITEMA, 0, (LPARAM)&item);
1476 ok(res == 1, "Unexpected return value %lu.\n", res);
1478 ok_sequence(sequences, PARENT_SEQ_INDEX, getdisp_parent_seq, "Get disp mask seq", TRUE);
1481 di_context.set_CBEIF_DI_SETITEM = TRUE;
1483 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1485 memset(&item, 0, sizeof(item));
1486 item.mask = CBEIF_IMAGE;
1487 di_context.mask = 0;
1488 res = SendMessageA(combo, CBEM_GETITEMA, 0, (LPARAM)&item);
1489 ok(res == 1, "Unexpected return value %lu.\n", res);
1490 todo_wine
1491 ok(di_context.mask == CBEIF_IMAGE, "Unexpected mask %#x.\n", di_context.mask);
1493 ok_sequence(sequences, PARENT_SEQ_INDEX, getdisp_parent_seq, "Get disp DI_SETITEM seq", TRUE);
1495 flush_sequences(sequences, NUM_MSG_SEQUENCES);
1497 memset(&item, 0, sizeof(item));
1498 item.mask = CBEIF_IMAGE;
1499 res = SendMessageA(combo, CBEM_GETITEMA, 0, (LPARAM)&item);
1500 ok(res == 1, "Unexpected return value %lu.\n", res);
1501 ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "Get disp after DI_SETITEM seq", FALSE);
1503 /* Request two fields, one was set. */
1504 memset(&item, 0, sizeof(item));
1505 item.mask = CBEIF_IMAGE | CBEIF_INDENT;
1506 di_context.mask = 0;
1507 res = SendMessageA(combo, CBEM_GETITEMA, 0, (LPARAM)&item);
1508 ok(res == 1, "Unexpected return value %lu.\n", res);
1509 todo_wine
1510 ok(di_context.mask == CBEIF_INDENT, "Unexpected mask %#x.\n", di_context.mask);
1512 di_context.set_CBEIF_DI_SETITEM = FALSE;
1514 DestroyWindow(combo);
1517 START_TEST(combo)
1519 ULONG_PTR ctx_cookie;
1520 HANDLE hCtx;
1522 init_functions();
1524 if (!init())
1525 return;
1527 init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
1529 /* ComboBoxEx32 tests. */
1530 test_comboex();
1531 test_comboex_WM_LBUTTONDOWN();
1532 test_comboex_CB_GETLBTEXT();
1533 test_comboex_WM_WINDOWPOSCHANGING();
1534 test_comboex_subclass();
1535 test_comboex_get_set_item();
1536 test_comboex_CBEN_GETDISPINFO();
1538 if (!load_v6_module(&ctx_cookie, &hCtx))
1540 cleanup();
1541 return;
1544 test_comboex();
1545 test_comboex_CB_GETLBTEXT();
1546 test_comboex_WM_WINDOWPOSCHANGING();
1547 test_comboex_get_set_item();
1548 test_comboex_CBEN_GETDISPINFO();
1550 /* ComboBox control tests. */
1551 test_combo_WS_VSCROLL();
1552 test_combo_setfont(CBS_DROPDOWN);
1553 test_combo_setfont(CBS_DROPDOWNLIST);
1554 test_combo_setitemheight(CBS_DROPDOWN);
1555 test_combo_setitemheight(CBS_DROPDOWNLIST);
1556 test_combo_CBN_SELCHANGE();
1557 test_combo_changesize(CBS_DROPDOWN);
1558 test_combo_changesize(CBS_DROPDOWNLIST);
1559 test_combo_editselection();
1560 test_combo_editselection_focus(CBS_SIMPLE);
1561 test_combo_editselection_focus(CBS_DROPDOWN);
1562 test_combo_listbox_styles(CBS_SIMPLE);
1563 test_combo_listbox_styles(CBS_DROPDOWN);
1564 test_combo_listbox_styles(CBS_DROPDOWNLIST);
1565 test_combo_dropdown_size(0);
1566 test_combo_dropdown_size(CBS_NOINTEGRALHEIGHT);
1567 test_combo_ctlcolor();
1569 cleanup();
1570 unload_v6_module(ctx_cookie, hCtx);