From: Amine Khaldi Date: Thu, 18 Jan 2018 22:49:50 +0000 (+0100) Subject: [COMCTL32_WINETEST] Sync with Wine 3.0. CORE-14225 X-Git-Tag: 0.4.9-dev~384 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=0d320bc25fb266bee81257975cfd81c883fbc69d [COMCTL32_WINETEST] Sync with Wine 3.0. CORE-14225 --- diff --git a/modules/rostests/winetests/comctl32/CMakeLists.txt b/modules/rostests/winetests/comctl32/CMakeLists.txt index f72f87a169a..9212bc0fe14 100644 --- a/modules/rostests/winetests/comctl32/CMakeLists.txt +++ b/modules/rostests/winetests/comctl32/CMakeLists.txt @@ -1,16 +1,18 @@ remove_definitions(-D_WIN32_WINNT=0x502 -D_WIN32_IE=0x600) -add_definitions(-DUSE_WINE_TODOS) +add_definitions(-DUSE_WINE_TODOS -DWINETEST_USE_DBGSTR_LONGLONG) list(APPEND SOURCE animate.c - comboex.c + combo.c datetime.c dpa.c + edit.c header.c imagelist.c ipaddress.c + listbox.c listview.c misc.c monthcal.c @@ -19,6 +21,7 @@ list(APPEND SOURCE progress.c propsheet.c rebar.c + static.c status.c syslink.c tab.c @@ -43,5 +46,10 @@ endif() set_module_type(comctl32_winetest win32cui) add_importlibs(comctl32_winetest comctl32 ole32 user32 gdi32 advapi32 msvcrt kernel32) + +if(MSVC) + add_importlibs(comctl32_winetest ntdll) +endif() + add_pch(comctl32_winetest precomp.h SOURCE) add_rostests_file(TARGET comctl32_winetest) diff --git a/modules/rostests/winetests/comctl32/combo.c b/modules/rostests/winetests/comctl32/combo.c new file mode 100644 index 00000000000..ca493c0cea0 --- /dev/null +++ b/modules/rostests/winetests/comctl32/combo.c @@ -0,0 +1,1194 @@ +/* Unit test suite for ComboBox and ComboBoxEx32 controls. + * + * Copyright 2005 Jason Edmeades + * Copyright 2007 Mikolaj Zalewski + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "precomp.h" + +#define EDITBOX_SEQ_INDEX 0 +#define NUM_MSG_SEQUENCES 1 + +#define EDITBOX_ID 0 +#define COMBO_ID 1995 + +#define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got) + +#define expect_rect(r, _left, _top, _right, _bottom) ok(r.left == _left && r.top == _top && \ + r.bottom == _bottom && r.right == _right, "Invalid rect %s vs (%d,%d)-(%d,%d)\n", \ + wine_dbgstr_rect(&r), _left, _top, _right, _bottom); + + +static struct msg_sequence *sequences[NUM_MSG_SEQUENCES]; + +static HWND hComboExParentWnd, hMainWnd; +static HINSTANCE hMainHinst; +static const char ComboExTestClass[] = "ComboExTestClass"; + +static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR); + +#define MAX_CHARS 100 +static char *textBuffer = NULL; + +static BOOL received_end_edit = FALSE; + +static void get_combobox_info(HWND hwnd, COMBOBOXINFO *info) +{ + BOOL ret; + + info->cbSize = sizeof(*info); + ret = GetComboBoxInfo(hwnd, info); + ok(ret, "Failed to get combobox info structure, error %d\n", GetLastError()); +} + +static HWND createComboEx(DWORD style) { + return CreateWindowExA(0, WC_COMBOBOXEXA, NULL, style, 0, 0, 300, 300, + hComboExParentWnd, NULL, hMainHinst, NULL); +} + +static LONG addItem(HWND cbex, int idx, const char *text) { + COMBOBOXEXITEMA cbexItem; + memset(&cbexItem, 0x00, sizeof(cbexItem)); + cbexItem.mask = CBEIF_TEXT; + cbexItem.iItem = idx; + cbexItem.pszText = (char*)text; + cbexItem.cchTextMax = 0; + return SendMessageA(cbex, CBEM_INSERTITEMA, 0, (LPARAM)&cbexItem); +} + +static LONG setItem(HWND cbex, int idx, const char *text) { + COMBOBOXEXITEMA cbexItem; + memset(&cbexItem, 0x00, sizeof(cbexItem)); + cbexItem.mask = CBEIF_TEXT; + cbexItem.iItem = idx; + cbexItem.pszText = (char*)text; + cbexItem.cchTextMax = 0; + return SendMessageA(cbex, CBEM_SETITEMA, 0, (LPARAM)&cbexItem); +} + +static LONG delItem(HWND cbex, int idx) { + return SendMessageA(cbex, CBEM_DELETEITEM, idx, 0); +} + +static LONG getItem(HWND cbex, int idx, COMBOBOXEXITEMA *cbItem) { + memset(cbItem, 0x00, sizeof(COMBOBOXEXITEMA)); + cbItem->mask = CBEIF_TEXT; + cbItem->pszText = textBuffer; + cbItem->iItem = idx; + cbItem->cchTextMax = 100; + return SendMessageA(cbex, CBEM_GETITEMA, 0, (LPARAM)cbItem); +} + +static LRESULT WINAPI editbox_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA); + static LONG defwndproc_counter = 0; + struct message msg = { 0 }; + LRESULT ret; + + msg.message = message; + msg.flags = sent|wparam|lparam; + if (defwndproc_counter) msg.flags |= defwinproc; + msg.wParam = wParam; + msg.lParam = lParam; + msg.id = EDITBOX_ID; + + if (message != WM_PAINT && + message != WM_ERASEBKGND && + message != WM_NCPAINT && + message != WM_NCHITTEST && + message != WM_GETTEXT && + message != WM_GETICON && + message != WM_DEVICECHANGE) + { + add_message(sequences, EDITBOX_SEQ_INDEX, &msg); + } + + defwndproc_counter++; + ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam); + defwndproc_counter--; + return ret; +} + +static HWND subclass_editbox(HWND hwndComboEx) +{ + WNDPROC oldproc; + HWND hwnd; + + hwnd = (HWND)SendMessageA(hwndComboEx, CBEM_GETEDITCONTROL, 0, 0); + oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC, + (LONG_PTR)editbox_subclass_proc); + SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc); + + return hwnd; +} + +static void test_comboex(void) +{ + HWND myHwnd = 0; + LONG res; + COMBOBOXEXITEMA cbexItem; + static const char *first_item = "First Item", + *second_item = "Second Item", + *third_item = "Third Item", + *middle_item = "Between First and Second Items", + *replacement_item = "Between First and Second Items", + *out_of_range_item = "Out of Range Item"; + + /* Allocate space for result */ + textBuffer = HeapAlloc(GetProcessHeap(), 0, MAX_CHARS); + + /* Basic comboboxex test */ + myHwnd = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN); + + /* Add items onto the end of the combobox */ + res = addItem(myHwnd, -1, first_item); + ok(res == 0, "Adding simple item failed (%d)\n", res); + res = addItem(myHwnd, -1, second_item); + ok(res == 1, "Adding simple item failed (%d)\n", res); + res = addItem(myHwnd, 2, third_item); + ok(res == 2, "Adding simple item failed (%d)\n", res); + res = addItem(myHwnd, 1, middle_item); + ok(res == 1, "Inserting simple item failed (%d)\n", res); + + /* Add an item completely out of range */ + res = addItem(myHwnd, 99, out_of_range_item); + ok(res == -1, "Adding using out of range index worked unexpectedly (%d)\n", res); + res = addItem(myHwnd, 5, out_of_range_item); + ok(res == -1, "Adding using out of range index worked unexpectedly (%d)\n", res); + /* Removed: Causes traps on Windows XP + res = addItem(myHwnd, -2, "Out Of Range Item"); + ok(res == -1, "Adding out of range worked unexpectedly (%ld)\n", res); + */ + + /* Get an item completely out of range */ + res = getItem(myHwnd, 99, &cbexItem); + ok(res == 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res, cbexItem.pszText); + res = getItem(myHwnd, 4, &cbexItem); + ok(res == 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res, cbexItem.pszText); + res = getItem(myHwnd, -2, &cbexItem); + ok(res == 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res, cbexItem.pszText); + + /* Get an item in range */ + res = getItem(myHwnd, 0, &cbexItem); + ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res); + ok(strcmp(first_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText); + + res = getItem(myHwnd, 1, &cbexItem); + ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res); + ok(strcmp(middle_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText); + + res = getItem(myHwnd, 2, &cbexItem); + ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res); + ok(strcmp(second_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText); + + res = getItem(myHwnd, 3, &cbexItem); + ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res); + ok(strcmp(third_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText); + + /* Set an item completely out of range */ + res = setItem(myHwnd, 99, replacement_item); + ok(res == 0, "Setting item using out of range index worked unexpectedly (%d)\n", res); + res = setItem(myHwnd, 4, replacement_item); + ok(res == 0, "Setting item using out of range index worked unexpectedly (%d)\n", res); + res = setItem(myHwnd, -2, replacement_item); + ok(res == 0, "Setting item using out of range index worked unexpectedly (%d)\n", res); + + /* Set an item in range */ + res = setItem(myHwnd, 0, replacement_item); + ok(res != 0, "Setting first item failed (%d)\n", res); + res = setItem(myHwnd, 3, replacement_item); + ok(res != 0, "Setting last item failed (%d)\n", res); + + /* Remove items completely out of range (4 items in control at this point) */ + res = delItem(myHwnd, -1); + ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%d)\n", res); + res = delItem(myHwnd, 4); + ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%d)\n", res); + + /* Remove items in range (4 items in control at this point) */ + res = delItem(myHwnd, 3); + ok(res == 3, "Deleting using out of range index failed (%d)\n", res); + res = delItem(myHwnd, 0); + ok(res == 2, "Deleting using out of range index failed (%d)\n", res); + res = delItem(myHwnd, 0); + ok(res == 1, "Deleting using out of range index failed (%d)\n", res); + res = delItem(myHwnd, 0); + ok(res == 0, "Deleting using out of range index failed (%d)\n", res); + + /* Remove from an empty box */ + res = delItem(myHwnd, 0); + ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%d)\n", res); + + + /* Cleanup */ + HeapFree(GetProcessHeap(), 0, textBuffer); + DestroyWindow(myHwnd); +} + +static void test_comboex_WM_LBUTTONDOWN(void) +{ + HWND hComboEx, hCombo, hEdit, hList; + COMBOBOXINFO cbInfo; + UINT x, y, item_height; + LRESULT result; + UINT i; + int idx; + RECT rect; + WCHAR buffer[3]; + static const UINT choices[] = {8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72}; + static const WCHAR stringFormat[] = {'%','2','d','\0'}; + + hComboEx = CreateWindowExA(0, WC_COMBOBOXEXA, NULL, + WS_VISIBLE|WS_CHILD|CBS_DROPDOWN, 0, 0, 200, 150, + hComboExParentWnd, NULL, hMainHinst, NULL); + + for (i = 0; i < sizeof(choices)/sizeof(UINT); i++){ + COMBOBOXEXITEMW cbexItem; + wsprintfW(buffer, stringFormat, choices[i]); + + memset(&cbexItem, 0x00, sizeof(cbexItem)); + cbexItem.mask = CBEIF_TEXT; + cbexItem.iItem = i; + cbexItem.pszText = buffer; + cbexItem.cchTextMax = 0; + ok(SendMessageW(hComboEx, CBEM_INSERTITEMW, 0, (LPARAM)&cbexItem) >= 0, + "Failed to add item %d\n", i); + } + + hCombo = (HWND)SendMessageA(hComboEx, CBEM_GETCOMBOCONTROL, 0, 0); + hEdit = (HWND)SendMessageA(hComboEx, CBEM_GETEDITCONTROL, 0, 0); + + get_combobox_info(hCombo, &cbInfo); + hList = cbInfo.hwndList; + + ok(GetFocus() == hComboExParentWnd, + "Focus not on Main Window, instead on %p\n", GetFocus()); + + /* Click on the button to drop down the list */ + x = cbInfo.rcButton.left + (cbInfo.rcButton.right-cbInfo.rcButton.left)/2; + y = cbInfo.rcButton.top + (cbInfo.rcButton.bottom-cbInfo.rcButton.top)/2; + result = SendMessageA(hCombo, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y)); + ok(result, "WM_LBUTTONDOWN was not processed. LastError=%d\n", + GetLastError()); + ok(GetFocus() == hCombo || + broken(GetFocus() != hCombo), /* win98 */ + "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n", + GetFocus()); + ok(SendMessageA(hComboEx, CB_GETDROPPEDSTATE, 0, 0), + "The dropdown list should have appeared after clicking the button.\n"); + idx = SendMessageA(hCombo, CB_GETTOPINDEX, 0, 0); + ok(idx == 0, "For TopIndex expected %d, got %d\n", 0, idx); + + result = SendMessageA(hCombo, WM_LBUTTONUP, 0, MAKELPARAM(x, y)); + ok(result, "WM_LBUTTONUP was not processed. LastError=%d\n", + GetLastError()); + ok(GetFocus() == hCombo || + broken(GetFocus() != hCombo), /* win98 */ + "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n", + GetFocus()); + + /* Click on the 5th item in the list */ + item_height = SendMessageA(hCombo, CB_GETITEMHEIGHT, 0, 0); + ok(GetClientRect(hList, &rect), "Failed to get list's client rect.\n"); + x = rect.left + (rect.right-rect.left)/2; + y = item_height/2 + item_height*4; + result = SendMessageA(hList, WM_MOUSEMOVE, 0, MAKELPARAM(x, y)); + ok(!result, "WM_MOUSEMOVE was not processed. LastError=%d\n", + GetLastError()); + ok(GetFocus() == hCombo || + broken(GetFocus() != hCombo), /* win98 */ + "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n", + GetFocus()); + + result = SendMessageA(hList, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y)); + ok(!result, "WM_LBUTTONDOWN was not processed. LastError=%d\n", + GetLastError()); + ok(GetFocus() == hCombo || + broken(GetFocus() != hCombo), /* win98 */ + "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n", + GetFocus()); + ok(SendMessageA(hComboEx, CB_GETDROPPEDSTATE, 0, 0), + "The dropdown list should still be visible.\n"); + + result = SendMessageA(hList, WM_LBUTTONUP, 0, MAKELPARAM(x, y)); + ok(!result, "WM_LBUTTONUP was not processed. LastError=%d\n", + GetLastError()); + todo_wine ok(GetFocus() == hEdit || + broken(GetFocus() == hCombo), /* win98 */ + "Focus not on ComboBoxEx's Edit Control, instead on %p\n", + GetFocus()); + + result = SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0); + ok(!result || + broken(result != 0), /* win98 */ + "The dropdown list should have been rolled up.\n"); + idx = SendMessageA(hComboEx, CB_GETCURSEL, 0, 0); + ok(idx == 4 || + broken(idx == -1), /* win98 */ + "Current Selection: expected %d, got %d\n", 4, idx); + ok(received_end_edit, "Expected to receive a CBEN_ENDEDIT message\n"); + + SetFocus( hComboExParentWnd ); + ok( GetFocus() == hComboExParentWnd, "got %p\n", GetFocus() ); + SetFocus( hComboEx ); + ok( GetFocus() == hEdit, "got %p\n", GetFocus() ); + + DestroyWindow(hComboEx); +} + +static void test_comboex_CB_GETLBTEXT(void) +{ + HWND hCombo; + CHAR buff[1]; + COMBOBOXEXITEMA item; + LRESULT ret; + + hCombo = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN); + + /* set text to null */ + addItem(hCombo, 0, NULL); + + buff[0] = 'a'; + item.mask = CBEIF_TEXT; + item.iItem = 0; + item.pszText = buff; + item.cchTextMax = 1; + ret = SendMessageA(hCombo, CBEM_GETITEMA, 0, (LPARAM)&item); + ok(ret != 0, "CBEM_GETITEM failed\n"); + ok(buff[0] == 0, "\n"); + + ret = SendMessageA(hCombo, CB_GETLBTEXTLEN, 0, 0); + ok(ret == 0, "Expected zero length\n"); + + ret = SendMessageA(hCombo, CB_GETLBTEXTLEN, 0, 0); + ok(ret == 0, "Expected zero length\n"); + + buff[0] = 'a'; + ret = SendMessageA(hCombo, CB_GETLBTEXT, 0, (LPARAM)buff); + ok(ret == 0, "Expected zero length\n"); + ok(buff[0] == 0, "Expected null terminator as a string, got %s\n", buff); + + DestroyWindow(hCombo); +} + +static void test_comboex_WM_WINDOWPOSCHANGING(void) +{ + HWND hCombo; + WINDOWPOS wp; + RECT rect; + int combo_height; + int ret; + + hCombo = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN); + ok(hCombo != NULL, "createComboEx failed\n"); + ret = GetWindowRect(hCombo, &rect); + ok(ret, "GetWindowRect failed\n"); + combo_height = rect.bottom - rect.top; + ok(combo_height > 0, "wrong combo height\n"); + + /* Test height > combo_height */ + wp.x = rect.left; + wp.y = rect.top; + wp.cx = (rect.right - rect.left); + wp.cy = combo_height * 2; + wp.flags = 0; + wp.hwnd = hCombo; + wp.hwndInsertAfter = NULL; + + ret = SendMessageA(hCombo, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp); + ok(ret == 0, "expected 0, got %x\n", ret); + ok(wp.cy == combo_height, + "Expected height %d, got %d\n", combo_height, wp.cy); + + /* Test height < combo_height */ + wp.x = rect.left; + wp.y = rect.top; + wp.cx = (rect.right - rect.left); + wp.cy = combo_height / 2; + wp.flags = 0; + wp.hwnd = hCombo; + wp.hwndInsertAfter = NULL; + + ret = SendMessageA(hCombo, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp); + ok(ret == 0, "expected 0, got %x\n", ret); + ok(wp.cy == combo_height, + "Expected height %d, got %d\n", combo_height, wp.cy); + + ret = DestroyWindow(hCombo); + ok(ret, "DestroyWindow failed\n"); +} + +static LRESULT ComboExTestOnNotify(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + NMHDR *hdr = (NMHDR*)lParam; + switch(hdr->code){ + case CBEN_ENDEDITA: + { + NMCBEENDEDITA *edit_info = (NMCBEENDEDITA*)hdr; + if(edit_info->iWhy==CBENF_DROPDOWN){ + received_end_edit = TRUE; + } + break; + } + case CBEN_ENDEDITW: + { + NMCBEENDEDITW *edit_info = (NMCBEENDEDITW*)hdr; + if(edit_info->iWhy==CBENF_DROPDOWN){ + received_end_edit = TRUE; + } + break; + } + } + return 0; +} + +static LRESULT CALLBACK ComboExTestWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch(msg) { + + case WM_DESTROY: + PostQuitMessage(0); + break; + case WM_NOTIFY: + return ComboExTestOnNotify(hWnd,msg,wParam,lParam); + default: + return DefWindowProcA(hWnd, msg, wParam, lParam); + } + + return 0L; +} + +static BOOL init(void) +{ + HMODULE hComctl32; + BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*); + WNDCLASSA wc; + INITCOMMONCONTROLSEX iccex; + + hComctl32 = GetModuleHandleA("comctl32.dll"); + pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx"); + if (!pInitCommonControlsEx) + { + win_skip("InitCommonControlsEx() is missing. Skipping the tests\n"); + return FALSE; + } + iccex.dwSize = sizeof(iccex); + iccex.dwICC = ICC_USEREX_CLASSES; + pInitCommonControlsEx(&iccex); + + pSetWindowSubclass = (void*)GetProcAddress(hComctl32, (LPSTR)410); + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = GetModuleHandleA(NULL); + wc.hIcon = NULL; + wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW); + wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW); + wc.lpszMenuName = NULL; + wc.lpszClassName = ComboExTestClass; + wc.lpfnWndProc = ComboExTestWndProc; + RegisterClassA(&wc); + + hMainWnd = CreateWindowA("static", "Test", WS_OVERLAPPEDWINDOW, 10, 10, 300, 300, NULL, NULL, NULL, 0); + ShowWindow(hMainWnd, SW_SHOW); + + hComboExParentWnd = CreateWindowExA(0, ComboExTestClass, "ComboEx test", WS_OVERLAPPEDWINDOW|WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, GetModuleHandleA(NULL), 0); + ok(hComboExParentWnd != NULL, "failed to create parent window\n"); + + hMainHinst = GetModuleHandleA(NULL); + + return hComboExParentWnd != NULL; +} + +static void cleanup(void) +{ + MSG msg; + + PostMessageA(hComboExParentWnd, WM_CLOSE, 0, 0); + while (GetMessageA(&msg,0,0,0)) { + TranslateMessage(&msg); + DispatchMessageA(&msg); + } + + DestroyWindow(hComboExParentWnd); + UnregisterClassA(ComboExTestClass, GetModuleHandleA(NULL)); + + DestroyWindow(hMainWnd); +} + +static void test_comboex_subclass(void) +{ + HWND hComboEx, hCombo, hEdit; + + hComboEx = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN); + + hCombo = (HWND)SendMessageA(hComboEx, CBEM_GETCOMBOCONTROL, 0, 0); + ok(hCombo != NULL, "Failed to get internal combo\n"); + hEdit = (HWND)SendMessageA(hComboEx, CBEM_GETEDITCONTROL, 0, 0); + ok(hEdit != NULL, "Failed to get internal edit\n"); + + if (pSetWindowSubclass) + { + ok(GetPropA(hCombo, "CC32SubclassInfo") != NULL, "Expected CC32SubclassInfo property\n"); + ok(GetPropA(hEdit, "CC32SubclassInfo") != NULL, "Expected CC32SubclassInfo property\n"); + } + + DestroyWindow(hComboEx); +} + +static const struct message test_setitem_edit_seq[] = { + { WM_SETTEXT, sent|id, 0, 0, EDITBOX_ID }, + { EM_SETSEL, sent|id|wparam|lparam, 0, 0, EDITBOX_ID }, + { EM_SETSEL, sent|id|wparam|lparam, 0, -1, EDITBOX_ID }, + { 0 } +}; + +static void test_comboex_get_set_item(void) +{ + char textA[] = "test"; + HWND hComboEx; + COMBOBOXEXITEMA item; + BOOL ret; + + hComboEx = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN); + + subclass_editbox(hComboEx); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + memset(&item, 0, sizeof(item)); + item.mask = CBEIF_TEXT; + item.pszText = textA; + item.iItem = -1; + ret = SendMessageA(hComboEx, CBEM_SETITEMA, 0, (LPARAM)&item); + expect(TRUE, ret); + + ok_sequence(sequences, EDITBOX_SEQ_INDEX, test_setitem_edit_seq, "set item data for edit", FALSE); + + /* get/set lParam */ + item.mask = CBEIF_LPARAM; + item.iItem = -1; + item.lParam = 0xdeadbeef; + ret = SendMessageA(hComboEx, CBEM_GETITEMA, 0, (LPARAM)&item); + expect(TRUE, ret); + ok(item.lParam == 0, "Expected zero, got %lx\n", item.lParam); + + item.lParam = 0x1abe11ed; + ret = SendMessageA(hComboEx, CBEM_SETITEMA, 0, (LPARAM)&item); + expect(TRUE, ret); + + item.lParam = 0; + ret = SendMessageA(hComboEx, CBEM_GETITEMA, 0, (LPARAM)&item); + expect(TRUE, ret); + ok(item.lParam == 0x1abe11ed, "Expected 0x1abe11ed, got %lx\n", item.lParam); + + DestroyWindow(hComboEx); +} + +static HWND create_combobox(DWORD style) +{ + return CreateWindowA("ComboBox", "Combo", WS_VISIBLE|WS_CHILD|style, 5, 5, 100, 100, hMainWnd, (HMENU)COMBO_ID, NULL, 0); +} + +static int font_height(HFONT hFont) +{ + TEXTMETRICA tm; + HFONT hFontOld; + HDC hDC; + + hDC = CreateCompatibleDC(NULL); + hFontOld = SelectObject(hDC, hFont); + GetTextMetricsA(hDC, &tm); + SelectObject(hDC, hFontOld); + DeleteDC(hDC); + + return tm.tmHeight; +} + +static void test_combo_setitemheight(DWORD style) +{ + HWND hCombo = create_combobox(style); + RECT r; + int i; + + GetClientRect(hCombo, &r); + expect_rect(r, 0, 0, 100, font_height(GetStockObject(SYSTEM_FONT)) + 8); + SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); + MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); + todo_wine expect_rect(r, 5, 5, 105, 105); + + for (i = 1; i < 30; i++) + { + SendMessageA(hCombo, CB_SETITEMHEIGHT, -1, i); + GetClientRect(hCombo, &r); + ok((r.bottom - r.top) == (i + 6), "Unexpected client rect height.\n"); + } + + DestroyWindow(hCombo); +} + +static void test_combo_setfont(DWORD style) +{ + HFONT hFont1, hFont2; + HWND hCombo; + RECT r; + int i; + + hCombo = create_combobox(style); + 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"); + 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"); + + GetClientRect(hCombo, &r); + expect_rect(r, 0, 0, 100, font_height(GetStockObject(SYSTEM_FONT)) + 8); + SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); + MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); + todo_wine expect_rect(r, 5, 5, 105, 105); + + /* The size of the dropped control is initially equal to the size + of the window when it was created. The size of the calculated + dropped area changes only by how much the selection area + changes, not by how much the list area changes. */ + if (font_height(hFont1) == 10 && font_height(hFont2) == 8) + { + SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE); + GetClientRect(hCombo, &r); + expect_rect(r, 0, 0, 100, 18); + SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); + MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); + todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont1))); + + SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont2, FALSE); + GetClientRect(hCombo, &r); + expect_rect(r, 0, 0, 100, 16); + SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); + MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); + todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont2))); + + SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE); + GetClientRect(hCombo, &r); + expect_rect(r, 0, 0, 100, 18); + SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r); + MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2); + todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont1))); + } + else + { + ok(0, "Expected Marlett font heights 10/8, got %d/%d\n", + font_height(hFont1), font_height(hFont2)); + } + + for (i = 1; i < 30; i++) + { + 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"); + int height = font_height(hFont); + + SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont, FALSE); + GetClientRect(hCombo, &r); + ok((r.bottom - r.top) == (height + 8), "Unexpected client rect height.\n"); + SendMessageA(hCombo, WM_SETFONT, 0, FALSE); + DeleteObject(hFont); + } + + DestroyWindow(hCombo); + DeleteObject(hFont1); + DeleteObject(hFont2); +} + +static LRESULT (CALLBACK *old_parent_proc)(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); +static LPCSTR expected_edit_text; +static LPCSTR expected_list_text; +static BOOL selchange_fired; + +static LRESULT CALLBACK parent_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_COMMAND: + switch (wparam) + { + case MAKEWPARAM(COMBO_ID, CBN_SELCHANGE): + { + HWND hCombo = (HWND)lparam; + char list[20], edit[20]; + int idx; + + memset(list, 0, sizeof(list)); + memset(edit, 0, sizeof(edit)); + + idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0); + SendMessageA(hCombo, CB_GETLBTEXT, idx, (LPARAM)list); + SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit); + + ok(!strcmp(edit, expected_edit_text), "edit: got %s, expected %s\n", + edit, expected_edit_text); + ok(!strcmp(list, expected_list_text), "list: got %s, expected %s\n", + list, expected_list_text); + + selchange_fired = TRUE; + } + break; + } + break; + } + + return CallWindowProcA(old_parent_proc, hwnd, msg, wparam, lparam); +} + +static void test_selection(DWORD style, const char * const text[], const int *edit, const int *list) +{ + HWND hCombo; + INT idx; + + hCombo = create_combobox(style); + + SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)text[0]); + SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)text[1]); + SendMessageA(hCombo, CB_SETCURSEL, -1, 0); + + old_parent_proc = (void *)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)parent_wnd_proc); + + idx = SendMessageA(hCombo, CB_GETCURSEL, 0, 0); + ok(idx == -1, "expected selection -1, got %d\n", idx); + + /* keyboard navigation */ + + expected_list_text = text[list[0]]; + expected_edit_text = text[edit[0]]; + selchange_fired = FALSE; + SendMessageA(hCombo, WM_KEYDOWN, VK_DOWN, 0); + ok(selchange_fired, "CBN_SELCHANGE not sent!\n"); + + expected_list_text = text[list[1]]; + expected_edit_text = text[edit[1]]; + selchange_fired = FALSE; + SendMessageA(hCombo, WM_KEYDOWN, VK_DOWN, 0); + ok(selchange_fired, "CBN_SELCHANGE not sent!\n"); + + expected_list_text = text[list[2]]; + expected_edit_text = text[edit[2]]; + selchange_fired = FALSE; + SendMessageA(hCombo, WM_KEYDOWN, VK_UP, 0); + ok(selchange_fired, "CBN_SELCHANGE not sent!\n"); + + /* programmatic navigation */ + + expected_list_text = text[list[3]]; + expected_edit_text = text[edit[3]]; + selchange_fired = FALSE; + SendMessageA(hCombo, CB_SETCURSEL, list[3], 0); + ok(!selchange_fired, "CBN_SELCHANGE sent!\n"); + + expected_list_text = text[list[4]]; + expected_edit_text = text[edit[4]]; + selchange_fired = FALSE; + SendMessageA(hCombo, CB_SETCURSEL, list[4], 0); + ok(!selchange_fired, "CBN_SELCHANGE sent!\n"); + + SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc); + DestroyWindow(hCombo); +} + +static void test_combo_CBN_SELCHANGE(void) +{ + static const char * const text[] = { "alpha", "beta", "" }; + static const int sel_1[] = { 2, 0, 1, 0, 1 }; + static const int sel_2[] = { 0, 1, 0, 0, 1 }; + + test_selection(CBS_SIMPLE, text, sel_1, sel_2); + test_selection(CBS_DROPDOWN, text, sel_1, sel_2); + test_selection(CBS_DROPDOWNLIST, text, sel_2, sel_2); +} + +static void test_combo_changesize(DWORD style) +{ + INT ddheight, clheight, ddwidth, clwidth; + HWND hCombo; + RECT rc; + + hCombo = create_combobox(style); + + /* get initial measurements */ + GetClientRect( hCombo, &rc); + clheight = rc.bottom - rc.top; + clwidth = rc.right - rc.left; + SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc); + ddheight = rc.bottom - rc.top; + ddwidth = rc.right - rc.left; + /* use MoveWindow to move & resize the combo */ + /* first make it slightly smaller */ + MoveWindow( hCombo, 10, 10, clwidth - 2, clheight - 2, TRUE); + GetClientRect( hCombo, &rc); + ok( rc.right - rc.left == clwidth - 2, "clientrect width is %d vs %d\n", + rc.right - rc.left, clwidth - 2); + ok( rc.bottom - rc.top == clheight, "clientrect height is %d vs %d\n", + rc.bottom - rc.top, clheight); + SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc); + ok( rc.right - rc.left == clwidth - 2, "drop-down rect width is %d vs %d\n", + rc.right - rc.left, clwidth - 2); + ok( rc.bottom - rc.top == ddheight, "drop-down rect height is %d vs %d\n", + rc.bottom - rc.top, ddheight); + ok( rc.right - rc.left == ddwidth -2, "drop-down rect width is %d vs %d\n", + rc.right - rc.left, ddwidth - 2); + /* new cx, cy is slightly bigger than the initial values */ + MoveWindow( hCombo, 10, 10, clwidth + 2, clheight + 2, TRUE); + GetClientRect( hCombo, &rc); + ok( rc.right - rc.left == clwidth + 2, "clientrect width is %d vs %d\n", + rc.right - rc.left, clwidth + 2); + ok( rc.bottom - rc.top == clheight, "clientrect height is %d vs %d\n", + rc.bottom - rc.top, clheight); + SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc); + ok( rc.right - rc.left == clwidth + 2, "drop-down rect width is %d vs %d\n", + rc.right - rc.left, clwidth + 2); + todo_wine { + ok( rc.bottom - rc.top == clheight + 2, "drop-down rect height is %d vs %d\n", + rc.bottom - rc.top, clheight + 2); + } + + ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, -1, 0); + ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); + ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); + ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); + + ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 0, 0); + ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); + ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); + ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); + + ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, clwidth - 1, 0); + ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); + ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); + ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); + + ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, clwidth << 1, 0); + ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1); + ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); + ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1); + + ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 0, 0); + ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1); + ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); + ok( ddwidth == (clwidth << 1), "drop-width is %d vs %d\n", ddwidth, clwidth << 1); + + ddwidth = SendMessageA(hCombo, CB_SETDROPPEDWIDTH, 1, 0); + ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); + ddwidth = SendMessageA(hCombo, CB_GETDROPPEDWIDTH, 0, 0); + ok( ddwidth == clwidth + 2, "drop-width is %d vs %d\n", ddwidth, clwidth + 2); + + DestroyWindow(hCombo); +} + +static void test_combo_editselection(void) +{ + COMBOBOXINFO cbInfo; + INT start, end; + char edit[20]; + HWND hCombo; + HWND hEdit; + DWORD len; + + /* Build a combo */ + hCombo = create_combobox(CBS_SIMPLE); + + get_combobox_info(hCombo, &cbInfo); + hEdit = cbInfo.hwndItem; + + /* Initially combo selection is empty*/ + len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); + ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len)); + ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len)); + + /* Set some text, and press a key to replace it */ + edit[0] = 0x00; + SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)"Jason1"); + SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit); + ok(strcmp(edit, "Jason1")==0, "Unexpected text retrieved %s\n", edit); + + /* Now what is the selection - still empty */ + SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end); + ok(start==0, "Unexpected start position for selection %d\n", start); + ok(end==0, "Unexpected end position for selection %d\n", end); + len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); + ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len)); + ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len)); + + /* Give it focus, and it gets selected */ + SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit); + SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end); + ok(start==0, "Unexpected start position for selection %d\n", start); + ok(end==6, "Unexpected end position for selection %d\n", end); + len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); + ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len)); + ok(HIWORD(len)==6, "Unexpected end position for selection %d\n", HIWORD(len)); + + /* Now emulate a key press */ + edit[0] = 0x00; + SendMessageA(hCombo, WM_CHAR, 'A', 0x1c0001); + SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit); + ok(strcmp(edit, "A")==0, "Unexpected text retrieved %s\n", edit); + + len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); + ok(LOWORD(len)==1, "Unexpected start position for selection %d\n", LOWORD(len)); + ok(HIWORD(len)==1, "Unexpected end position for selection %d\n", HIWORD(len)); + + /* Now what happens when it gets more focus a second time - it doesn't reselect */ + SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit); + len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); + ok(LOWORD(len)==1, "Unexpected start position for selection %d\n", LOWORD(len)); + ok(HIWORD(len)==1, "Unexpected end position for selection %d\n", HIWORD(len)); + DestroyWindow(hCombo); + + /* Start again - Build a combo */ + hCombo = create_combobox(CBS_SIMPLE); + get_combobox_info(hCombo, &cbInfo); + hEdit = cbInfo.hwndItem; + + /* Set some text and give focus so it gets selected */ + edit[0] = 0x00; + SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)"Jason2"); + SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit); + ok(strcmp(edit, "Jason2")==0, "Unexpected text retrieved %s\n", edit); + + SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit); + + /* Now what is the selection */ + SendMessageA(hCombo, CB_GETEDITSEL, (WPARAM)&start, (WPARAM)&end); + ok(start==0, "Unexpected start position for selection %d\n", start); + ok(end==6, "Unexpected end position for selection %d\n", end); + len = SendMessageA(hCombo, CB_GETEDITSEL, 0,0); + ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len)); + ok(HIWORD(len)==6, "Unexpected end position for selection %d\n", HIWORD(len)); + + /* Now change the selection to the apparently invalid start -1, end -1 and + show it means no selection (ie start -1) but cursor at end */ + SendMessageA(hCombo, CB_SETEDITSEL, 0, -1); + edit[0] = 0x00; + SendMessageA(hCombo, WM_CHAR, 'A', 0x1c0001); + SendMessageA(hCombo, WM_GETTEXT, sizeof(edit), (LPARAM)edit); + ok(strcmp(edit, "Jason2A")==0, "Unexpected text retrieved %s\n", edit); + DestroyWindow(hCombo); +} + +static WNDPROC edit_window_proc; +static long setsel_start = 1, setsel_end = 1; +static HWND hCBN_SetFocus, hCBN_KillFocus; + +static LRESULT CALLBACK combobox_subclass_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == EM_SETSEL) + { + setsel_start = wParam; + setsel_end = lParam; + } + return CallWindowProcA(edit_window_proc, hwnd, msg, wParam, lParam); +} + +static LRESULT CALLBACK test_window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_COMMAND: + switch (HIWORD(wParam)) + { + case CBN_SETFOCUS: + hCBN_SetFocus = (HWND)lParam; + break; + case CBN_KILLFOCUS: + hCBN_KillFocus = (HWND)lParam; + break; + } + break; + case WM_NEXTDLGCTL: + SetFocus((HWND)wParam); + break; + } + return CallWindowProcA(old_parent_proc, hwnd, msg, wParam, lParam); +} + +static void test_combo_editselection_focus(DWORD style) +{ + static const char wine_test[] = "Wine Test"; + HWND hCombo, hEdit, hButton; + char buffer[16] = {0}; + COMBOBOXINFO cbInfo; + DWORD len; + + hCombo = create_combobox(style); + get_combobox_info(hCombo, &cbInfo); + hEdit = cbInfo.hwndItem; + + hButton = CreateWindowA("Button", "OK", WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON, + 5, 50, 100, 20, hMainWnd, NULL, + (HINSTANCE)GetWindowLongPtrA(hMainWnd, GWLP_HINSTANCE), NULL); + + old_parent_proc = (WNDPROC)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)test_window_proc); + edit_window_proc = (WNDPROC)SetWindowLongPtrA(hEdit, GWLP_WNDPROC, (ULONG_PTR)combobox_subclass_proc); + + SendMessageA(hCombo, WM_SETFOCUS, 0, (LPARAM)hEdit); + ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start); + todo_wine ok(setsel_end == INT_MAX, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end); + ok(hCBN_SetFocus == hCombo, "Wrong handle set by CBN_SETFOCUS; got %p\n", hCBN_SetFocus); + ok(GetFocus() == hEdit, "hEdit should have keyboard focus\n"); + + SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hButton, TRUE); + ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start); + todo_wine ok(setsel_end == 0, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end); + ok(hCBN_KillFocus == hCombo, "Wrong handle set by CBN_KILLFOCUS; got %p\n", hCBN_KillFocus); + ok(GetFocus() == hButton, "hButton should have keyboard focus\n"); + + SendMessageA(hCombo, WM_SETTEXT, 0, (LPARAM)wine_test); + SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hCombo, TRUE); + ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start); + todo_wine ok(setsel_end == INT_MAX, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end); + ok(hCBN_SetFocus == hCombo, "Wrong handle set by CBN_SETFOCUS; got %p\n", hCBN_SetFocus); + ok(GetFocus() == hEdit, "hEdit should have keyboard focus\n"); + SendMessageA(hCombo, WM_GETTEXT, sizeof(buffer), (LPARAM)buffer); + ok(!strcmp(buffer, wine_test), "Unexpected text in edit control; got '%s'\n", buffer); + + SendMessageA(hMainWnd, WM_NEXTDLGCTL, (WPARAM)hButton, TRUE); + ok(setsel_start == 0, "Unexpected EM_SETSEL start value; got %ld\n", setsel_start); + todo_wine ok(setsel_end == 0, "Unexpected EM_SETSEL end value; got %ld\n", setsel_end); + ok(hCBN_KillFocus == hCombo, "Wrong handle set by CBN_KILLFOCUS; got %p\n", hCBN_KillFocus); + ok(GetFocus() == hButton, "hButton should have keyboard focus\n"); + len = SendMessageA(hCombo, CB_GETEDITSEL, 0, 0); + ok(len == 0, "Unexpected text selection; start: %u, end: %u\n", LOWORD(len), HIWORD(len)); + + SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc); + DestroyWindow(hButton); + DestroyWindow(hCombo); +} + +static void test_combo_listbox_styles(DWORD cb_style) +{ + DWORD style, exstyle, expect_style, expect_exstyle; + COMBOBOXINFO info; + HWND combo; + + expect_style = WS_CHILD|WS_CLIPSIBLINGS|LBS_COMBOBOX|LBS_HASSTRINGS|LBS_NOTIFY; + if (cb_style == CBS_SIMPLE) + { + expect_style |= WS_VISIBLE; + expect_exstyle = WS_EX_CLIENTEDGE; + } + else + { + expect_style |= WS_BORDER; + expect_exstyle = WS_EX_TOOLWINDOW; + } + + combo = create_combobox(cb_style); + get_combobox_info(combo, &info); + + style = GetWindowLongW( info.hwndList, GWL_STYLE ); + exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE ); + ok(style == expect_style, "%08x: got %08x\n", cb_style, style); + ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle); + + if (cb_style != CBS_SIMPLE) + expect_exstyle |= WS_EX_TOPMOST; + + SendMessageW(combo, CB_SHOWDROPDOWN, TRUE, 0 ); + style = GetWindowLongW( info.hwndList, GWL_STYLE ); + exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE ); + ok(style == (expect_style | WS_VISIBLE), "%08x: got %08x\n", cb_style, style); + ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle); + + SendMessageW(combo, CB_SHOWDROPDOWN, FALSE, 0 ); + style = GetWindowLongW( info.hwndList, GWL_STYLE ); + exstyle = GetWindowLongW( info.hwndList, GWL_EXSTYLE ); + ok(style == expect_style, "%08x: got %08x\n", cb_style, style); + ok(exstyle == expect_exstyle, "%08x: got %08x\n", cb_style, exstyle); + + DestroyWindow(combo); +} + +static void test_combo_WS_VSCROLL(void) +{ + HWND hCombo, hList; + COMBOBOXINFO info; + DWORD style; + int i; + + hCombo = create_combobox(CBS_DROPDOWNLIST); + + get_combobox_info(hCombo, &info); + hList = info.hwndList; + + for (i = 0; i < 3; i++) + { + char buffer[2]; + sprintf(buffer, "%d", i); + SendMessageA(hCombo, CB_ADDSTRING, 0, (LPARAM)buffer); + } + + style = GetWindowLongA(info.hwndList, GWL_STYLE); + SetWindowLongA(hList, GWL_STYLE, style | WS_VSCROLL); + + SendMessageA(hCombo, CB_SHOWDROPDOWN, TRUE, 0); + SendMessageA(hCombo, CB_SHOWDROPDOWN, FALSE, 0); + + style = GetWindowLongA(hList, GWL_STYLE); + ok((style & WS_VSCROLL) != 0, "Style does not include WS_VSCROLL\n"); + + DestroyWindow(hCombo); +} + +START_TEST(combo) +{ + ULONG_PTR ctx_cookie; + HANDLE hCtx; + + if (!init()) + return; + + init_msg_sequences(sequences, NUM_MSG_SEQUENCES); + + /* ComboBoxEx32 tests. */ + test_comboex(); + test_comboex_WM_LBUTTONDOWN(); + test_comboex_CB_GETLBTEXT(); + test_comboex_WM_WINDOWPOSCHANGING(); + test_comboex_subclass(); + test_comboex_get_set_item(); + + if (!load_v6_module(&ctx_cookie, &hCtx)) + { + cleanup(); + return; + } + + /* ComboBox control tests. */ + test_combo_WS_VSCROLL(); + test_combo_setfont(CBS_DROPDOWN); + test_combo_setfont(CBS_DROPDOWNLIST); + test_combo_setitemheight(CBS_DROPDOWN); + test_combo_setitemheight(CBS_DROPDOWNLIST); + test_combo_CBN_SELCHANGE(); + test_combo_changesize(CBS_DROPDOWN); + test_combo_changesize(CBS_DROPDOWNLIST); + test_combo_editselection(); + test_combo_editselection_focus(CBS_SIMPLE); + test_combo_editselection_focus(CBS_DROPDOWN); + test_combo_listbox_styles(CBS_SIMPLE); + test_combo_listbox_styles(CBS_DROPDOWN); + test_combo_listbox_styles(CBS_DROPDOWNLIST); + + cleanup(); + unload_v6_module(ctx_cookie, hCtx); +} diff --git a/modules/rostests/winetests/comctl32/comboex.c b/modules/rostests/winetests/comctl32/comboex.c deleted file mode 100644 index 947a871a6b9..00000000000 --- a/modules/rostests/winetests/comctl32/comboex.c +++ /dev/null @@ -1,608 +0,0 @@ -/* Unit test suite for comboex control. - * - * Copyright 2005 Jason Edmeades - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include "precomp.h" - -#define EDITBOX_SEQ_INDEX 0 -#define NUM_MSG_SEQUENCES 1 - -#define EDITBOX_ID 0 - -#define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got) - -static struct msg_sequence *sequences[NUM_MSG_SEQUENCES]; - -static HWND hComboExParentWnd; -static HINSTANCE hMainHinst; -static const char ComboExTestClass[] = "ComboExTestClass"; - -static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR); - -#define MAX_CHARS 100 -static char *textBuffer = NULL; - -static BOOL received_end_edit = FALSE; - -static HWND createComboEx(DWORD style) { - return CreateWindowExA(0, WC_COMBOBOXEXA, NULL, style, 0, 0, 300, 300, - hComboExParentWnd, NULL, hMainHinst, NULL); -} - -static LONG addItem(HWND cbex, int idx, const char *text) { - COMBOBOXEXITEMA cbexItem; - memset(&cbexItem, 0x00, sizeof(cbexItem)); - cbexItem.mask = CBEIF_TEXT; - cbexItem.iItem = idx; - cbexItem.pszText = (char*)text; - cbexItem.cchTextMax = 0; - return SendMessageA(cbex, CBEM_INSERTITEMA, 0, (LPARAM)&cbexItem); -} - -static LONG setItem(HWND cbex, int idx, const char *text) { - COMBOBOXEXITEMA cbexItem; - memset(&cbexItem, 0x00, sizeof(cbexItem)); - cbexItem.mask = CBEIF_TEXT; - cbexItem.iItem = idx; - cbexItem.pszText = (char*)text; - cbexItem.cchTextMax = 0; - return SendMessageA(cbex, CBEM_SETITEMA, 0, (LPARAM)&cbexItem); -} - -static LONG delItem(HWND cbex, int idx) { - return SendMessageA(cbex, CBEM_DELETEITEM, idx, 0); -} - -static LONG getItem(HWND cbex, int idx, COMBOBOXEXITEMA *cbItem) { - memset(cbItem, 0x00, sizeof(COMBOBOXEXITEMA)); - cbItem->mask = CBEIF_TEXT; - cbItem->pszText = textBuffer; - cbItem->iItem = idx; - cbItem->cchTextMax = 100; - return SendMessageA(cbex, CBEM_GETITEMA, 0, (LPARAM)cbItem); -} - -static LRESULT WINAPI editbox_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA); - static LONG defwndproc_counter = 0; - struct message msg = { 0 }; - LRESULT ret; - - msg.message = message; - msg.flags = sent|wparam|lparam; - if (defwndproc_counter) msg.flags |= defwinproc; - msg.wParam = wParam; - msg.lParam = lParam; - msg.id = EDITBOX_ID; - - if (message != WM_PAINT && - message != WM_ERASEBKGND && - message != WM_NCPAINT && - message != WM_NCHITTEST && - message != WM_GETTEXT && - message != WM_GETICON && - message != WM_DEVICECHANGE) - { - add_message(sequences, EDITBOX_SEQ_INDEX, &msg); - } - - defwndproc_counter++; - ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam); - defwndproc_counter--; - return ret; -} - -static HWND subclass_editbox(HWND hwndComboEx) -{ - WNDPROC oldproc; - HWND hwnd; - - hwnd = (HWND)SendMessageA(hwndComboEx, CBEM_GETEDITCONTROL, 0, 0); - oldproc = (WNDPROC)SetWindowLongPtrA(hwnd, GWLP_WNDPROC, - (LONG_PTR)editbox_subclass_proc); - SetWindowLongPtrA(hwnd, GWLP_USERDATA, (LONG_PTR)oldproc); - - return hwnd; -} - -static void test_comboboxex(void) { - HWND myHwnd = 0; - LONG res; - COMBOBOXEXITEMA cbexItem; - static const char *first_item = "First Item", - *second_item = "Second Item", - *third_item = "Third Item", - *middle_item = "Between First and Second Items", - *replacement_item = "Between First and Second Items", - *out_of_range_item = "Out of Range Item"; - - /* Allocate space for result */ - textBuffer = HeapAlloc(GetProcessHeap(), 0, MAX_CHARS); - - /* Basic comboboxex test */ - myHwnd = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN); - - /* Add items onto the end of the combobox */ - res = addItem(myHwnd, -1, first_item); - ok(res == 0, "Adding simple item failed (%d)\n", res); - res = addItem(myHwnd, -1, second_item); - ok(res == 1, "Adding simple item failed (%d)\n", res); - res = addItem(myHwnd, 2, third_item); - ok(res == 2, "Adding simple item failed (%d)\n", res); - res = addItem(myHwnd, 1, middle_item); - ok(res == 1, "Inserting simple item failed (%d)\n", res); - - /* Add an item completely out of range */ - res = addItem(myHwnd, 99, out_of_range_item); - ok(res == -1, "Adding using out of range index worked unexpectedly (%d)\n", res); - res = addItem(myHwnd, 5, out_of_range_item); - ok(res == -1, "Adding using out of range index worked unexpectedly (%d)\n", res); - /* Removed: Causes traps on Windows XP - res = addItem(myHwnd, -2, "Out Of Range Item"); - ok(res == -1, "Adding out of range worked unexpectedly (%ld)\n", res); - */ - - /* Get an item completely out of range */ - res = getItem(myHwnd, 99, &cbexItem); - ok(res == 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res, cbexItem.pszText); - res = getItem(myHwnd, 4, &cbexItem); - ok(res == 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res, cbexItem.pszText); - res = getItem(myHwnd, -2, &cbexItem); - ok(res == 0, "Getting item using out of range index worked unexpectedly (%d, %s)\n", res, cbexItem.pszText); - - /* Get an item in range */ - res = getItem(myHwnd, 0, &cbexItem); - ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res); - ok(strcmp(first_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText); - - res = getItem(myHwnd, 1, &cbexItem); - ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res); - ok(strcmp(middle_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText); - - res = getItem(myHwnd, 2, &cbexItem); - ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res); - ok(strcmp(second_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText); - - res = getItem(myHwnd, 3, &cbexItem); - ok(res != 0, "Getting item using valid index failed unexpectedly (%d)\n", res); - ok(strcmp(third_item, cbexItem.pszText) == 0, "Getting item returned wrong string (%s)\n", cbexItem.pszText); - - /* Set an item completely out of range */ - res = setItem(myHwnd, 99, replacement_item); - ok(res == 0, "Setting item using out of range index worked unexpectedly (%d)\n", res); - res = setItem(myHwnd, 4, replacement_item); - ok(res == 0, "Setting item using out of range index worked unexpectedly (%d)\n", res); - res = setItem(myHwnd, -2, replacement_item); - ok(res == 0, "Setting item using out of range index worked unexpectedly (%d)\n", res); - - /* Set an item in range */ - res = setItem(myHwnd, 0, replacement_item); - ok(res != 0, "Setting first item failed (%d)\n", res); - res = setItem(myHwnd, 3, replacement_item); - ok(res != 0, "Setting last item failed (%d)\n", res); - - /* Remove items completely out of range (4 items in control at this point) */ - res = delItem(myHwnd, -1); - ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%d)\n", res); - res = delItem(myHwnd, 4); - ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%d)\n", res); - - /* Remove items in range (4 items in control at this point) */ - res = delItem(myHwnd, 3); - ok(res == 3, "Deleting using out of range index failed (%d)\n", res); - res = delItem(myHwnd, 0); - ok(res == 2, "Deleting using out of range index failed (%d)\n", res); - res = delItem(myHwnd, 0); - ok(res == 1, "Deleting using out of range index failed (%d)\n", res); - res = delItem(myHwnd, 0); - ok(res == 0, "Deleting using out of range index failed (%d)\n", res); - - /* Remove from an empty box */ - res = delItem(myHwnd, 0); - ok(res == CB_ERR, "Deleting using out of range index worked unexpectedly (%d)\n", res); - - - /* Cleanup */ - HeapFree(GetProcessHeap(), 0, textBuffer); - DestroyWindow(myHwnd); -} - -static void test_WM_LBUTTONDOWN(void) -{ - HWND hComboEx, hCombo, hEdit, hList; - COMBOBOXINFO cbInfo; - UINT x, y, item_height; - LRESULT result; - UINT i; - int idx; - RECT rect; - WCHAR buffer[3]; - static const UINT choices[] = {8,9,10,11,12,14,16,18,20,22,24,26,28,36,48,72}; - static const WCHAR stringFormat[] = {'%','2','d','\0'}; - BOOL (WINAPI *pGetComboBoxInfo)(HWND, PCOMBOBOXINFO); - - pGetComboBoxInfo = (void*)GetProcAddress(GetModuleHandleA("user32.dll"), "GetComboBoxInfo"); - if (!pGetComboBoxInfo){ - win_skip("GetComboBoxInfo is not available\n"); - return; - } - - hComboEx = CreateWindowExA(0, WC_COMBOBOXEXA, NULL, - WS_VISIBLE|WS_CHILD|CBS_DROPDOWN, 0, 0, 200, 150, - hComboExParentWnd, NULL, hMainHinst, NULL); - - for (i = 0; i < sizeof(choices)/sizeof(UINT); i++){ - COMBOBOXEXITEMW cbexItem; - wsprintfW(buffer, stringFormat, choices[i]); - - memset(&cbexItem, 0x00, sizeof(cbexItem)); - cbexItem.mask = CBEIF_TEXT; - cbexItem.iItem = i; - cbexItem.pszText = buffer; - cbexItem.cchTextMax = 0; - ok(SendMessageW(hComboEx, CBEM_INSERTITEMW, 0, (LPARAM)&cbexItem) >= 0, - "Failed to add item %d\n", i); - } - - hCombo = (HWND)SendMessageA(hComboEx, CBEM_GETCOMBOCONTROL, 0, 0); - hEdit = (HWND)SendMessageA(hComboEx, CBEM_GETEDITCONTROL, 0, 0); - - cbInfo.cbSize = sizeof(COMBOBOXINFO); - result = pGetComboBoxInfo(hCombo, &cbInfo); - ok(result, "Failed to get combobox info structure. LastError=%d\n", - GetLastError()); - hList = cbInfo.hwndList; - - ok(GetFocus() == hComboExParentWnd, - "Focus not on Main Window, instead on %p\n", GetFocus()); - - /* Click on the button to drop down the list */ - x = cbInfo.rcButton.left + (cbInfo.rcButton.right-cbInfo.rcButton.left)/2; - y = cbInfo.rcButton.top + (cbInfo.rcButton.bottom-cbInfo.rcButton.top)/2; - result = SendMessageA(hCombo, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y)); - ok(result, "WM_LBUTTONDOWN was not processed. LastError=%d\n", - GetLastError()); - ok(GetFocus() == hCombo || - broken(GetFocus() != hCombo), /* win98 */ - "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n", - GetFocus()); - ok(SendMessageA(hComboEx, CB_GETDROPPEDSTATE, 0, 0), - "The dropdown list should have appeared after clicking the button.\n"); - idx = SendMessageA(hCombo, CB_GETTOPINDEX, 0, 0); - ok(idx == 0, "For TopIndex expected %d, got %d\n", 0, idx); - - result = SendMessageA(hCombo, WM_LBUTTONUP, 0, MAKELPARAM(x, y)); - ok(result, "WM_LBUTTONUP was not processed. LastError=%d\n", - GetLastError()); - ok(GetFocus() == hCombo || - broken(GetFocus() != hCombo), /* win98 */ - "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n", - GetFocus()); - - /* Click on the 5th item in the list */ - item_height = SendMessageA(hCombo, CB_GETITEMHEIGHT, 0, 0); - ok(GetClientRect(hList, &rect), "Failed to get list's client rect.\n"); - x = rect.left + (rect.right-rect.left)/2; - y = item_height/2 + item_height*4; - result = SendMessageA(hList, WM_MOUSEMOVE, 0, MAKELPARAM(x, y)); - ok(!result, "WM_MOUSEMOVE was not processed. LastError=%d\n", - GetLastError()); - ok(GetFocus() == hCombo || - broken(GetFocus() != hCombo), /* win98 */ - "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n", - GetFocus()); - - result = SendMessageA(hList, WM_LBUTTONDOWN, 0, MAKELPARAM(x, y)); - ok(!result, "WM_LBUTTONDOWN was not processed. LastError=%d\n", - GetLastError()); - ok(GetFocus() == hCombo || - broken(GetFocus() != hCombo), /* win98 */ - "Focus not on ComboBoxEx's ComboBox Control, instead on %p\n", - GetFocus()); - ok(SendMessageA(hComboEx, CB_GETDROPPEDSTATE, 0, 0), - "The dropdown list should still be visible.\n"); - - result = SendMessageA(hList, WM_LBUTTONUP, 0, MAKELPARAM(x, y)); - ok(!result, "WM_LBUTTONUP was not processed. LastError=%d\n", - GetLastError()); - todo_wine ok(GetFocus() == hEdit || - broken(GetFocus() == hCombo), /* win98 */ - "Focus not on ComboBoxEx's Edit Control, instead on %p\n", - GetFocus()); - - result = SendMessageA(hCombo, CB_GETDROPPEDSTATE, 0, 0); - ok(!result || - broken(result != 0), /* win98 */ - "The dropdown list should have been rolled up.\n"); - idx = SendMessageA(hComboEx, CB_GETCURSEL, 0, 0); - ok(idx == 4 || - broken(idx == -1), /* win98 */ - "Current Selection: expected %d, got %d\n", 4, idx); - ok(received_end_edit, "Expected to receive a CBEN_ENDEDIT message\n"); - - SetFocus( hComboExParentWnd ); - ok( GetFocus() == hComboExParentWnd, "got %p\n", GetFocus() ); - SetFocus( hComboEx ); - ok( GetFocus() == hEdit, "got %p\n", GetFocus() ); - - DestroyWindow(hComboEx); -} - -static void test_CB_GETLBTEXT(void) -{ - HWND hCombo; - CHAR buff[1]; - COMBOBOXEXITEMA item; - LRESULT ret; - - hCombo = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN); - - /* set text to null */ - addItem(hCombo, 0, NULL); - - buff[0] = 'a'; - item.mask = CBEIF_TEXT; - item.iItem = 0; - item.pszText = buff; - item.cchTextMax = 1; - ret = SendMessageA(hCombo, CBEM_GETITEMA, 0, (LPARAM)&item); - ok(ret != 0, "CBEM_GETITEM failed\n"); - ok(buff[0] == 0, "\n"); - - ret = SendMessageA(hCombo, CB_GETLBTEXTLEN, 0, 0); - ok(ret == 0, "Expected zero length\n"); - - ret = SendMessageA(hCombo, CB_GETLBTEXTLEN, 0, 0); - ok(ret == 0, "Expected zero length\n"); - - buff[0] = 'a'; - ret = SendMessageA(hCombo, CB_GETLBTEXT, 0, (LPARAM)buff); - ok(ret == 0, "Expected zero length\n"); - ok(buff[0] == 0, "Expected null terminator as a string, got %s\n", buff); - - DestroyWindow(hCombo); -} - -static void test_WM_WINDOWPOSCHANGING(void) -{ - HWND hCombo; - WINDOWPOS wp; - RECT rect; - int combo_height; - int ret; - - hCombo = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN); - ok(hCombo != NULL, "createComboEx failed\n"); - ret = GetWindowRect(hCombo, &rect); - ok(ret, "GetWindowRect failed\n"); - combo_height = rect.bottom - rect.top; - ok(combo_height > 0, "wrong combo height\n"); - - /* Test height > combo_height */ - wp.x = rect.left; - wp.y = rect.top; - wp.cx = (rect.right - rect.left); - wp.cy = combo_height * 2; - wp.flags = 0; - wp.hwnd = hCombo; - wp.hwndInsertAfter = NULL; - - ret = SendMessageA(hCombo, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp); - ok(ret == 0, "expected 0, got %x\n", ret); - ok(wp.cy == combo_height, - "Expected height %d, got %d\n", combo_height, wp.cy); - - /* Test height < combo_height */ - wp.x = rect.left; - wp.y = rect.top; - wp.cx = (rect.right - rect.left); - wp.cy = combo_height / 2; - wp.flags = 0; - wp.hwnd = hCombo; - wp.hwndInsertAfter = NULL; - - ret = SendMessageA(hCombo, WM_WINDOWPOSCHANGING, 0, (LPARAM)&wp); - ok(ret == 0, "expected 0, got %x\n", ret); - ok(wp.cy == combo_height, - "Expected height %d, got %d\n", combo_height, wp.cy); - - ret = DestroyWindow(hCombo); - ok(ret, "DestroyWindow failed\n"); -} - -static LRESULT ComboExTestOnNotify(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - NMHDR *hdr = (NMHDR*)lParam; - switch(hdr->code){ - case CBEN_ENDEDITA: - { - NMCBEENDEDITA *edit_info = (NMCBEENDEDITA*)hdr; - if(edit_info->iWhy==CBENF_DROPDOWN){ - received_end_edit = TRUE; - } - break; - } - case CBEN_ENDEDITW: - { - NMCBEENDEDITW *edit_info = (NMCBEENDEDITW*)hdr; - if(edit_info->iWhy==CBENF_DROPDOWN){ - received_end_edit = TRUE; - } - break; - } - } - return 0; -} - -static LRESULT CALLBACK ComboExTestWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - switch(msg) { - - case WM_DESTROY: - PostQuitMessage(0); - break; - case WM_NOTIFY: - return ComboExTestOnNotify(hWnd,msg,wParam,lParam); - default: - return DefWindowProcA(hWnd, msg, wParam, lParam); - } - - return 0L; -} - -static BOOL init(void) -{ - HMODULE hComctl32; - BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*); - WNDCLASSA wc; - INITCOMMONCONTROLSEX iccex; - - hComctl32 = GetModuleHandleA("comctl32.dll"); - pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx"); - if (!pInitCommonControlsEx) - { - win_skip("InitCommonControlsEx() is missing. Skipping the tests\n"); - return FALSE; - } - iccex.dwSize = sizeof(iccex); - iccex.dwICC = ICC_USEREX_CLASSES; - pInitCommonControlsEx(&iccex); - - pSetWindowSubclass = (void*)GetProcAddress(hComctl32, (LPSTR)410); - - wc.style = CS_HREDRAW | CS_VREDRAW; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = GetModuleHandleA(NULL); - wc.hIcon = NULL; - wc.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW); - wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW); - wc.lpszMenuName = NULL; - wc.lpszClassName = ComboExTestClass; - wc.lpfnWndProc = ComboExTestWndProc; - RegisterClassA(&wc); - - hComboExParentWnd = CreateWindowExA(0, ComboExTestClass, "ComboEx test", WS_OVERLAPPEDWINDOW|WS_VISIBLE, - CW_USEDEFAULT, CW_USEDEFAULT, 680, 260, NULL, NULL, GetModuleHandleA(NULL), 0); - ok(hComboExParentWnd != NULL, "failed to create parent window\n"); - - hMainHinst = GetModuleHandleA(NULL); - - return hComboExParentWnd != NULL; -} - -static void cleanup(void) -{ - MSG msg; - - PostMessageA(hComboExParentWnd, WM_CLOSE, 0, 0); - while (GetMessageA(&msg,0,0,0)) { - TranslateMessage(&msg); - DispatchMessageA(&msg); - } - - DestroyWindow(hComboExParentWnd); - UnregisterClassA(ComboExTestClass, GetModuleHandleA(NULL)); -} - -static void test_comboboxex_subclass(void) -{ - HWND hComboEx, hCombo, hEdit; - - hComboEx = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN); - - hCombo = (HWND)SendMessageA(hComboEx, CBEM_GETCOMBOCONTROL, 0, 0); - ok(hCombo != NULL, "Failed to get internal combo\n"); - hEdit = (HWND)SendMessageA(hComboEx, CBEM_GETEDITCONTROL, 0, 0); - ok(hEdit != NULL, "Failed to get internal edit\n"); - - if (pSetWindowSubclass) - { - ok(GetPropA(hCombo, "CC32SubclassInfo") != NULL, "Expected CC32SubclassInfo property\n"); - ok(GetPropA(hEdit, "CC32SubclassInfo") != NULL, "Expected CC32SubclassInfo property\n"); - } - - DestroyWindow(hComboEx); -} - -static const struct message test_setitem_edit_seq[] = { - { WM_SETTEXT, sent|id, 0, 0, EDITBOX_ID }, - { EM_SETSEL, sent|id|wparam|lparam, 0, 0, EDITBOX_ID }, - { EM_SETSEL, sent|id|wparam|lparam, 0, -1, EDITBOX_ID }, - { 0 } -}; - -static void test_get_set_item(void) -{ - char textA[] = "test"; - HWND hComboEx; - COMBOBOXEXITEMA item; - BOOL ret; - - hComboEx = createComboEx(WS_BORDER | WS_VISIBLE | WS_CHILD | CBS_DROPDOWN); - - subclass_editbox(hComboEx); - - flush_sequences(sequences, NUM_MSG_SEQUENCES); - - memset(&item, 0, sizeof(item)); - item.mask = CBEIF_TEXT; - item.pszText = textA; - item.iItem = -1; - ret = SendMessageA(hComboEx, CBEM_SETITEMA, 0, (LPARAM)&item); - expect(TRUE, ret); - - ok_sequence(sequences, EDITBOX_SEQ_INDEX, test_setitem_edit_seq, "set item data for edit", FALSE); - - /* get/set lParam */ - item.mask = CBEIF_LPARAM; - item.iItem = -1; - item.lParam = 0xdeadbeef; - ret = SendMessageA(hComboEx, CBEM_GETITEMA, 0, (LPARAM)&item); - expect(TRUE, ret); - ok(item.lParam == 0, "Expected zero, got %lx\n", item.lParam); - - item.lParam = 0x1abe11ed; - ret = SendMessageA(hComboEx, CBEM_SETITEMA, 0, (LPARAM)&item); - expect(TRUE, ret); - - item.lParam = 0; - ret = SendMessageA(hComboEx, CBEM_GETITEMA, 0, (LPARAM)&item); - expect(TRUE, ret); - ok(item.lParam == 0x1abe11ed, "Expected 0x1abe11ed, got %lx\n", item.lParam); - - DestroyWindow(hComboEx); -} - -START_TEST(comboex) -{ - if (!init()) - return; - - init_msg_sequences(sequences, NUM_MSG_SEQUENCES); - - test_comboboxex(); - test_WM_LBUTTONDOWN(); - test_CB_GETLBTEXT(); - test_WM_WINDOWPOSCHANGING(); - test_comboboxex_subclass(); - test_get_set_item(); - - cleanup(); -} diff --git a/modules/rostests/winetests/comctl32/edit.c b/modules/rostests/winetests/comctl32/edit.c new file mode 100644 index 00000000000..47196245a4f --- /dev/null +++ b/modules/rostests/winetests/comctl32/edit.c @@ -0,0 +1,3024 @@ +/* Unit test suite for edit control. + * + * Copyright 2004 Vitaliy Margolen + * Copyright 2005 C. Scott Ananian + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "precomp.h" + +#ifndef ES_COMBO +#define ES_COMBO 0x200 +#endif + +#define ID_EDITTESTDBUTTON 0x123 +#define ID_EDITTEST2 99 +#define MAXLEN 200 + +struct edit_notify { + int en_change, en_maxtext, en_update; +}; + +static struct edit_notify notifications; + +static INT_PTR CALLBACK multi_edit_dialog_proc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam) +{ + static int num_ok_commands = 0; + switch (msg) + { + case WM_INITDIALOG: + { + HWND hedit = GetDlgItem(hdlg, 1000); + SetFocus(hedit); + switch (lparam) + { + /* test cases related to bug 12319 */ + case 0: + PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0); + break; + case 1: + PostMessageA(hedit, WM_CHAR, VK_TAB, 0xf0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0); + break; + case 2: + PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001); + PostMessageA(hedit, WM_CHAR, VK_TAB, 0xf0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0); + break; + + /* test cases for pressing enter */ + case 3: + num_ok_commands = 0; + PostMessageA(hedit, WM_KEYDOWN, VK_RETURN, 0x1c0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 1); + break; + + default: + break; + } + break; + } + + case WM_COMMAND: + if (HIWORD(wparam) != BN_CLICKED) + break; + + switch (LOWORD(wparam)) + { + case IDOK: + num_ok_commands++; + break; + + default: + break; + } + break; + + case WM_USER: + { + HWND hfocus = GetFocus(); + HWND hedit = GetDlgItem(hdlg, 1000); + HWND hedit2 = GetDlgItem(hdlg, 1001); + HWND hedit3 = GetDlgItem(hdlg, 1002); + + if (wparam != 0xdeadbeef) + break; + + switch (lparam) + { + case 0: + if (hfocus == hedit) + EndDialog(hdlg, 1111); + else if (hfocus == hedit2) + EndDialog(hdlg, 2222); + else if (hfocus == hedit3) + EndDialog(hdlg, 3333); + else + EndDialog(hdlg, 4444); + break; + case 1: + if ((hfocus == hedit) && (num_ok_commands == 0)) + EndDialog(hdlg, 11); + else + EndDialog(hdlg, 22); + break; + default: + EndDialog(hdlg, 5555); + } + break; + } + + case WM_CLOSE: + EndDialog(hdlg, 333); + break; + + default: + break; + } + + return FALSE; +} + +static INT_PTR CALLBACK edit_dialog_proc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + HWND hedit = GetDlgItem(hdlg, 1000); + SetFocus(hedit); + switch (lparam) + { + /* from bug 11841 */ + case 0: + PostMessageA(hedit, WM_KEYDOWN, VK_ESCAPE, 0x10001); + break; + case 1: + PostMessageA(hedit, WM_KEYDOWN, VK_RETURN, 0x1c0001); + break; + case 2: + PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 1); + break; + + /* more test cases for WM_CHAR */ + case 3: + PostMessageA(hedit, WM_CHAR, VK_ESCAPE, 0x10001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0); + break; + case 4: + PostMessageA(hedit, WM_CHAR, VK_RETURN, 0x1c0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0); + break; + case 5: + PostMessageA(hedit, WM_CHAR, VK_TAB, 0xf0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0); + break; + + /* more test cases for WM_KEYDOWN + WM_CHAR */ + case 6: + PostMessageA(hedit, WM_KEYDOWN, VK_ESCAPE, 0x10001); + PostMessageA(hedit, WM_CHAR, VK_ESCAPE, 0x10001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0); + break; + case 7: + PostMessageA(hedit, WM_KEYDOWN, VK_RETURN, 0x1c0001); + PostMessageA(hedit, WM_CHAR, VK_RETURN, 0x1c0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 1); + break; + case 8: + PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001); + PostMessageA(hedit, WM_CHAR, VK_TAB, 0xf0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 1); + break; + + /* multiple tab tests */ + case 9: + PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001); + PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 2); + break; + case 10: + PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001); + PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001); + PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 2); + break; + + default: + break; + } + break; + } + + case WM_COMMAND: + if (HIWORD(wparam) != BN_CLICKED) + break; + + switch (LOWORD(wparam)) + { + case IDOK: + EndDialog(hdlg, 111); + break; + + case IDCANCEL: + EndDialog(hdlg, 222); + break; + + default: + break; + } + break; + + case WM_USER: + { + int len; + HWND hok = GetDlgItem(hdlg, IDOK); + HWND hcancel = GetDlgItem(hdlg, IDCANCEL); + HWND hedit = GetDlgItem(hdlg, 1000); + HWND hfocus = GetFocus(); + + if (wparam != 0xdeadbeef) + break; + + switch (lparam) + { + case 0: + len = SendMessageA(hedit, WM_GETTEXTLENGTH, 0, 0); + if (len == 0) + EndDialog(hdlg, 444); + else + EndDialog(hdlg, 555); + break; + + case 1: + len = SendMessageA(hedit, WM_GETTEXTLENGTH, 0, 0); + if ((hfocus == hok) && len == 0) + EndDialog(hdlg, 444); + else + EndDialog(hdlg, 555); + break; + + case 2: + if (hfocus == hok) + EndDialog(hdlg, 11); + else if (hfocus == hcancel) + EndDialog(hdlg, 22); + else if (hfocus == hedit) + EndDialog(hdlg, 33); + else + EndDialog(hdlg, 44); + break; + + default: + EndDialog(hdlg, 555); + } + break; + } + + case WM_CLOSE: + EndDialog(hdlg, 333); + break; + + default: + break; + } + + return FALSE; +} + +static INT_PTR CALLBACK edit_singleline_dialog_proc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + HWND hedit = GetDlgItem(hdlg, 1000); + SetFocus(hedit); + switch (lparam) + { + /* test cases for WM_KEYDOWN */ + case 0: + PostMessageA(hedit, WM_KEYDOWN, VK_ESCAPE, 0x10001); + break; + case 1: + PostMessageA(hedit, WM_KEYDOWN, VK_RETURN, 0x1c0001); + break; + case 2: + PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 1); + break; + + /* test cases for WM_CHAR */ + case 3: + PostMessageA(hedit, WM_CHAR, VK_ESCAPE, 0x10001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0); + break; + case 4: + PostMessageA(hedit, WM_CHAR, VK_RETURN, 0x1c0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0); + break; + case 5: + PostMessageA(hedit, WM_CHAR, VK_TAB, 0xf0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0); + break; + + /* test cases for WM_KEYDOWN + WM_CHAR */ + case 6: + PostMessageA(hedit, WM_KEYDOWN, VK_ESCAPE, 0x10001); + PostMessageA(hedit, WM_CHAR, VK_ESCAPE, 0x10001); + break; + case 7: + PostMessageA(hedit, WM_KEYDOWN, VK_RETURN, 0x1c0001); + PostMessageA(hedit, WM_CHAR, VK_RETURN, 0x1c0001); + break; + case 8: + PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001); + PostMessageA(hedit, WM_CHAR, VK_TAB, 0xf0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 1); + break; + + default: + break; + } + break; + } + + case WM_COMMAND: + if (HIWORD(wparam) != BN_CLICKED) + break; + + switch (LOWORD(wparam)) + { + case IDOK: + EndDialog(hdlg, 111); + break; + + case IDCANCEL: + EndDialog(hdlg, 222); + break; + + default: + break; + } + break; + + case WM_USER: + { + HWND hok = GetDlgItem(hdlg, IDOK); + HWND hedit = GetDlgItem(hdlg, 1000); + HWND hfocus = GetFocus(); + int len = SendMessageA(hedit, WM_GETTEXTLENGTH, 0, 0); + + if (wparam != 0xdeadbeef) + break; + + switch (lparam) + { + case 0: + if ((hfocus == hedit) && len == 0) + EndDialog(hdlg, 444); + else + EndDialog(hdlg, 555); + break; + + case 1: + if ((hfocus == hok) && len == 0) + EndDialog(hdlg, 444); + else + EndDialog(hdlg, 555); + break; + + default: + EndDialog(hdlg, 55); + } + break; + } + + case WM_CLOSE: + EndDialog(hdlg, 333); + break; + + default: + break; + } + + return FALSE; +} + +static INT_PTR CALLBACK edit_wantreturn_dialog_proc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + HWND hedit = GetDlgItem(hdlg, 1000); + SetFocus(hedit); + switch (lparam) + { + /* test cases for WM_KEYDOWN */ + case 0: + PostMessageA(hedit, WM_KEYDOWN, VK_ESCAPE, 0x10001); + break; + case 1: + PostMessageA(hedit, WM_KEYDOWN, VK_RETURN, 0x1c0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0); + break; + case 2: + PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 1); + break; + + /* test cases for WM_CHAR */ + case 3: + PostMessageA(hedit, WM_CHAR, VK_ESCAPE, 0x10001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0); + break; + case 4: + PostMessageA(hedit, WM_CHAR, VK_RETURN, 0x1c0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 2); + break; + case 5: + PostMessageA(hedit, WM_CHAR, VK_TAB, 0xf0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0); + break; + + /* test cases for WM_KEYDOWN + WM_CHAR */ + case 6: + PostMessageA(hedit, WM_KEYDOWN, VK_ESCAPE, 0x10001); + PostMessageA(hedit, WM_CHAR, VK_ESCAPE, 0x10001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 0); + break; + case 7: + PostMessageA(hedit, WM_KEYDOWN, VK_RETURN, 0x1c0001); + PostMessageA(hedit, WM_CHAR, VK_RETURN, 0x1c0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 2); + break; + case 8: + PostMessageA(hedit, WM_KEYDOWN, VK_TAB, 0xf0001); + PostMessageA(hedit, WM_CHAR, VK_TAB, 0xf0001); + PostMessageA(hdlg, WM_USER, 0xdeadbeef, 1); + break; + + default: + break; + } + break; + } + + case WM_COMMAND: + if (HIWORD(wparam) != BN_CLICKED) + break; + + switch (LOWORD(wparam)) + { + case IDOK: + EndDialog(hdlg, 111); + break; + + case IDCANCEL: + EndDialog(hdlg, 222); + break; + + default: + break; + } + break; + + case WM_USER: + { + HWND hok = GetDlgItem(hdlg, IDOK); + HWND hedit = GetDlgItem(hdlg, 1000); + HWND hfocus = GetFocus(); + int len = SendMessageA(hedit, WM_GETTEXTLENGTH, 0, 0); + + if (wparam != 0xdeadbeef) + break; + + switch (lparam) + { + case 0: + if ((hfocus == hedit) && len == 0) + EndDialog(hdlg, 444); + else + EndDialog(hdlg, 555); + break; + + case 1: + if ((hfocus == hok) && len == 0) + EndDialog(hdlg, 444); + else + EndDialog(hdlg, 555); + break; + + case 2: + if ((hfocus == hedit) && len == 2) + EndDialog(hdlg, 444); + else + EndDialog(hdlg, 555); + break; + + default: + EndDialog(hdlg, 55); + } + break; + } + + case WM_CLOSE: + EndDialog(hdlg, 333); + break; + + default: + break; + } + + return FALSE; +} + +static HINSTANCE hinst; +static HWND hwndET2; +static const char szEditTest2Class[] = "EditTest2Class"; +static const char szEditTest3Class[] = "EditTest3Class"; +static const char szEditTest4Class[] = "EditTest4Class"; +static const char szEditTextPositionClass[] = "EditTextPositionWindowClass"; + +static HWND create_editcontrol (DWORD style, DWORD exstyle) +{ + HWND handle; + + handle = CreateWindowExA(exstyle, + "EDIT", + "Test Text", + style, + 10, 10, 300, 300, + NULL, NULL, hinst, NULL); + ok (handle != NULL, "CreateWindow EDIT Control failed\n"); + assert (handle); + if (winetest_interactive) + ShowWindow (handle, SW_SHOW); + return handle; +} + +static HWND create_editcontrolW(DWORD style, DWORD exstyle) +{ + static const WCHAR testtextW[] = {'T','e','s','t',' ','t','e','x','t',0}; + HWND handle; + + handle = CreateWindowExW(exstyle, WC_EDITW, testtextW, style, 10, 10, 300, 300, + NULL, NULL, hinst, NULL); + ok(handle != NULL, "Failed to create Edit control.\n"); + return handle; +} + +static HWND create_child_editcontrol (DWORD style, DWORD exstyle) +{ + HWND parentWnd; + HWND editWnd; + RECT rect; + BOOL b; + SetRect(&rect, 0, 0, 300, 300); + b = AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); + ok(b, "AdjustWindowRect failed\n"); + + parentWnd = CreateWindowExA(0, + szEditTextPositionClass, + "Edit Test", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + rect.right - rect.left, rect.bottom - rect.top, + NULL, NULL, hinst, NULL); + ok (parentWnd != NULL, "CreateWindow EDIT Test failed\n"); + assert(parentWnd); + + editWnd = CreateWindowExA(exstyle, + "EDIT", + "Test Text", + WS_CHILD | style, + 0, 0, 300, 300, + parentWnd, NULL, hinst, NULL); + ok (editWnd != NULL, "CreateWindow EDIT Test Text failed\n"); + assert(editWnd); + if (winetest_interactive) + ShowWindow (parentWnd, SW_SHOW); + return editWnd; +} + +static void destroy_child_editcontrol (HWND hwndEdit) +{ + if (GetParent(hwndEdit)) + DestroyWindow(GetParent(hwndEdit)); + else { + trace("Edit control has no parent!\n"); + DestroyWindow(hwndEdit); + } +} + +static LONG get_edit_style (HWND hwnd) +{ + return GetWindowLongA( hwnd, GWL_STYLE ) & ( + ES_LEFT | +/* FIXME: not implemented + ES_CENTER | + ES_RIGHT | + ES_OEMCONVERT | +*/ + ES_MULTILINE | + ES_UPPERCASE | + ES_LOWERCASE | + ES_PASSWORD | + ES_AUTOVSCROLL | + ES_AUTOHSCROLL | + ES_NOHIDESEL | + ES_COMBO | + ES_READONLY | + ES_WANTRETURN | + ES_NUMBER + ); +} + +static void set_client_height(HWND Wnd, unsigned Height) +{ + RECT ClientRect, WindowRect; + + GetWindowRect(Wnd, &WindowRect); + GetClientRect(Wnd, &ClientRect); + SetWindowPos(Wnd, NULL, 0, 0, + WindowRect.right - WindowRect.left, + Height + (WindowRect.bottom - WindowRect.top) - + (ClientRect.bottom - ClientRect.top), + SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER); + + /* Workaround for a bug in Windows' edit control + (multi-line mode) */ + GetWindowRect(Wnd, &WindowRect); + SetWindowPos(Wnd, NULL, 0, 0, + WindowRect.right - WindowRect.left + 1, + WindowRect.bottom - WindowRect.top + 1, + SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER); + SetWindowPos(Wnd, NULL, 0, 0, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER); + + GetClientRect(Wnd, &ClientRect); + ok(ClientRect.bottom - ClientRect.top == Height, + "The client height should be %d, but is %d\n", + Height, ClientRect.bottom - ClientRect.top); +} + +static void test_edit_control_1(void) +{ + HWND hwEdit; + MSG msMessage; + int i; + LONG r; + + msMessage.message = WM_KEYDOWN; + + trace("EDIT: Single line\n"); + hwEdit = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + r = get_edit_style(hwEdit); + ok(r == (ES_AUTOVSCROLL | ES_AUTOHSCROLL), "Wrong style expected 0xc0 got: 0x%x\n", r); + for (i = 0; i < 65535; i++) + { + msMessage.wParam = i; + r = SendMessageA(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage); + ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS), + "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS got %x\n", r); + } + DestroyWindow(hwEdit); + + trace("EDIT: Single line want returns\n"); + hwEdit = create_editcontrol(ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + r = get_edit_style(hwEdit); + ok(r == (ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN), "Wrong style expected 0x10c0 got: 0x%x\n", r); + for (i = 0; i < 65535; i++) + { + msMessage.wParam = i; + r = SendMessageA(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage); + ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS), + "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTARROWS got %x\n", r); + } + DestroyWindow(hwEdit); + + trace("EDIT: Multiline line\n"); + hwEdit = create_editcontrol(ES_MULTILINE | WS_VSCROLL | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + r = get_edit_style(hwEdit); + ok(r == (ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE), "Wrong style expected 0xc4 got: 0x%x\n", r); + for (i = 0; i < 65535; i++) + { + msMessage.wParam = i; + r = SendMessageA(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage); + ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS), + "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS got %x\n", r); + } + DestroyWindow(hwEdit); + + trace("EDIT: Multi line want returns\n"); + hwEdit = create_editcontrol(ES_MULTILINE | WS_VSCROLL | ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + r = get_edit_style(hwEdit); + ok(r == (ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE), "Wrong style expected 0x10c4 got: 0x%x\n", r); + for (i = 0; i < 65535; i++) + { + msMessage.wParam = i; + r = SendMessageA(hwEdit, WM_GETDLGCODE, 0, (LPARAM) &msMessage); + ok(r == (DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS), + "Expected DLGC_WANTCHARS | DLGC_HASSETSEL | DLGC_WANTALLKEYS | DLGC_WANTARROWS got %x\n", r); + } + DestroyWindow(hwEdit); +} + +/* WM_SETTEXT is implemented by selecting all text, and then replacing the + * selection. This test checks that the first 'select all' doesn't generate + * an UPDATE message which can escape and (via a handler) change the + * selection, which would cause WM_SETTEXT to break. This old bug + * was fixed 18-Mar-2005; we check here to ensure it doesn't regress. + */ +static void test_edit_control_2(void) +{ + HWND hwndMain, phwnd; + char szLocalString[MAXLEN]; + LONG r, w = 150, h = 50; + POINT cpos; + + /* Create main and edit windows. */ + hwndMain = CreateWindowA(szEditTest2Class, "ET2", WS_OVERLAPPEDWINDOW, + 0, 0, 200, 200, NULL, NULL, hinst, NULL); + assert(hwndMain); + if (winetest_interactive) + ShowWindow (hwndMain, SW_SHOW); + + hwndET2 = CreateWindowA("EDIT", NULL, + WS_CHILD|WS_BORDER|ES_LEFT|ES_AUTOHSCROLL, + 0, 0, w, h, /* important this not be 0 size. */ + hwndMain, (HMENU) ID_EDITTEST2, hinst, NULL); + assert(hwndET2); + if (winetest_interactive) + ShowWindow (hwndET2, SW_SHOW); + + trace("EDIT: SETTEXT atomicity\n"); + /* Send messages to "type" in the word 'foo'. */ + r = SendMessageA(hwndET2, WM_CHAR, 'f', 1); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + r = SendMessageA(hwndET2, WM_CHAR, 'o', 1); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + r = SendMessageA(hwndET2, WM_CHAR, 'o', 1); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + /* 'foo' should have been changed to 'bar' by the UPDATE handler. */ + GetWindowTextA(hwndET2, szLocalString, MAXLEN); + ok(strcmp(szLocalString, "bar")==0, + "Wrong contents of edit: %s\n", szLocalString); + + /* try setting the caret before it's visible */ + r = SetCaretPos(0, 0); + todo_wine ok(0 == r, "SetCaretPos succeeded unexpectedly, expected: 0, got: %d\n", r); + phwnd = SetFocus(hwndET2); + ok(phwnd != NULL, "SetFocus failed unexpectedly, expected non-zero, got NULL\n"); + r = SetCaretPos(0, 0); + ok(1 == r, "SetCaretPos failed unexpectedly, expected: 1, got: %d\n", r); + r = GetCaretPos(&cpos); + ok(1 == r, "GetCaretPos failed unexpectedly, expected: 1, got: %d\n", r); + ok(cpos.x == 0 && cpos.y == 0, "Wrong caret position, expected: (0,0), got: (%d,%d)\n", cpos.x, cpos.y); + r = SetCaretPos(-1, -1); + ok(1 == r, "SetCaretPos failed unexpectedly, expected: 1, got: %d\n", r); + r = GetCaretPos(&cpos); + ok(1 == r, "GetCaretPos failed unexpectedly, expected: 1, got: %d\n", r); + ok(cpos.x == -1 && cpos.y == -1, "Wrong caret position, expected: (-1,-1), got: (%d,%d)\n", cpos.x, cpos.y); + r = SetCaretPos(w << 1, h << 1); + ok(1 == r, "SetCaretPos failed unexpectedly, expected: 1, got: %d\n", r); + r = GetCaretPos(&cpos); + ok(1 == r, "GetCaretPos failed unexpectedly, expected: 1, got: %d\n", r); + ok(cpos.x == (w << 1) && cpos.y == (h << 1), "Wrong caret position, expected: (%d,%d), got: (%d,%d)\n", w << 1, h << 1, cpos.x, cpos.y); + r = SetCaretPos(w, h); + ok(1 == r, "SetCaretPos failed unexpectedly, expected: 1, got: %d\n", r); + r = GetCaretPos(&cpos); + ok(1 == r, "GetCaretPos failed unexpectedly, expected: 1, got: %d\n", r); + ok(cpos.x == w && cpos.y == h, "Wrong caret position, expected: (%d,%d), got: (%d,%d)\n", w, h, cpos.x, cpos.y); + r = SetCaretPos(w - 1, h - 1); + ok(1 == r, "SetCaretPos failed unexpectedly, expected: 1, got: %d\n", r); + r = GetCaretPos(&cpos); + ok(1 == r, "GetCaretPos failed unexpectedly, expected: 1, got: %d\n", r); + ok(cpos.x == (w - 1) && cpos.y == (h - 1), "Wrong caret position, expected: (%d,%d), got: (%d,%d)\n", w - 1, h - 1, cpos.x, cpos.y); + + DestroyWindow(hwndET2); + DestroyWindow(hwndMain); +} + +static void ET2_check_change(void) +{ + char szLocalString[MAXLEN]; + /* This EN_UPDATE handler changes any 'foo' to 'bar'. */ + GetWindowTextA(hwndET2, szLocalString, MAXLEN); + if (!strcmp(szLocalString, "foo")) + { + strcpy(szLocalString, "bar"); + SendMessageA(hwndET2, WM_SETTEXT, 0, (LPARAM)szLocalString); + } + /* always leave the cursor at the end. */ + SendMessageA(hwndET2, EM_SETSEL, MAXLEN - 1, MAXLEN - 1); +} + +static void ET2_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) +{ + if (id == ID_EDITTEST2 && codeNotify == EN_UPDATE) + ET2_check_change(); +} + +static LRESULT CALLBACK ET2_WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) +{ + switch (iMsg) + { + case WM_COMMAND: + ET2_OnCommand(hwnd, LOWORD(wParam), (HWND)lParam, HIWORD(wParam)); + break; + } + return DefWindowProcA(hwnd, iMsg, wParam, lParam); +} + +static void zero_notify(void) +{ + notifications.en_change = 0; + notifications.en_maxtext = 0; + notifications.en_update = 0; +} + +#define test_notify(enchange, enmaxtext, enupdate) \ +do { \ + ok(notifications.en_change == enchange, "expected %d EN_CHANGE notifications, " \ + "got %d\n", enchange, notifications.en_change); \ + ok(notifications.en_maxtext == enmaxtext, "expected %d EN_MAXTEXT notifications, " \ + "got %d\n", enmaxtext, notifications.en_maxtext); \ + ok(notifications.en_update == enupdate, "expected %d EN_UPDATE notifications, " \ + "got %d\n", enupdate, notifications.en_update); \ +} while(0) + +static LRESULT CALLBACK edit3_wnd_procA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_COMMAND: + switch (HIWORD(wParam)) + { + case EN_MAXTEXT: + notifications.en_maxtext++; + break; + case EN_UPDATE: + notifications.en_update++; + break; + case EN_CHANGE: + notifications.en_change++; + break; + } + break; + } + return DefWindowProcA(hWnd, msg, wParam, lParam); +} + +/* Test behaviour of WM_SETTEXT, WM_REPLACESEL and notifications sent in response + * to these messages. + */ +static void test_edit_control_3(void) +{ + static const char *str = "this is a long string."; + static const char *str2 = "this is a long string.\r\nthis is a long string.\r\nthis is a long string.\r\nthis is a long string."; + HWND hWnd, hParent; + int len, dpi; + HDC hDC; + + hDC = GetDC(NULL); + dpi = GetDeviceCaps(hDC, LOGPIXELSY); + ReleaseDC(NULL, hDC); + + trace("EDIT: Test notifications\n"); + + hParent = CreateWindowExA(0, + szEditTest3Class, + NULL, + 0, + CW_USEDEFAULT, CW_USEDEFAULT, 10, 10, + NULL, NULL, NULL, NULL); + assert(hParent); + + trace("EDIT: Single line, no ES_AUTOHSCROLL\n"); + hWnd = CreateWindowExA(0, + "EDIT", + NULL, + 0, + 10, 10, 50, 50, + hParent, NULL, NULL, NULL); + assert(hWnd); + + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + if (len == lstrlenA(str)) /* Win 8 */ + test_notify(1, 0, 1); + else + test_notify(1, 1, 1); + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)"a"); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(1 == len, "wrong text length, expected 1, got %d\n", len); + test_notify(1, 0, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) == len, "text shouldn't have been truncated\n"); + test_notify(1, 0, 1); + + len = SendMessageA(hWnd, EM_GETSEL, 0, 0); + ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len)); + ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len)); + SendMessageA(hParent, WM_SETFOCUS, 0, (LPARAM)hWnd); + len = SendMessageA(hWnd, EM_GETSEL, 0, 0); + ok(LOWORD(len)==0, "Unexpected start position for selection %d\n", LOWORD(len)); + ok(HIWORD(len)==0, "Unexpected end position for selection %d\n", HIWORD(len)); + + SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0); + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len); + test_notify(1, 1, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) == len, "text shouldn't have been truncated\n"); + test_notify(1, 0, 1); + + DestroyWindow(hWnd); + + trace("EDIT: Single line, ES_AUTOHSCROLL\n"); + hWnd = CreateWindowExA(0, + "EDIT", + NULL, + ES_AUTOHSCROLL, + 10, 10, 50, 50, + hParent, NULL, NULL, NULL); + assert(hWnd); + + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) == len, "text shouldn't have been truncated\n"); + test_notify(1, 0, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) == len, "text shouldn't have been truncated\n"); + test_notify(1, 0, 1); + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n"); + test_notify(1, 0, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n"); + test_notify(1, 0, 1); + + SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0); + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len); + test_notify(1, 1, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) == len, "text shouldn't have been truncated\n"); + test_notify(1, 0, 1); + + DestroyWindow(hWnd); + + trace("EDIT: Multline, no ES_AUTOHSCROLL, no ES_AUTOVSCROLL\n"); + hWnd = CreateWindowExA(0, + "EDIT", + NULL, + ES_MULTILINE, + 10, 10, (50 * dpi) / 96, (50 * dpi) / 96, + hParent, NULL, NULL, NULL); + assert(hWnd); + + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + if (len == lstrlenA(str)) /* Win 8 */ + test_notify(1, 0, 1); + else + { + ok(0 == len, "text should have been truncated, expected 0, got %d\n", len); + test_notify(1, 1, 1); + } + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)"a"); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(1 == SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0), "wrong text length, expected 1, got %d\n", len); + test_notify(1, 0, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) == len, "text shouldn't have been truncated\n"); + test_notify(0, 0, 0); + + SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0); + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len); + test_notify(1, 1, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) == len, "text shouldn't have been truncated\n"); + test_notify(0, 0, 0); + + DestroyWindow(hWnd); + + trace("EDIT: Multline, ES_AUTOHSCROLL, no ES_AUTOVSCROLL\n"); + hWnd = CreateWindowExA(0, + "EDIT", + NULL, + ES_MULTILINE | ES_AUTOHSCROLL, + 10, 10, (50 * dpi) / 96, (50 * dpi) / 96, + hParent, NULL, NULL, NULL); + assert(hWnd); + + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(0 == len, "text should have been truncated, expected 0, got %d\n", len); + test_notify(1, 1, 1); + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)"a"); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(1 == SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0), "wrong text length, expected 1, got %d\n", len); + test_notify(1, 0, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n"); + test_notify(0, 0, 0); + + SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0); + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len); + test_notify(1, 1, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n"); + test_notify(0, 0, 0); + + DestroyWindow(hWnd); + + trace("EDIT: Multline, ES_AUTOHSCROLL and ES_AUTOVSCROLL\n"); + hWnd = CreateWindowExA(0, + "EDIT", + NULL, + ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, + 10, 10, 50, 50, + hParent, NULL, NULL, NULL); + assert(hWnd); + + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n"); + test_notify(1, 0, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n"); + test_notify(0, 0, 0); + + SendMessageA(hWnd, EM_SETLIMITTEXT, 5, 0); + + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)""); + zero_notify(); + SendMessageA(hWnd, EM_REPLACESEL, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(5 == len, "text should have been truncated to limit, expected 5, got %d\n", len); + test_notify(1, 1, 1); + + zero_notify(); + SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str2); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str2) == len, "text shouldn't have been truncated\n"); + test_notify(0, 0, 0); + + DestroyWindow(hWnd); +} + +static void test_char_from_pos(void) +{ + int lo, hi, mid, ret, i; + HWND hwEdit; + + hwEdit = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + SendMessageA(hwEdit, WM_SETTEXT, 0, (LPARAM)"aa"); + lo = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 0, 0)); + hi = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 1, 0)); + mid = lo + (hi - lo) / 2; + + for (i = lo; i < mid; i++) + { + ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i)); + ok(0 == ret, "expected 0 got %d\n", ret); + } + + for (i = mid; i <= hi; i++) + { + ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i)); + ok(1 == ret, "expected 1 got %d\n", ret); + } + + ret = SendMessageA(hwEdit, EM_POSFROMCHAR, 2, 0); + ok(-1 == ret, "expected -1 got %d\n", ret); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(ES_RIGHT | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + SendMessageA(hwEdit, WM_SETTEXT, 0, (LPARAM)"aa"); + lo = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 0, 0)); + hi = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 1, 0)); + mid = lo + (hi - lo) / 2; + + for (i = lo; i < mid; i++) + { + ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i)); + ok(0 == ret, "expected 0 got %d\n", ret); + } + + for (i = mid; i <= hi; i++) + { + ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i)); + ok(1 == ret, "expected 1 got %d\n", ret); + } + + ret = SendMessageA(hwEdit, EM_POSFROMCHAR, 2, 0); + ok(-1 == ret, "expected -1 got %d\n", ret); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(ES_CENTER | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + SendMessageA(hwEdit, WM_SETTEXT, 0, (LPARAM)"aa"); + lo = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 0, 0)); + hi = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 1, 0)); + mid = lo + (hi - lo) / 2; + + for (i = lo; i < mid; i++) + { + ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i)); + ok(0 == ret, "expected 0 got %d\n", ret); + } + + for (i = mid; i <= hi; i++) + { + ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i)); + ok(1 == ret, "expected 1 got %d\n", ret); + } + + ret = SendMessageA(hwEdit, EM_POSFROMCHAR, 2, 0); + ok(-1 == ret, "expected -1 got %d\n", ret); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + SendMessageA(hwEdit, WM_SETTEXT, 0, (LPARAM)"aa"); + lo = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 0, 0)); + hi = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 1, 0)); + mid = lo + (hi - lo) / 2 + 1; + + for (i = lo; i < mid; i++) + { + ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i)); + ok((0 == ret || 1 == ret /* Vista */), "expected 0 or 1 got %d\n", ret); + } + + for (i = mid; i <= hi; i++) + { + ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i)); + ok(1 == ret, "expected 1 got %d\n", ret); + } + + ret = SendMessageA(hwEdit, EM_POSFROMCHAR, 2, 0); + ok(-1 == ret, "expected -1 got %d\n", ret); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(ES_MULTILINE | ES_RIGHT | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + SendMessageA(hwEdit, WM_SETTEXT, 0, (LPARAM)"aa"); + lo = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 0, 0)); + hi = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 1, 0)); + mid = lo + (hi - lo) / 2 + 1; + + for (i = lo; i < mid; i++) + { + ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i)); + ok((0 == ret || 1 == ret /* Vista */), "expected 0 or 1 got %d\n", ret); + } + + for (i = mid; i <= hi; i++) + { + ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i)); + ok(1 == ret, "expected 1 got %d\n", ret); + } + + ret = SendMessageA(hwEdit, EM_POSFROMCHAR, 2, 0); + ok(-1 == ret, "expected -1 got %d\n", ret); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(ES_MULTILINE | ES_CENTER | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + SendMessageA(hwEdit, WM_SETTEXT, 0, (LPARAM)"aa"); + lo = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 0, 0)); + hi = LOWORD(SendMessageA(hwEdit, EM_POSFROMCHAR, 1, 0)); + mid = lo + (hi - lo) / 2 + 1; + + for (i = lo; i < mid; i++) + { + ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i)); + ok((0 == ret || 1 == ret /* Vista */), "expected 0 or 1 got %d\n", ret); + } + + for (i = mid; i <= hi; i++) + { + ret = LOWORD(SendMessageA(hwEdit, EM_CHARFROMPOS, 0, i)); + ok(1 == ret, "expected 1 got %d\n", ret); + } + + ret = SendMessageA(hwEdit, EM_POSFROMCHAR, 2, 0); + ok(-1 == ret, "expected -1 got %d\n", ret); + DestroyWindow(hwEdit); +} + +/* Test if creating edit control without ES_AUTOHSCROLL and ES_AUTOVSCROLL + * truncates text that doesn't fit. + */ +static void test_edit_control_5(void) +{ + static const char *str = "test\r\ntest"; + HWND parentWnd; + HWND hWnd; + int len; + RECT rc1 = { 10, 10, 11, 11}; + RECT rc; + + /* first show that a non-child won't do for this test */ + hWnd = CreateWindowExA(0, + "EDIT", + str, + 0, + 10, 10, 1, 1, + NULL, NULL, NULL, NULL); + assert(hWnd); + /* size of non-child edit control is (much) bigger than requested */ + GetWindowRect( hWnd, &rc); + ok( rc.right - rc.left > 20, "size of the window (%d) is smaller than expected\n", + rc.right - rc.left); + DestroyWindow(hWnd); + /* so create a parent, and give it edit controls children to test with */ + parentWnd = CreateWindowExA(0, + szEditTextPositionClass, + "Edit Test", WS_VISIBLE | + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + 250, 250, + NULL, NULL, hinst, NULL); + assert(parentWnd); + ShowWindow( parentWnd, SW_SHOW); + /* single line */ + hWnd = CreateWindowExA(0, + "EDIT", + str, WS_VISIBLE | WS_BORDER | + WS_CHILD, + rc1.left, rc1.top, rc1.right - rc1.left, rc1.bottom - rc1.top, + parentWnd, NULL, NULL, NULL); + assert(hWnd); + GetClientRect( hWnd, &rc); + ok( rc.right == rc1.right - rc1.left && rc.bottom == rc1.bottom - rc1.top, + "Client rectangle not the expected size %s\n", wine_dbgstr_rect( &rc )); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) == len, "text shouldn't have been truncated\n"); + DestroyWindow(hWnd); + /* multi line */ + hWnd = CreateWindowExA(0, + "EDIT", + str, + WS_CHILD | ES_MULTILINE, + rc1.left, rc1.top, rc1.right - rc1.left, rc1.bottom - rc1.top, + parentWnd, NULL, NULL, NULL); + assert(hWnd); + GetClientRect( hWnd, &rc); + ok( rc.right == rc1.right - rc1.left && rc.bottom == rc1.bottom - rc1.top, + "Client rectangle not the expected size %s\n", wine_dbgstr_rect( &rc )); + len = SendMessageA(hWnd, WM_GETTEXTLENGTH, 0, 0); + ok(lstrlenA(str) == len, "text shouldn't have been truncated\n"); + DestroyWindow(hWnd); + DestroyWindow(parentWnd); +} + +/* Test WM_GETTEXT processing + * after destroy messages + */ +static void test_edit_control_6(void) +{ + static const char *str = "test\r\ntest"; + char buf[MAXLEN]; + HWND hWnd; + LONG ret; + + hWnd = CreateWindowExA(0, "EDIT", "Test", 0, 10, 10, 1, 1, NULL, NULL, hinst, NULL); + ok(hWnd != NULL, "Failed to create edit control.\n"); + + ret = SendMessageA(hWnd, WM_SETTEXT, 0, (LPARAM)str); + ok(ret == TRUE, "Expected %d, got %d\n", TRUE, ret); + ret = SendMessageA(hWnd, WM_GETTEXT, MAXLEN, (LPARAM)buf); + ok(ret == strlen(str), "Expected %s, got len %d\n", str, ret); + ok(!strcmp(buf, str), "Expected %s, got %s\n", str, buf); + + buf[0] = 0; + ret = SendMessageA(hWnd, WM_DESTROY, 0, 0); +todo_wine + ok(ret == 1, "Unexpected return value %d\n", ret); + ret = SendMessageA(hWnd, WM_GETTEXT, MAXLEN, (LPARAM)buf); + ok(ret == strlen(str), "Expected %s, got len %d\n", str, ret); + ok(!strcmp(buf, str), "Expected %s, got %s\n", str, buf); + + buf[0] = 0; + ret = SendMessageA(hWnd, WM_NCDESTROY, 0, 0); + ok(ret == 0, "Expected 0, got %d\n", ret); + ret = SendMessageA(hWnd, WM_GETTEXT, MAXLEN, (LPARAM)buf); +todo_wine { + ok(ret == strlen("Test"), "Unexpected text length %d\n", ret); + ok(!strcmp(buf, "Test"), "Unexpected text %s\n", buf); +} + DestroyWindow(hWnd); +} + +static void test_edit_control_limittext(void) +{ + HWND hwEdit; + DWORD r; + + /* Test default limit for single-line control */ + trace("EDIT: buffer limit for single-line\n"); + hwEdit = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + r = SendMessageA(hwEdit, EM_GETLIMITTEXT, 0, 0); + ok(r == 30000, "Incorrect default text limit, expected 30000 got %u\n", r); + SendMessageA(hwEdit, EM_SETLIMITTEXT, 0, 0); + r = SendMessageA(hwEdit, EM_GETLIMITTEXT, 0, 0); + ok( r == 2147483646, "got limit %u (expected 2147483646)\n", r); + DestroyWindow(hwEdit); + + /* Test default limit for multi-line control */ + trace("EDIT: buffer limit for multi-line\n"); + hwEdit = create_editcontrol(ES_MULTILINE | WS_VSCROLL | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + r = SendMessageA(hwEdit, EM_GETLIMITTEXT, 0, 0); + ok(r == 30000, "Incorrect default text limit, expected 30000 got %u\n", r); + SendMessageA(hwEdit, EM_SETLIMITTEXT, 0, 0); + r = SendMessageA(hwEdit, EM_GETLIMITTEXT, 0, 0); + ok( r == 4294967295U, "got limit %u (expected 4294967295)\n", r); + DestroyWindow(hwEdit); +} + +/* Test EM_SCROLL */ +static void test_edit_control_scroll(void) +{ + static const char *single_line_str = "a"; + static const char *multiline_str = "Test\r\nText"; + HWND hwEdit; + LONG ret; + + /* Check the return value when EM_SCROLL doesn't scroll + * anything. Should not return true unless any lines were actually + * scrolled. */ + hwEdit = CreateWindowA( + "EDIT", + single_line_str, + WS_VSCROLL | ES_MULTILINE, + 1, 1, 100, 100, + NULL, NULL, hinst, NULL); + + assert(hwEdit); + + ret = SendMessageA(hwEdit, EM_SCROLL, SB_PAGEDOWN, 0); + ok(!ret, "Returned %x, expected 0.\n", ret); + + ret = SendMessageA(hwEdit, EM_SCROLL, SB_PAGEUP, 0); + ok(!ret, "Returned %x, expected 0.\n", ret); + + ret = SendMessageA(hwEdit, EM_SCROLL, SB_LINEUP, 0); + ok(!ret, "Returned %x, expected 0.\n", ret); + + ret = SendMessageA(hwEdit, EM_SCROLL, SB_LINEDOWN, 0); + ok(!ret, "Returned %x, expected 0.\n", ret); + + DestroyWindow (hwEdit); + + /* SB_PAGEDOWN while at the beginning of a buffer with few lines + should not cause EM_SCROLL to return a negative value of + scrolled lines that would put us "before" the beginning. */ + hwEdit = CreateWindowA( + "EDIT", + multiline_str, + WS_VSCROLL | ES_MULTILINE, + 0, 0, 100, 100, + NULL, NULL, hinst, NULL); + assert(hwEdit); + + ret = SendMessageA(hwEdit, EM_SCROLL, SB_PAGEDOWN, 0); + ok(!ret, "Returned %x, expected 0.\n", ret); + + DestroyWindow (hwEdit); +} + +static void test_margins_usefontinfo(UINT charset) +{ + INT margins, threshold, expect, empty_expect, small_expect; + HWND hwnd; + HDC hdc; + SIZE size; + BOOL cjk; + LOGFONTA lf; + HFONT hfont; + RECT rect; + + memset(&lf, 0, sizeof(lf)); + lf.lfHeight = -11; + lf.lfWeight = FW_NORMAL; + lf.lfCharSet = charset; + strcpy(lf.lfFaceName, "Tahoma"); + + hfont = CreateFontIndirectA(&lf); + ok(hfont != NULL, "got %p\n", hfont); + + /* Big window rectangle */ + hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, 5000, 1000, NULL, NULL, NULL, NULL); + ok(hwnd != NULL, "got %p\n", hwnd); + GetClientRect(hwnd, &rect); + ok(!IsRectEmpty(&rect), "got rect %s\n", wine_dbgstr_rect(&rect)); + + hdc = GetDC(hwnd); + hfont = SelectObject(hdc, hfont); + size.cx = GdiGetCharDimensions( hdc, NULL, &size.cy ); + expect = MAKELONG(size.cx / 2, size.cx / 2); + small_expect = 0; + empty_expect = size.cx >= 28 ? small_expect : expect; + + charset = GetTextCharset(hdc); + switch (charset) + { + case SHIFTJIS_CHARSET: + case HANGUL_CHARSET: + case GB2312_CHARSET: + case CHINESEBIG5_CHARSET: + cjk = TRUE; + break; + default: + cjk = FALSE; + } + + hfont = SelectObject(hdc, hfont); + ReleaseDC(hwnd, hdc); + + margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); + ok(margins == 0, "got %x\n", margins); + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0)); + margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); + if (!cjk) + ok(margins == expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins)); + else + { + ok(HIWORD(margins) > 0 && LOWORD(margins) > 0, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins)); + expect = empty_expect = small_expect = margins; + } + DestroyWindow(hwnd); + + threshold = (size.cx / 2 + size.cx) * 2; + + /* Size below which non-cjk margins are zero */ + hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, threshold - 1, 100, NULL, NULL, NULL, NULL); + ok(hwnd != NULL, "got %p\n", hwnd); + GetClientRect(hwnd, &rect); + ok(!IsRectEmpty(&rect), "got rect %s\n", wine_dbgstr_rect(&rect)); + + margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); + ok(margins == 0, "got %x\n", margins); + + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0)); + margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); + ok(margins == small_expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins)); + DestroyWindow(hwnd); + + /* Size at which non-cjk margins become non-zero */ + hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, threshold, 100, NULL, NULL, NULL, NULL); + ok(hwnd != NULL, "got %p\n", hwnd); + GetClientRect(hwnd, &rect); + ok(!IsRectEmpty(&rect), "got rect %s\n", wine_dbgstr_rect(&rect)); + + margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); + ok(margins == 0, "got %x\n", margins); + + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0)); + margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); + ok(margins == expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins)); + DestroyWindow(hwnd); + + /* Empty rect */ + hwnd = CreateWindowExA(0, "Edit", "A", WS_POPUP, 0, 0, 0, 0, NULL, NULL, NULL, NULL); + ok(hwnd != NULL, "got %p\n", hwnd); + GetClientRect(hwnd, &rect); + ok(IsRectEmpty(&rect), "got rect %s\n", wine_dbgstr_rect(&rect)); + + margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); + ok(margins == 0, "got %x\n", margins); + + SendMessageA(hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0)); + margins = SendMessageA(hwnd, EM_GETMARGINS, 0, 0); + ok(margins == empty_expect, "%d: got %d, %d\n", charset, HIWORD(margins), LOWORD(margins)); + DestroyWindow(hwnd); + + DeleteObject(hfont); +} + +static void test_margins(void) +{ + DWORD old_margins, new_margins; + RECT old_rect, new_rect; + INT old_right_margin; + HWND hwEdit; + + hwEdit = create_editcontrol(WS_BORDER | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + + old_margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0); + old_right_margin = HIWORD(old_margins); + + /* Check if setting the margins works */ + + SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN, MAKELONG(10, 0)); + new_margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0); + ok(LOWORD(new_margins) == 10, "Wrong left margin: %d\n", LOWORD(new_margins)); + ok(HIWORD(new_margins) == old_right_margin, "Wrong right margin: %d\n", HIWORD(new_margins)); + + SendMessageA(hwEdit, EM_SETMARGINS, EC_RIGHTMARGIN, MAKELONG(0, 10)); + new_margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0); + ok(LOWORD(new_margins) == 10, "Wrong left margin: %d\n", LOWORD(new_margins)); + ok(HIWORD(new_margins) == 10, "Wrong right margin: %d\n", HIWORD(new_margins)); + + /* The size of the rectangle must decrease if we increase the margin */ + + SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(5, 5)); + SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&old_rect); + SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(15, 20)); + SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect); + ok(new_rect.left == old_rect.left + 10, "The left border of the rectangle is wrong\n"); + ok(new_rect.right == old_rect.right - 15, "The right border of the rectangle is wrong\n"); + ok(new_rect.top == old_rect.top, "The top border of the rectangle must not change\n"); + ok(new_rect.bottom == old_rect.bottom, "The bottom border of the rectangle must not change\n"); + + /* If we set the margin to same value as the current margin, + the rectangle must not change */ + + SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(10, 10)); + SetRect(&old_rect, 1, 1, 99, 99); + SendMessageA(hwEdit, EM_SETRECT, 0, (LPARAM)&old_rect); + SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&old_rect); + SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(10, 10)); + SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect); + ok(EqualRect(&old_rect, &new_rect), "The border of the rectangle has changed\n"); + + /* The lParam argument of the WM_SIZE message should be ignored. */ + + SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&old_rect); + SendMessageA(hwEdit, WM_SIZE, SIZE_RESTORED, 0); + SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect); + ok(EqualRect(&old_rect, &new_rect), "The border of the rectangle has changed\n"); + SendMessageA(hwEdit, WM_SIZE, SIZE_MINIMIZED, 0); + SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect); + ok(EqualRect(&old_rect, &new_rect), "The border of the rectangle has changed\n"); + SendMessageA(hwEdit, WM_SIZE, SIZE_MAXIMIZED, 0); + SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect); + ok(EqualRect(&old_rect, &new_rect), "The border of the rectangle has changed\n"); + SendMessageA(hwEdit, WM_SIZE, SIZE_RESTORED, MAKELONG(10, 10)); + SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM)&new_rect); + ok(EqualRect(&old_rect, &new_rect), "The border of the rectangle has changed\n"); + + DestroyWindow (hwEdit); + + test_margins_usefontinfo(ANSI_CHARSET); + test_margins_usefontinfo(EASTEUROPE_CHARSET); + + test_margins_usefontinfo(SHIFTJIS_CHARSET); + test_margins_usefontinfo(HANGUL_CHARSET); + test_margins_usefontinfo(CHINESEBIG5_CHARSET); + /* Don't test JOHAB_CHARSET. Treated as CJK by Win 8, + but not by < Win 8 and Win 10. */ + + test_margins_usefontinfo(DEFAULT_CHARSET); +} + +static INT CALLBACK find_font_proc(const LOGFONTA *elf, const TEXTMETRICA *ntm, DWORD type, LPARAM lParam) +{ + return 0; +} + +static void test_margins_font_change(void) +{ + DWORD margins, font_margins, ret; + HFONT hfont, hfont2; + HWND hwEdit; + LOGFONTA lf; + HDC hdc; + + hdc = GetDC(0); + ret = EnumFontFamiliesA(hdc, "Arial", find_font_proc, 0); + ReleaseDC(0, hdc); + if (ret) + { + trace("Arial not found - skipping font change margin tests\n"); + return; + } + + hwEdit = create_child_editcontrol(0, 0); + + SetWindowPos(hwEdit, NULL, 10, 10, 1000, 100, SWP_NOZORDER | SWP_NOACTIVATE); + + memset(&lf, 0, sizeof(lf)); + strcpy(lf.lfFaceName, "Arial"); + lf.lfHeight = 16; + lf.lfCharSet = DEFAULT_CHARSET; + hfont = CreateFontIndirectA(&lf); + lf.lfHeight = 30; + hfont2 = CreateFontIndirectA(&lf); + + SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0); + font_margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0); + ok(LOWORD(font_margins) != 0, "got %d\n", LOWORD(font_margins)); + ok(HIWORD(font_margins) != 0, "got %d\n", HIWORD(font_margins)); + + /* With 'small' edit controls, test that the margin doesn't get set */ + SetWindowPos(hwEdit, NULL, 10, 10, 16, 100, SWP_NOZORDER | SWP_NOACTIVATE); + SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0,0)); + SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0); + margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0); + ok(LOWORD(margins) == 0, "got %d\n", LOWORD(margins)); + ok(HIWORD(margins) == 0, "got %d\n", HIWORD(margins)); + + SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(1,0)); + SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0); + margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0); + ok(LOWORD(margins) == 1, "got %d\n", LOWORD(margins)); + ok(HIWORD(margins) == 0, "got %d\n", HIWORD(margins)); + + SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(1,1)); + SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0); + margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0); + ok(LOWORD(margins) == 1, "got %d\n", LOWORD(margins)); + ok(HIWORD(margins) == 1, "got %d\n", HIWORD(margins)); + + SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO,EC_USEFONTINFO)); + margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0); + ok(LOWORD(margins) == 1, "got %d\n", LOWORD(margins)); + ok(HIWORD(margins) == 1, "got %d\n", HIWORD(margins)); + + SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont2, 0); + margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0); + ok(LOWORD(margins) == 1, "got %d\n", LOWORD(margins)); + ok(HIWORD(margins) == 1, "got %d\n", HIWORD(margins)); + + /* Above a certain size threshold then the margin is updated */ + SetWindowPos(hwEdit, NULL, 10, 10, 1000, 100, SWP_NOZORDER | SWP_NOACTIVATE); + SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(1,0)); + SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0); + margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0); + ok(LOWORD(margins) == LOWORD(font_margins), "got %d\n", LOWORD(margins)); + ok(HIWORD(margins) == HIWORD(font_margins), "got %d\n", HIWORD(margins)); + + SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(1,1)); + SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0); + margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0); + ok(LOWORD(margins) == LOWORD(font_margins), "got %d\n", LOWORD(margins)); + ok(HIWORD(margins) == HIWORD(font_margins), "got %d\n", HIWORD(margins)); + + SendMessageA(hwEdit, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(EC_USEFONTINFO,EC_USEFONTINFO)); + SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont, 0); + margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0); + ok(LOWORD(margins) == LOWORD(font_margins), "got %d\n", LOWORD(margins)); + ok(HIWORD(margins) == HIWORD(font_margins), "got %d\n", HIWORD(margins)); + SendMessageA(hwEdit, WM_SETFONT, (WPARAM)hfont2, 0); + margins = SendMessageA(hwEdit, EM_GETMARGINS, 0, 0); + ok(LOWORD(margins) != LOWORD(font_margins), "got %d\n", LOWORD(margins)); + ok(HIWORD(margins) != HIWORD(font_margins), "got %d\n", HIWORD(margins)); + + SendMessageA(hwEdit, WM_SETFONT, 0, 0); + + DeleteObject(hfont2); + DeleteObject(hfont); + destroy_child_editcontrol(hwEdit); + +} + +#define edit_pos_ok(exp, got, txt) \ + ok(exp == got, "wrong " #txt " expected %d got %d\n", exp, got); + +#define check_pos(hwEdit, set_height, test_top, test_height, test_left) \ +do { \ + RECT format_rect; \ + int left_margin; \ + set_client_height(hwEdit, set_height); \ + SendMessageA(hwEdit, EM_GETRECT, 0, (LPARAM) &format_rect); \ + left_margin = LOWORD(SendMessageA(hwEdit, EM_GETMARGINS, 0, 0)); \ + edit_pos_ok(test_top, format_rect.top, vertical position); \ + edit_pos_ok((int)test_height, format_rect.bottom - format_rect.top, height); \ + edit_pos_ok(test_left, format_rect.left - left_margin, left); \ +} while(0) + +static void test_text_position_style(DWORD style) +{ + HWND hwEdit; + HFONT font, oldFont; + HDC dc; + TEXTMETRICA metrics; + INT b, bm, b2, b3; + BOOL xb, single_line = !(style & ES_MULTILINE); + + b = GetSystemMetrics(SM_CYBORDER) + 1; + b2 = 2 * b; + b3 = 3 * b; + bm = b2 - 1; + + /* Get a stock font for which we can determine the metrics */ + font = GetStockObject(SYSTEM_FONT); + ok (font != NULL, "GetStockObject SYSTEM_FONT failed\n"); + dc = GetDC(NULL); + ok (dc != NULL, "GetDC() failed\n"); + oldFont = SelectObject(dc, font); + xb = GetTextMetricsA(dc, &metrics); + ok (xb, "GetTextMetrics failed\n"); + SelectObject(dc, oldFont); + ReleaseDC(NULL, dc); + + /* Windows' edit control has some bugs in multi-line mode: + * - Sometimes the format rectangle doesn't get updated + * (see workaround in set_client_height()) + * - If the height of the control is smaller than the height of a text + * line, the format rectangle is still as high as a text line + * (higher than the client rectangle) and the caret is not shown + */ + + /* Edit controls that are in a parent window */ + + hwEdit = create_child_editcontrol(style | WS_VISIBLE, 0); + SendMessageA(hwEdit, WM_SETFONT, (WPARAM) font, FALSE); + if (single_line) + check_pos(hwEdit, metrics.tmHeight - 1, 0, metrics.tmHeight - 1, 0); + check_pos(hwEdit, metrics.tmHeight , 0, metrics.tmHeight , 0); + check_pos(hwEdit, metrics.tmHeight + 1, 0, metrics.tmHeight , 0); + check_pos(hwEdit, metrics.tmHeight + 2, 0, metrics.tmHeight , 0); + check_pos(hwEdit, metrics.tmHeight + 10, 0, metrics.tmHeight , 0); + destroy_child_editcontrol(hwEdit); + + hwEdit = create_child_editcontrol(style | WS_BORDER | WS_VISIBLE, 0); + SendMessageA(hwEdit, WM_SETFONT, (WPARAM) font, FALSE); + if (single_line) + check_pos(hwEdit, metrics.tmHeight - 1, 0, metrics.tmHeight - 1, b); + check_pos(hwEdit, metrics.tmHeight , 0, metrics.tmHeight , b); + check_pos(hwEdit, metrics.tmHeight + 1, 0, metrics.tmHeight , b); + check_pos(hwEdit, metrics.tmHeight + bm, 0, metrics.tmHeight , b); + check_pos(hwEdit, metrics.tmHeight + b2, b, metrics.tmHeight , b); + check_pos(hwEdit, metrics.tmHeight + b3, b, metrics.tmHeight , b); + destroy_child_editcontrol(hwEdit); + + hwEdit = create_child_editcontrol(style | WS_VISIBLE, WS_EX_CLIENTEDGE); + SendMessageA(hwEdit, WM_SETFONT, (WPARAM) font, FALSE); + if (single_line) + check_pos(hwEdit, metrics.tmHeight - 1, 0, metrics.tmHeight - 1, 1); + check_pos(hwEdit, metrics.tmHeight , 0, metrics.tmHeight , 1); + check_pos(hwEdit, metrics.tmHeight + 1, 0, metrics.tmHeight , 1); + check_pos(hwEdit, metrics.tmHeight + 2, 1, metrics.tmHeight , 1); + check_pos(hwEdit, metrics.tmHeight + 10, 1, metrics.tmHeight , 1); + destroy_child_editcontrol(hwEdit); + + hwEdit = create_child_editcontrol(style | WS_BORDER | WS_VISIBLE, WS_EX_CLIENTEDGE); + SendMessageA(hwEdit, WM_SETFONT, (WPARAM) font, FALSE); + if (single_line) + check_pos(hwEdit, metrics.tmHeight - 1, 0, metrics.tmHeight - 1, 1); + check_pos(hwEdit, metrics.tmHeight , 0, metrics.tmHeight , 1); + check_pos(hwEdit, metrics.tmHeight + 1, 0, metrics.tmHeight , 1); + check_pos(hwEdit, metrics.tmHeight + 2, 1, metrics.tmHeight , 1); + check_pos(hwEdit, metrics.tmHeight + 10, 1, metrics.tmHeight , 1); + destroy_child_editcontrol(hwEdit); + + + /* Edit controls that are popup windows */ + + hwEdit = create_editcontrol(style | WS_POPUP, 0); + SendMessageA(hwEdit, WM_SETFONT, (WPARAM) font, FALSE); + if (single_line) + check_pos(hwEdit, metrics.tmHeight - 1, 0, metrics.tmHeight - 1, 0); + check_pos(hwEdit, metrics.tmHeight , 0, metrics.tmHeight , 0); + check_pos(hwEdit, metrics.tmHeight + 1, 0, metrics.tmHeight , 0); + check_pos(hwEdit, metrics.tmHeight + 2, 0, metrics.tmHeight , 0); + check_pos(hwEdit, metrics.tmHeight + 10, 0, metrics.tmHeight , 0); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(style | WS_POPUP | WS_BORDER, 0); + SendMessageA(hwEdit, WM_SETFONT, (WPARAM) font, FALSE); + if (single_line) + check_pos(hwEdit, metrics.tmHeight - 1, 0, metrics.tmHeight - 1, b); + check_pos(hwEdit, metrics.tmHeight , 0, metrics.tmHeight , b); + check_pos(hwEdit, metrics.tmHeight + 1, 0, metrics.tmHeight , b); + check_pos(hwEdit, metrics.tmHeight + bm, 0, metrics.tmHeight , b); + check_pos(hwEdit, metrics.tmHeight + b2, b, metrics.tmHeight , b); + check_pos(hwEdit, metrics.tmHeight + b3, b, metrics.tmHeight , b); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(style | WS_POPUP, WS_EX_CLIENTEDGE); + SendMessageA(hwEdit, WM_SETFONT, (WPARAM) font, FALSE); + if (single_line) + check_pos(hwEdit, metrics.tmHeight - 1, 0, metrics.tmHeight - 1, 1); + check_pos(hwEdit, metrics.tmHeight , 0, metrics.tmHeight , 1); + check_pos(hwEdit, metrics.tmHeight + 1, 0, metrics.tmHeight , 1); + check_pos(hwEdit, metrics.tmHeight + 2, 1, metrics.tmHeight , 1); + check_pos(hwEdit, metrics.tmHeight + 10, 1, metrics.tmHeight , 1); + DestroyWindow(hwEdit); + + hwEdit = create_editcontrol(style | WS_POPUP | WS_BORDER, WS_EX_CLIENTEDGE); + SendMessageA(hwEdit, WM_SETFONT, (WPARAM) font, FALSE); + if (single_line) + check_pos(hwEdit, metrics.tmHeight - 1, 0, metrics.tmHeight - 1, 1); + check_pos(hwEdit, metrics.tmHeight , 0, metrics.tmHeight , 1); + check_pos(hwEdit, metrics.tmHeight + 1, 0, metrics.tmHeight , 1); + check_pos(hwEdit, metrics.tmHeight + 2, 1, metrics.tmHeight , 1); + check_pos(hwEdit, metrics.tmHeight + 10, 1, metrics.tmHeight , 1); + DestroyWindow(hwEdit); +} + +static void test_text_position(void) +{ + trace("EDIT: Text position (Single line)\n"); + test_text_position_style(ES_AUTOHSCROLL | ES_AUTOVSCROLL); + trace("EDIT: Text position (Multi line)\n"); + test_text_position_style(ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL); +} + +static void test_espassword(void) +{ + HWND hwEdit; + LONG r; + char buffer[1024]; + const char* password = "secret"; + + hwEdit = create_editcontrol(ES_PASSWORD, 0); + r = get_edit_style(hwEdit); + ok(r == ES_PASSWORD, "Wrong style expected ES_PASSWORD got: 0x%x\n", r); + /* set text */ + r = SendMessageA(hwEdit , WM_SETTEXT, 0, (LPARAM) password); + ok(r == TRUE, "Expected: %d, got: %d\n", TRUE, r); + + /* select all, cut (ctrl-x) */ + SendMessageA(hwEdit, EM_SETSEL, 0, -1); + r = SendMessageA(hwEdit, WM_CHAR, 24, 0); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + + /* get text */ + r = SendMessageA(hwEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + ok(r == strlen(password), "Expected: %s, got len %d\n", password, r); + ok(strcmp(buffer, password) == 0, "expected %s, got %s\n", password, buffer); + + r = OpenClipboard(hwEdit); + ok(r == TRUE, "expected %d, got %d\n", TRUE, r); + r = EmptyClipboard(); + ok(r == TRUE, "expected %d, got %d\n", TRUE, r); + r = CloseClipboard(); + ok(r == TRUE, "expected %d, got %d\n", TRUE, r); + + /* select all, copy (ctrl-c) and paste (ctrl-v) */ + SendMessageA(hwEdit, EM_SETSEL, 0, -1); + r = SendMessageA(hwEdit, WM_CHAR, 3, 0); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + r = SendMessageA(hwEdit, WM_CHAR, 22, 0); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + + /* get text */ + buffer[0] = 0; + r = SendMessageA(hwEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + ok(r == 0, "Expected: 0, got: %d\n", r); + ok(strcmp(buffer, "") == 0, "expected empty string, got %s\n", buffer); + + DestroyWindow(hwEdit); +} + +static void test_undo(void) +{ + HWND hwEdit; + LONG r; + DWORD cpMin, cpMax; + char buffer[1024]; + const char* text = "undo this"; + + hwEdit = create_editcontrol(0, 0); + r = get_edit_style(hwEdit); + ok(0 == r, "Wrong style expected 0x%x got: 0x%x\n", 0, r); + + /* set text */ + r = SendMessageA(hwEdit , WM_SETTEXT, 0, (LPARAM) text); + ok(TRUE == r, "Expected: %d, got: %d\n", TRUE, r); + + /* select all, */ + cpMin = cpMax = 0xdeadbeef; + SendMessageA(hwEdit, EM_SETSEL, 0, -1); + r = SendMessageA(hwEdit, EM_GETSEL, (WPARAM) &cpMin, (LPARAM) &cpMax); + ok((strlen(text) << 16) == r, "Unexpected length %d\n", r); + ok(0 == cpMin, "Expected: %d, got %d\n", 0, cpMin); + ok(9 == cpMax, "Expected: %d, got %d\n", 9, cpMax); + + /* cut (ctrl-x) */ + r = SendMessageA(hwEdit, WM_CHAR, 24, 0); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + + /* get text */ + buffer[0] = 0; + r = SendMessageA(hwEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + ok(0 == r, "Expected: %d, got len %d\n", 0, r); + ok(0 == strcmp(buffer, ""), "expected %s, got %s\n", "", buffer); + + /* undo (ctrl-z) */ + r = SendMessageA(hwEdit, WM_CHAR, 26, 0); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + + /* get text */ + buffer[0] = 0; + r = SendMessageA(hwEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + ok(strlen(text) == r, "Unexpected length %d\n", r); + ok(0 == strcmp(buffer, text), "expected %s, got %s\n", text, buffer); + + /* undo again (ctrl-z) */ + r = SendMessageA(hwEdit, WM_CHAR, 26, 0); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + + /* get text */ + buffer[0] = 0; + r = SendMessageA(hwEdit, WM_GETTEXT, 1024, (LPARAM) buffer); + ok(r == 0, "Expected: %d, got len %d\n", 0, r); + ok(0 == strcmp(buffer, ""), "expected %s, got %s\n", "", buffer); + + DestroyWindow(hwEdit); +} + +static void test_enter(void) +{ + char buffer[16]; + HWND hwEdit; + LONG r; + + /* multiline */ + hwEdit = create_editcontrol(ES_MULTILINE, 0); + r = get_edit_style(hwEdit); + ok(ES_MULTILINE == r, "Wrong style expected ES_MULTILINE got: 0x%x\n", r); + + /* set text */ + r = SendMessageA(hwEdit , WM_SETTEXT, 0, (LPARAM) ""); + ok(TRUE == r, "Expected: %d, got: %d\n", TRUE, r); + + r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + + /* get text */ + buffer[0] = 0; + r = SendMessageA(hwEdit, WM_GETTEXT, 16, (LPARAM) buffer); + ok(2 == r, "Expected: %d, got len %d\n", 2, r); + ok(0 == strcmp(buffer, "\r\n"), "expected \"\\r\\n\", got \"%s\"\n", buffer); + + DestroyWindow (hwEdit); + + /* single line */ + hwEdit = create_editcontrol(0, 0); + r = get_edit_style(hwEdit); + ok(0 == r, "Wrong style expected 0x%x got: 0x%x\n", 0, r); + + /* set text */ + r = SendMessageA(hwEdit , WM_SETTEXT, 0, (LPARAM) ""); + ok(TRUE == r, "Expected: %d, got: %d\n", TRUE, r); + + r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + + /* get text */ + buffer[0] = 0; + r = SendMessageA(hwEdit, WM_GETTEXT, 16, (LPARAM) buffer); + ok(0 == r, "Expected: %d, got len %d\n", 0, r); + ok(0 == strcmp(buffer, ""), "expected \"\", got \"%s\"\n", buffer); + + DestroyWindow(hwEdit); + + /* single line with ES_WANTRETURN */ + hwEdit = create_editcontrol(ES_WANTRETURN, 0); + r = get_edit_style(hwEdit); + ok(ES_WANTRETURN == r, "Wrong style expected ES_WANTRETURN got: 0x%x\n", r); + + /* set text */ + r = SendMessageA(hwEdit , WM_SETTEXT, 0, (LPARAM) ""); + ok(TRUE == r, "Expected: %d, got: %d\n", TRUE, r); + + r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + + /* get text */ + buffer[0] = 0; + r = SendMessageA(hwEdit, WM_GETTEXT, 16, (LPARAM) buffer); + ok(0 == r, "Expected: %d, got len %d\n", 0, r); + ok(0 == strcmp(buffer, ""), "expected \"\", got \"%s\"\n", buffer); + + DestroyWindow(hwEdit); +} + +static void test_tab(void) +{ + char buffer[16]; + HWND hwEdit; + LONG r; + + /* multiline */ + hwEdit = create_editcontrol(ES_MULTILINE, 0); + r = get_edit_style(hwEdit); + ok(ES_MULTILINE == r, "Wrong style expected ES_MULTILINE got: 0x%x\n", r); + + /* set text */ + r = SendMessageA(hwEdit , WM_SETTEXT, 0, (LPARAM) ""); + ok(TRUE == r, "Expected: %d, got: %d\n", TRUE, r); + + r = SendMessageA(hwEdit, WM_CHAR, VK_TAB, 0); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + + /* get text */ + buffer[0] = 0; + r = SendMessageA(hwEdit, WM_GETTEXT, 16, (LPARAM) buffer); + ok(1 == r, "Expected: %d, got len %d\n", 1, r); + ok(0 == strcmp(buffer, "\t"), "expected \"\\t\", got \"%s\"\n", buffer); + + DestroyWindow(hwEdit); + + /* single line */ + hwEdit = create_editcontrol(0, 0); + r = get_edit_style(hwEdit); + ok(0 == r, "Wrong style expected 0x%x got: 0x%x\n", 0, r); + + /* set text */ + r = SendMessageA(hwEdit , WM_SETTEXT, 0, (LPARAM) ""); + ok(TRUE == r, "Expected: %d, got: %d\n", TRUE, r); + + r = SendMessageA(hwEdit, WM_CHAR, VK_TAB, 0); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + + /* get text */ + buffer[0] = 0; + r = SendMessageA(hwEdit, WM_GETTEXT, 16, (LPARAM) buffer); + ok(0 == r, "Expected: %d, got len %d\n", 0, r); + ok(0 == strcmp(buffer, ""), "expected \"\", got \"%s\"\n", buffer); + + DestroyWindow(hwEdit); +} + +static void test_edit_dialog(void) +{ + int r; + + /* from bug 11841 */ + r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 0); + ok(333 == r, "Expected %d, got %d\n", 333, r); + r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 1); + ok(111 == r, "Expected %d, got %d\n", 111, r); + r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 2); + ok(444 == r, "Expected %d, got %d\n", 444, r); + + /* more tests for WM_CHAR */ + r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 3); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 4); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 5); + ok(444 == r, "Expected %d, got %d\n", 444, r); + + /* more tests for WM_KEYDOWN + WM_CHAR */ + r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 6); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 7); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_READONLY_DIALOG", NULL, edit_dialog_proc, 8); + ok(444 == r, "Expected %d, got %d\n", 444, r); + + /* tests with an editable edit control */ + r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 0); + ok(333 == r, "Expected %d, got %d\n", 333, r); + r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 1); + ok(111 == r, "Expected %d, got %d\n", 111, r); + r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 2); + ok(444 == r, "Expected %d, got %d\n", 444, r); + + /* tests for WM_CHAR */ + r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 3); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 4); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 5); + ok(444 == r, "Expected %d, got %d\n", 444, r); + + /* tests for WM_KEYDOWN + WM_CHAR */ + r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 6); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 7); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 8); + ok(444 == r, "Expected %d, got %d\n", 444, r); + + /* multiple tab tests */ + r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 9); + ok(22 == r, "Expected %d, got %d\n", 22, r); + r = DialogBoxParamA(hinst, "EDIT_DIALOG", NULL, edit_dialog_proc, 10); + ok(33 == r, "Expected %d, got %d\n", 33, r); +} + +static void test_multi_edit_dialog(void) +{ + int r; + + /* test for multiple edit dialogs (bug 12319) */ + r = DialogBoxParamA(hinst, "MULTI_EDIT_DIALOG", NULL, multi_edit_dialog_proc, 0); + ok(2222 == r, "Expected %d, got %d\n", 2222, r); + r = DialogBoxParamA(hinst, "MULTI_EDIT_DIALOG", NULL, multi_edit_dialog_proc, 1); + ok(1111 == r, "Expected %d, got %d\n", 1111, r); + r = DialogBoxParamA(hinst, "MULTI_EDIT_DIALOG", NULL, multi_edit_dialog_proc, 2); + ok(2222 == r, "Expected %d, got %d\n", 2222, r); + r = DialogBoxParamA(hinst, "MULTI_EDIT_DIALOG", NULL, multi_edit_dialog_proc, 3); + ok(11 == r, "Expected %d, got %d\n", 11, r); +} + +static void test_wantreturn_edit_dialog(void) +{ + int r; + + /* tests for WM_KEYDOWN */ + r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 0); + ok(333 == r, "Expected %d, got %d\n", 333, r); + r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 1); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 2); + ok(444 == r, "Expected %d, got %d\n", 444, r); + + /* tests for WM_CHAR */ + r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 3); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 4); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 5); + ok(444 == r, "Expected %d, got %d\n", 444, r); + + /* tests for WM_KEYDOWN + WM_CHAR */ + r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 6); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 7); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_WANTRETURN_DIALOG", NULL, edit_wantreturn_dialog_proc, 8); + ok(444 == r, "Expected %d, got %d\n", 444, r); +} + +static void test_singleline_wantreturn_edit_dialog(void) +{ + int r; + + /* tests for WM_KEYDOWN */ + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 0); + ok(222 == r, "Expected %d, got %d\n", 222, r); + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 1); + ok(111 == r, "Expected %d, got %d\n", 111, r); + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 2); + ok(444 == r, "Expected %d, got %d\n", 444, r); + + /* tests for WM_CHAR */ + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 3); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 4); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 5); + ok(444 == r, "Expected %d, got %d\n", 444, r); + + /* tests for WM_KEYDOWN + WM_CHAR */ + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 6); + ok(222 == r, "Expected %d, got %d\n", 222, r); + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 7); + ok(111 == r, "Expected %d, got %d\n", 111, r); + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_DIALOG", NULL, edit_singleline_dialog_proc, 8); + ok(444 == r, "Expected %d, got %d\n", 444, r); + + /* tests for WM_KEYDOWN */ + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 0); + ok(222 == r, "Expected %d, got %d\n", 222, r); + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 1); + ok(111 == r, "Expected %d, got %d\n", 111, r); + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 2); + ok(444 == r, "Expected %d, got %d\n", 444, r); + + /* tests for WM_CHAR */ + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 3); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 4); + ok(444 == r, "Expected %d, got %d\n", 444, r); + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 5); + ok(444 == r, "Expected %d, got %d\n", 444, r); + + /* tests for WM_KEYDOWN + WM_CHAR */ + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 6); + ok(222 == r, "Expected %d, got %d\n", 222, r); + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 7); + ok(111 == r, "Expected %d, got %d\n", 111, r); + r = DialogBoxParamA(hinst, "EDIT_SINGLELINE_WANTRETURN_DIALOG", NULL, edit_singleline_dialog_proc, 8); + ok(444 == r, "Expected %d, got %d\n", 444, r); +} + +static int child_edit_wmkeydown_num_messages = 0; +static INT_PTR CALLBACK child_edit_wmkeydown_proc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_DESTROY: + case WM_NCDESTROY: + break; + + default: + child_edit_wmkeydown_num_messages++; + break; + } + + return FALSE; +} + +static void test_child_edit_wmkeydown(void) +{ + HWND hwEdit, hwParent; + int r; + + hwEdit = create_child_editcontrol(0, 0); + hwParent = GetParent(hwEdit); + SetWindowLongPtrA(hwParent, GWLP_WNDPROC, (LONG_PTR)child_edit_wmkeydown_proc); + r = SendMessageA(hwEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001); + ok(1 == r, "expected 1, got %d\n", r); + ok(0 == child_edit_wmkeydown_num_messages, "expected 0, got %d\n", child_edit_wmkeydown_num_messages); + destroy_child_editcontrol(hwEdit); +} + +static BOOL got_en_setfocus = FALSE; +static BOOL got_wm_capturechanged = FALSE; +static LRESULT (CALLBACK *p_edit_proc)(HWND, UINT, WPARAM, LPARAM); + +static LRESULT CALLBACK edit4_wnd_procA(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_COMMAND: + switch (HIWORD(wParam)) + { + case EN_SETFOCUS: + got_en_setfocus = TRUE; + break; + } + break; + case WM_CAPTURECHANGED: + if (hWnd != (HWND)lParam) + { + got_wm_capturechanged = TRUE; + EndMenu(); + } + break; + } + return DefWindowProcA(hWnd, msg, wParam, lParam); +} + +struct context_menu_messages +{ + unsigned int wm_command, em_setsel; +}; + +static struct context_menu_messages menu_messages; + +static LRESULT CALLBACK child_edit_menu_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_ENTERIDLE: + if (wParam == MSGF_MENU) + { + HWND hwndMenu = (HWND)lParam; + MENUBARINFO mbi = { sizeof(mbi) }; + if (GetMenuBarInfo(hwndMenu, OBJID_CLIENT, 0, &mbi)) + { + MENUITEMINFOA mii = { sizeof(MENUITEMINFOA), MIIM_STATE }; + if (GetMenuItemInfoA(mbi.hMenu, EM_SETSEL, FALSE, &mii)) + { + if (mii.fState & MFS_HILITE) + { + PostMessageA(hwnd, WM_KEYDOWN, VK_RETURN, 0x1c0001); + PostMessageA(hwnd, WM_KEYUP, VK_RETURN, 0x1c0001); + } + else + { + PostMessageA(hwnd, WM_KEYDOWN, VK_DOWN, 0x500001); + PostMessageA(hwnd, WM_KEYUP, VK_DOWN, 0x500001); + } + } + } + } + break; + case WM_COMMAND: + menu_messages.wm_command++; + break; + case EM_SETSEL: + menu_messages.em_setsel++; + break; + } + return CallWindowProcA(p_edit_proc, hwnd, msg, wParam, lParam); +} + +static void test_contextmenu(void) +{ + HWND hwndMain, hwndEdit; + MSG msg; + + hwndMain = CreateWindowA(szEditTest4Class, "ET4", WS_OVERLAPPEDWINDOW|WS_VISIBLE, + 0, 0, 200, 200, NULL, NULL, hinst, NULL); + assert(hwndMain); + + hwndEdit = CreateWindowA("EDIT", NULL, + WS_CHILD|WS_BORDER|WS_VISIBLE|ES_LEFT|ES_AUTOHSCROLL, + 0, 0, 150, 50, /* important this not be 0 size. */ + hwndMain, (HMENU) ID_EDITTEST2, hinst, NULL); + assert(hwndEdit); + + SetFocus(NULL); + SetCapture(hwndMain); + SendMessageA(hwndEdit, WM_CONTEXTMENU, (WPARAM)hwndEdit, MAKEWORD(10, 10)); + ok(got_en_setfocus, "edit box didn't get focused\n"); + ok(got_wm_capturechanged, "main window capture did not change\n"); + + DestroyWindow(hwndEdit); + + hwndEdit = CreateWindowA("EDIT", "Test Text", + WS_CHILD | WS_BORDER | WS_VISIBLE, + 0, 0, 100, 100, + hwndMain, NULL, hinst, NULL); + memset(&menu_messages, 0, sizeof(menu_messages)); + p_edit_proc = (void*)SetWindowLongPtrA(hwndEdit, GWLP_WNDPROC, + (ULONG_PTR)child_edit_menu_proc); + + SetFocus(hwndEdit); + SendMessageA(hwndEdit, WM_SETTEXT, 0, (LPARAM)"foo"); + SendMessageA(hwndEdit, WM_CONTEXTMENU, (WPARAM)hwndEdit, MAKEWORD(-1, -1)); + while (PeekMessageA(&msg, hwndEdit, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + ok(menu_messages.wm_command == 0, + "Expected no WM_COMMAND messages, got %d\n", menu_messages.wm_command); + ok(menu_messages.em_setsel == 1, + "Expected 1 EM_SETSEL message, got %d\n", menu_messages.em_setsel); + + DestroyWindow(hwndEdit); + DestroyWindow(hwndMain); +} + +static BOOL register_classes(void) +{ + WNDCLASSA test2; + WNDCLASSA test3; + WNDCLASSA test4; + WNDCLASSA text_position; + + test2.style = 0; + test2.lpfnWndProc = ET2_WndProc; + test2.cbClsExtra = 0; + test2.cbWndExtra = 0; + test2.hInstance = hinst; + test2.hIcon = NULL; + test2.hCursor = LoadCursorA (NULL, (LPCSTR)IDC_ARROW); + test2.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + test2.lpszMenuName = NULL; + test2.lpszClassName = szEditTest2Class; + if (!RegisterClassA(&test2)) return FALSE; + + test3.style = 0; + test3.lpfnWndProc = edit3_wnd_procA; + test3.cbClsExtra = 0; + test3.cbWndExtra = 0; + test3.hInstance = hinst; + test3.hIcon = 0; + test3.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); + test3.hbrBackground = GetStockObject(WHITE_BRUSH); + test3.lpszMenuName = NULL; + test3.lpszClassName = szEditTest3Class; + if (!RegisterClassA(&test3)) return FALSE; + + test4.style = 0; + test4.lpfnWndProc = edit4_wnd_procA; + test4.cbClsExtra = 0; + test4.cbWndExtra = 0; + test4.hInstance = hinst; + test4.hIcon = NULL; + test4.hCursor = LoadCursorA (NULL, (LPCSTR)IDC_ARROW); + test4.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + test4.lpszMenuName = NULL; + test4.lpszClassName = szEditTest4Class; + if (!RegisterClassA(&test4)) return FALSE; + + text_position.style = CS_HREDRAW | CS_VREDRAW; + text_position.cbClsExtra = 0; + text_position.cbWndExtra = 0; + text_position.hInstance = hinst; + text_position.hIcon = NULL; + text_position.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW); + text_position.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1); + text_position.lpszMenuName = NULL; + text_position.lpszClassName = szEditTextPositionClass; + text_position.lpfnWndProc = DefWindowProcA; + if (!RegisterClassA(&text_position)) return FALSE; + + return TRUE; +} + +static void UnregisterWindowClasses (void) +{ + UnregisterClassA(szEditTest2Class, hinst); + UnregisterClassA(szEditTest3Class, hinst); + UnregisterClassA(szEditTest4Class, hinst); + UnregisterClassA(szEditTextPositionClass, hinst); +} + +static void test_fontsize(void) +{ + HWND hwEdit; + HFONT hfont; + HDC hDC; + LOGFONTA lf; + LONG r; + char szLocalString[MAXLEN]; + int dpi; + + hDC = GetDC(NULL); + dpi = GetDeviceCaps(hDC, LOGPIXELSY); + ReleaseDC(NULL, hDC); + + memset(&lf,0,sizeof(LOGFONTA)); + strcpy(lf.lfFaceName,"Arial"); + lf.lfHeight = -300; /* taller than the edit box */ + lf.lfWeight = 500; + hfont = CreateFontIndirectA(&lf); + + trace("EDIT: Oversized font (Multi line)\n"); + hwEdit= CreateWindowA("EDIT", NULL, ES_MULTILINE|ES_AUTOHSCROLL, + 0, 0, (150 * dpi) / 96, (50 * dpi) / 96, NULL, NULL, + hinst, NULL); + + SendMessageA(hwEdit,WM_SETFONT,(WPARAM)hfont,0); + + if (winetest_interactive) + ShowWindow (hwEdit, SW_SHOW); + + r = SendMessageA(hwEdit, WM_CHAR, 'A', 1); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + r = SendMessageA(hwEdit, WM_CHAR, 'B', 1); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + r = SendMessageA(hwEdit, WM_CHAR, 'C', 1); + ok(1 == r, "Expected: %d, got: %d\n", 1, r); + + GetWindowTextA(hwEdit, szLocalString, MAXLEN); + ok(strcmp(szLocalString, "ABC")==0, + "Wrong contents of edit: %s\n", szLocalString); + + r = SendMessageA(hwEdit, EM_POSFROMCHAR,0,0); + ok(r != -1,"EM_POSFROMCHAR failed index 0\n"); + r = SendMessageA(hwEdit, EM_POSFROMCHAR,1,0); + ok(r != -1,"EM_POSFROMCHAR failed index 1\n"); + r = SendMessageA(hwEdit, EM_POSFROMCHAR,2,0); + ok(r != -1,"EM_POSFROMCHAR failed index 2\n"); + r = SendMessageA(hwEdit, EM_POSFROMCHAR,3,0); + ok(r == -1,"EM_POSFROMCHAR succeeded index 3\n"); + + DestroyWindow (hwEdit); + DeleteObject(hfont); +} + +struct dialog_mode_messages +{ + int wm_getdefid, wm_close, wm_command, wm_nextdlgctl; +}; + +static struct dialog_mode_messages dm_messages; + +static void zero_dm_messages(void) +{ + dm_messages.wm_command = 0; + dm_messages.wm_close = 0; + dm_messages.wm_getdefid = 0; + dm_messages.wm_nextdlgctl = 0; +} + +#define test_dm_messages(wmcommand, wmclose, wmgetdefid, wmnextdlgctl) \ + ok(dm_messages.wm_command == wmcommand, "expected %d WM_COMMAND messages, " \ + "got %d\n", wmcommand, dm_messages.wm_command); \ + ok(dm_messages.wm_close == wmclose, "expected %d WM_CLOSE messages, " \ + "got %d\n", wmclose, dm_messages.wm_close); \ + ok(dm_messages.wm_getdefid == wmgetdefid, "expected %d WM_GETDIFID messages, " \ + "got %d\n", wmgetdefid, dm_messages.wm_getdefid);\ + ok(dm_messages.wm_nextdlgctl == wmnextdlgctl, "expected %d WM_NEXTDLGCTL messages, " \ + "got %d\n", wmnextdlgctl, dm_messages.wm_nextdlgctl) + +static LRESULT CALLBACK dialog_mode_wnd_proc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) +{ + switch (iMsg) + { + case WM_COMMAND: + dm_messages.wm_command++; + break; + case DM_GETDEFID: + dm_messages.wm_getdefid++; + return MAKELONG(ID_EDITTESTDBUTTON, DC_HASDEFID); + case WM_NEXTDLGCTL: + dm_messages.wm_nextdlgctl++; + break; + case WM_CLOSE: + dm_messages.wm_close++; + break; + } + + return DefWindowProcA(hwnd, iMsg, wParam, lParam); +} + +static void test_dialogmode(void) +{ + HWND hwEdit, hwParent, hwButton; + MSG msg= {0}; + int len, r; + hwEdit = create_child_editcontrol(ES_MULTILINE, 0); + + r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0x1c0001); + ok(1 == r, "expected 1, got %d\n", r); + len = SendMessageA(hwEdit, WM_GETTEXTLENGTH, 0, 0); + ok(11 == len, "expected 11, got %d\n", len); + + r = SendMessageA(hwEdit, WM_GETDLGCODE, 0, 0); + ok(0x8d == r, "expected 0x8d, got 0x%x\n", r); + + r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0x1c0001); + ok(1 == r, "expected 1, got %d\n", r); + len = SendMessageA(hwEdit, WM_GETTEXTLENGTH, 0, 0); + ok(13 == len, "expected 13, got %d\n", len); + + r = SendMessageA(hwEdit, WM_GETDLGCODE, 0, (LPARAM)&msg); + ok(0x8d == r, "expected 0x8d, got 0x%x\n", r); + r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0x1c0001); + ok(1 == r, "expected 1, got %d\n", r); + len = SendMessageA(hwEdit, WM_GETTEXTLENGTH, 0, 0); + ok(13 == len, "expected 13, got %d\n", len); + + r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0x1c0001); + ok(1 == r, "expected 1, got %d\n", r); + len = SendMessageA(hwEdit, WM_GETTEXTLENGTH, 0, 0); + ok(13 == len, "expected 13, got %d\n", len); + + destroy_child_editcontrol(hwEdit); + + hwEdit = create_editcontrol(ES_MULTILINE, 0); + + r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0x1c0001); + ok(1 == r, "expected 1, got %d\n", r); + len = SendMessageA(hwEdit, WM_GETTEXTLENGTH, 0, 0); + ok(11 == len, "expected 11, got %d\n", len); + + msg.hwnd = hwEdit; + msg.message = WM_KEYDOWN; + msg.wParam = VK_BACK; + msg.lParam = 0xe0001; + r = SendMessageA(hwEdit, WM_GETDLGCODE, VK_BACK, (LPARAM)&msg); + ok(0x8d == r, "expected 0x8d, got 0x%x\n", r); + + r = SendMessageA(hwEdit, WM_CHAR, VK_RETURN, 0x1c0001); + ok(1 == r, "expected 1, got %d\n", r); + len = SendMessageA(hwEdit, WM_GETTEXTLENGTH, 0, 0); + ok(11 == len, "expected 11, got %d\n", len); + + DestroyWindow(hwEdit); + + hwEdit = create_child_editcontrol(0, 0); + hwParent = GetParent(hwEdit); + SetWindowLongPtrA(hwParent, GWLP_WNDPROC, (LONG_PTR)dialog_mode_wnd_proc); + + zero_dm_messages(); + r = SendMessageA(hwEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001); + ok(1 == r, "expected 1, got %d\n", r); + test_dm_messages(0, 0, 0, 0); + zero_dm_messages(); + + r = SendMessageA(hwEdit, WM_KEYDOWN, VK_TAB, 0xf0001); + ok(1 == r, "expected 1, got %d\n", r); + test_dm_messages(0, 0, 0, 0); + zero_dm_messages(); + + msg.hwnd = hwEdit; + msg.message = WM_KEYDOWN; + msg.wParam = VK_TAB; + msg.lParam = 0xf0001; + r = SendMessageA(hwEdit, WM_GETDLGCODE, VK_TAB, (LPARAM)&msg); + ok(0x89 == r, "expected 0x89, got 0x%x\n", r); + test_dm_messages(0, 0, 0, 0); + zero_dm_messages(); + + r = SendMessageA(hwEdit, WM_KEYDOWN, VK_TAB, 0xf0001); + ok(1 == r, "expected 1, got %d\n", r); + test_dm_messages(0, 0, 0, 0); + zero_dm_messages(); + + destroy_child_editcontrol(hwEdit); + + hwEdit = create_child_editcontrol(ES_MULTILINE, 0); + hwParent = GetParent(hwEdit); + SetWindowLongPtrA(hwParent, GWLP_WNDPROC, (LONG_PTR)dialog_mode_wnd_proc); + + r = SendMessageA(hwEdit, WM_KEYDOWN, VK_TAB, 0xf0001); + ok(1 == r, "expected 1, got %d\n", r); + test_dm_messages(0, 0, 0, 0); + zero_dm_messages(); + + msg.hwnd = hwEdit; + msg.message = WM_KEYDOWN; + msg.wParam = VK_ESCAPE; + msg.lParam = 0x10001; + r = SendMessageA(hwEdit, WM_GETDLGCODE, VK_ESCAPE, (LPARAM)&msg); + ok(0x8d == r, "expected 0x8d, got 0x%x\n", r); + test_dm_messages(0, 0, 0, 0); + zero_dm_messages(); + + r = SendMessageA(hwEdit, WM_KEYDOWN, VK_ESCAPE, 0x10001); + ok(1 == r, "expected 1, got %d\n", r); + test_dm_messages(0, 0, 0, 0); + zero_dm_messages(); + + r = SendMessageA(hwEdit, WM_KEYDOWN, VK_TAB, 0xf0001); + ok(1 == r, "expected 1, got %d\n", r); + test_dm_messages(0, 0, 0, 1); + zero_dm_messages(); + + r = SendMessageA(hwEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001); + ok(1 == r, "expected 1, got %d\n", r); + test_dm_messages(0, 0, 1, 0); + zero_dm_messages(); + + hwButton = CreateWindowA("BUTTON", "OK", WS_VISIBLE|WS_CHILD|BS_PUSHBUTTON, + 100, 100, 50, 20, hwParent, (HMENU)ID_EDITTESTDBUTTON, hinst, NULL); + ok(hwButton!=NULL, "CreateWindow failed with error code %d\n", GetLastError()); + + r = SendMessageA(hwEdit, WM_KEYDOWN, VK_RETURN, 0x1c0001); + ok(1 == r, "expected 1, got %d\n", r); + test_dm_messages(0, 0, 1, 1); + zero_dm_messages(); + + DestroyWindow(hwButton); + destroy_child_editcontrol(hwEdit); +} + +static void test_EM_GETHANDLE(void) +{ + static const WCHAR str1W[] = {'1','1','1','1','+','1','1','1','1','+','1','1','1','1','#',0}; + static const WCHAR str2W[] = {'2','2','2','2','-','2','2','2','2','-','2','2','2','2','-','2','2','2','2','#',0}; + static const char str0[] = "untouched"; + static const char str1[] = "1111+1111+1111#"; + static const char str1_1[] = "2111+1111+1111#"; + static const char str2[] = "2222-2222-2222-2222#"; + static const char str3[] = "3333*3333*3333*3333*3333#"; + CHAR current[42]; + HWND hEdit; + HLOCAL hmem; + HLOCAL hmem2; + HLOCAL halloc; + WCHAR *buffer; + int len; + int r; + + trace("EDIT: EM_GETHANDLE\n"); + + /* EM_GETHANDLE is not supported for a single line edit control */ + hEdit = create_editcontrol(WS_BORDER, 0); + ok(hEdit != NULL, "got %p (expected != NULL)\n", hEdit); + + hmem = (HGLOBAL) SendMessageA(hEdit, EM_GETHANDLE, 0, 0); + ok(hmem == NULL, "got %p (expected NULL)\n", hmem); + DestroyWindow(hEdit); + + /* EM_GETHANDLE needs a multiline edit control */ + hEdit = create_editcontrol(WS_BORDER | ES_MULTILINE, 0); + ok(hEdit != NULL, "got %p (expected != NULL)\n", hEdit); + + /* set some text */ + r = SendMessageA(hEdit, WM_SETTEXT, 0, (LPARAM)str1); + len = SendMessageA(hEdit, WM_GETTEXTLENGTH, 0, 0); + ok((r == 1) && (len == lstrlenA(str1)), "got %d and %d (expected 1 and %d)\n", r, len, lstrlenA(str1)); + + lstrcpyA(current, str0); + r = SendMessageA(hEdit, WM_GETTEXT, sizeof(current), (LPARAM)current); + ok((r == lstrlenA(str1)) && !lstrcmpA(current, str1), + "got %d and \"%s\" (expected %d and \"%s\")\n", r, current, lstrlenA(str1), str1); + + hmem = (HGLOBAL) SendMessageA(hEdit, EM_GETHANDLE, 0, 0); + ok(hmem != NULL, "got %p (expected != NULL)\n", hmem); + /* The buffer belongs to the app now. According to MSDN, the app has to LocalFree the + buffer, LocalAlloc a new buffer and pass it to the edit control with EM_SETHANDLE. */ + + buffer = LocalLock(hmem); + ok(buffer != NULL, "got %p (expected != NULL)\n", buffer); + len = lstrlenW(buffer); +todo_wine + ok(len == lstrlenW(str1W) && !lstrcmpW(buffer, str1W), "Unexpected buffer contents %s, length %d.\n", + wine_dbgstr_w(buffer), len); + LocalUnlock(hmem); + + /* See if WM_GETTEXTLENGTH/WM_GETTEXT still work. */ + len = SendMessageA(hEdit, WM_GETTEXTLENGTH, 0, 0); + ok(len == lstrlenA(str1), "Unexpected text length %d.\n", len); + + lstrcpyA(current, str0); + r = SendMessageA(hEdit, WM_GETTEXT, sizeof(current), (LPARAM)current); + ok((r == lstrlenA(str1)) && !lstrcmpA(current, str1), + "Unexpected retval %d and text \"%s\" (expected %d and \"%s\")\n", r, current, lstrlenA(str1), str1); + + /* Application altered buffer contents, see if WM_GETTEXTLENGTH/WM_GETTEXT pick that up. */ + buffer = LocalLock(hmem); + ok(buffer != NULL, "got %p (expected != NULL)\n", buffer); + buffer[0] = '2'; + LocalUnlock(hmem); + + len = SendMessageA(hEdit, WM_GETTEXTLENGTH, 0, 0); + ok(len == lstrlenA(str1_1), "Unexpected text length %d.\n", len); + + lstrcpyA(current, str0); + r = SendMessageA(hEdit, WM_GETTEXT, sizeof(current), (LPARAM)current); +todo_wine + ok(r == lstrlenA(str1_1) && !lstrcmpA(current, str1_1), + "Unexpected retval %d and text \"%s\" (expected %d and \"%s\")\n", r, current, lstrlenA(str1_1), str1_1); + + /* See if WM_SETTEXT/EM_REPLACESEL work. */ + r = SendMessageA(hEdit, WM_SETTEXT, 0, (LPARAM)str1); + ok(r, "Failed to set text.\n"); + + buffer = LocalLock(hmem); +todo_wine + ok(buffer != NULL && buffer[0] == '1', "Unexpected buffer contents\n"); + LocalUnlock(hmem); + + r = SendMessageA(hEdit, EM_REPLACESEL, 0, (LPARAM)str1_1); + ok(r, "Failed to replace selection.\n"); + + buffer = LocalLock(hmem); +todo_wine + ok(buffer != NULL && buffer[0] == '2', "Unexpected buffer contents\n"); + LocalUnlock(hmem); + + /* use LocalAlloc first to get a different handle */ + halloc = LocalAlloc(LMEM_MOVEABLE, 42); + ok(halloc != NULL, "got %p (expected != NULL)\n", halloc); + /* prepare our new memory */ + buffer = LocalLock(halloc); + ok(buffer != NULL, "got %p (expected != NULL)\n", buffer); + lstrcpyW(buffer, str2W); + LocalUnlock(halloc); + + /* LocalFree the old memory handle before EM_SETHANDLE the new handle */ + LocalFree(hmem); + /* use LocalAlloc after the LocalFree to likely consume the handle */ + hmem2 = LocalAlloc(LMEM_MOVEABLE, 42); + ok(hmem2 != NULL, "got %p (expected != NULL)\n", hmem2); + + SendMessageA(hEdit, EM_SETHANDLE, (WPARAM)halloc, 0); + + len = SendMessageA(hEdit, WM_GETTEXTLENGTH, 0, 0); +todo_wine + ok(len == lstrlenA(str2), "got %d (expected %d)\n", len, lstrlenA(str2)); + + lstrcpyA(current, str0); + r = SendMessageA(hEdit, WM_GETTEXT, sizeof(current), (LPARAM)current); +todo_wine + ok(r == lstrlenA(str2) && !lstrcmpA(current, str2), + "got %d and \"%s\" (expected %d and \"%s\")\n", r, current, lstrlenA(str2), str2); + + /* set a different text */ + r = SendMessageA(hEdit, WM_SETTEXT, 0, (LPARAM)str3); + len = SendMessageA(hEdit, WM_GETTEXTLENGTH, 0, 0); + ok((r == 1) && (len == lstrlenA(str3)), "got %d and %d (expected 1 and %d)\n", r, len, lstrlenA(str3)); + + lstrcpyA(current, str0); + r = SendMessageA(hEdit, WM_GETTEXT, sizeof(current), (LPARAM)current); + ok((r == lstrlenA(str3)) && !lstrcmpA(current, str3), + "got %d and \"%s\" (expected %d and \"%s\")\n", r, current, lstrlenA(str3), str3); + + LocalFree(hmem2); + DestroyWindow(hEdit); + + /* Some apps have bugs ... */ + hEdit = create_editcontrol(WS_BORDER | ES_MULTILINE, 0); + + /* set some text */ + r = SendMessageA(hEdit, WM_SETTEXT, 0, (LPARAM)str1); + len = SendMessageA(hEdit, WM_GETTEXTLENGTH, 0, 0); + ok((r == 1) && (len == lstrlenA(str1)), "got %d and %d (expected 1 and %d)\n", r, len, lstrlenA(str1)); + + /* everything is normal up to EM_GETHANDLE */ + hmem = (HGLOBAL) SendMessageA(hEdit, EM_GETHANDLE, 0, 0); + /* Some messages still work while other messages fail. + After LocalFree the memory handle, messages can crash the app */ + + /* A buggy editor used EM_GETHANDLE twice */ + hmem2 = (HGLOBAL) SendMessageA(hEdit, EM_GETHANDLE, 0, 0); + ok(hmem2 == hmem, "got %p (expected %p)\n", hmem2, hmem); + + /* Let the edit control free the memory handle */ + SendMessageA(hEdit, EM_SETHANDLE, (WPARAM)hmem2, 0); + + DestroyWindow(hEdit); +} + +static void test_paste(void) +{ + static const char *str = "this is a simple text"; + static const char *str2 = "first line\r\nsecond line"; + HWND hEdit, hMultilineEdit; + HANDLE hmem, hmem_ret; + char *buffer; + int r, len; + + hEdit = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + hMultilineEdit = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE, 0); + + /* Prepare clipboard data with simple text */ + hmem = GlobalAlloc(GMEM_MOVEABLE, 255); + ok(hmem != NULL, "got %p (expected != NULL)\n", hmem); + buffer = GlobalLock(hmem); + ok(buffer != NULL, "got %p (expected != NULL)\n", buffer); + strcpy(buffer, str); + GlobalUnlock(hmem); + + r = OpenClipboard(hEdit); + ok(r == TRUE, "expected %d, got %d\n", TRUE, r); + r = EmptyClipboard(); + ok(r == TRUE, "expected %d, got %d\n", TRUE, r); + hmem_ret = SetClipboardData(CF_TEXT, hmem); + ok(hmem_ret == hmem, "expected %p, got %p\n", hmem, hmem_ret); + r = CloseClipboard(); + ok(r == TRUE, "expected %d, got %d\n", TRUE, r); + + /* Paste single line */ + SendMessageA(hEdit, WM_SETTEXT, 0, (LPARAM)""); + r = SendMessageA(hEdit, WM_PASTE, 0, 0); + len = SendMessageA(hEdit, WM_GETTEXTLENGTH, 0, 0); + ok(strlen(str) == len, "got %d\n", len); + + /* Prepare clipboard data with multiline text */ + hmem = GlobalAlloc(GMEM_MOVEABLE, 255); + ok(hmem != NULL, "got %p (expected != NULL)\n", hmem); + buffer = GlobalLock(hmem); + ok(buffer != NULL, "got %p (expected != NULL)\n", buffer); + strcpy(buffer, str2); + GlobalUnlock(hmem); + + r = OpenClipboard(hEdit); + ok(r == TRUE, "expected %d, got %d\n", TRUE, r); + r = EmptyClipboard(); + ok(r == TRUE, "expected %d, got %d\n", TRUE, r); + hmem_ret = SetClipboardData(CF_TEXT, hmem); + ok(hmem_ret == hmem, "expected %p, got %p\n", hmem, hmem_ret); + r = CloseClipboard(); + ok(r == TRUE, "expected %d, got %d\n", TRUE, r); + + /* Paste multiline text in singleline edit - should be cut */ + SendMessageA(hEdit, WM_SETTEXT, 0, (LPARAM)""); + r = SendMessageA(hEdit, WM_PASTE, 0, 0); + len = SendMessageA(hEdit, WM_GETTEXTLENGTH, 0, 0); + ok(strlen("first line") == len, "got %d\n", len); + + /* Paste multiline text in multiline edit */ + SendMessageA(hMultilineEdit, WM_SETTEXT, 0, (LPARAM)""); + r = SendMessageA(hMultilineEdit, WM_PASTE, 0, 0); + len = SendMessageA(hMultilineEdit, WM_GETTEXTLENGTH, 0, 0); + ok(strlen(str2) == len, "got %d\n", len); + + /* Cleanup */ + DestroyWindow(hEdit); + DestroyWindow(hMultilineEdit); +} + +static void test_EM_GETLINE(void) +{ + HWND hwnd[2]; + int i; + + hwnd[0] = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + hwnd[1] = create_editcontrolW(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0); + + for (i = 0; i < sizeof(hwnd)/sizeof(hwnd[0]); i++) + { + static const WCHAR strW[] = {'t','e','x','t',0}; + static const char *str = "text"; + WCHAR buffW[16]; + char buff[16]; + int r; + + todo_wine_if(i == 0) + ok(IsWindowUnicode(hwnd[i]), "Expected unicode window.\n"); + + SendMessageA(hwnd[i], WM_SETTEXT, 0, (LPARAM)str); + + memset(buff, 0, sizeof(buff)); + *(WORD *)buff = sizeof(buff); + r = SendMessageA(hwnd[i], EM_GETLINE, 0, (LPARAM)buff); + ok(r == strlen(str), "Failed to get a line %d.\n", r); + ok(!strcmp(buff, str), "Unexpected line data %s.\n", buff); + + memset(buff, 0, sizeof(buff)); + *(WORD *)buff = sizeof(buff); + r = SendMessageA(hwnd[i], EM_GETLINE, 1, (LPARAM)buff); + ok(r == strlen(str), "Failed to get a line %d.\n", r); + ok(!strcmp(buff, str), "Unexpected line data %s.\n", buff); + + memset(buffW, 0, sizeof(buffW)); + *(WORD *)buffW = sizeof(buffW)/sizeof(buffW[0]); + r = SendMessageW(hwnd[i], EM_GETLINE, 0, (LPARAM)buffW); + ok(r == lstrlenW(strW), "Failed to get a line %d.\n", r); + ok(!lstrcmpW(buffW, strW), "Unexpected line data %s.\n", wine_dbgstr_w(buffW)); + + memset(buffW, 0, sizeof(buffW)); + *(WORD *)buffW = sizeof(buffW)/sizeof(buffW[0]); + r = SendMessageW(hwnd[i], EM_GETLINE, 1, (LPARAM)buffW); + ok(r == lstrlenW(strW), "Failed to get a line %d.\n", r); + ok(!lstrcmpW(buffW, strW), "Unexpected line data %s.\n", wine_dbgstr_w(buffW)); + + DestroyWindow(hwnd[i]); + } +} + +START_TEST(edit) +{ + ULONG_PTR ctx_cookie; + HANDLE hCtx; + BOOL b; + + if (!load_v6_module(&ctx_cookie, &hCtx)) + return; + + hinst = GetModuleHandleA(NULL); + b = register_classes(); + ok(b, "Failed to register test classes.\n"); + if (!b) return; + + test_edit_control_1(); + test_edit_control_2(); + test_edit_control_3(); + test_char_from_pos(); + test_edit_control_5(); + test_edit_control_6(); + test_edit_control_limittext(); + test_edit_control_scroll(); + test_margins(); + test_margins_font_change(); + test_text_position(); + test_espassword(); + test_undo(); + test_enter(); + test_tab(); + test_edit_dialog(); + test_multi_edit_dialog(); + test_wantreturn_edit_dialog(); + test_singleline_wantreturn_edit_dialog(); + test_child_edit_wmkeydown(); + test_fontsize(); + test_dialogmode(); + test_contextmenu(); + test_EM_GETHANDLE(); + test_paste(); + test_EM_GETLINE(); + + UnregisterWindowClasses(); + + unload_v6_module(ctx_cookie, hCtx); +} diff --git a/modules/rostests/winetests/comctl32/imagelist.c b/modules/rostests/winetests/comctl32/imagelist.c index 57e7f5c0692..55466ac37aa 100644 --- a/modules/rostests/winetests/comctl32/imagelist.c +++ b/modules/rostests/winetests/comctl32/imagelist.c @@ -49,6 +49,7 @@ typedef struct _ILHEAD #include "poppack.h" static HIMAGELIST (WINAPI *pImageList_Create)(int, int, UINT, int, int); +static BOOL (WINAPI *pImageList_Destroy)(HIMAGELIST); static int (WINAPI *pImageList_Add)(HIMAGELIST, HBITMAP, HBITMAP); static BOOL (WINAPI *pImageList_DrawIndirect)(IMAGELISTDRAWPARAMS*); static BOOL (WINAPI *pImageList_SetImageCount)(HIMAGELIST,UINT); @@ -56,6 +57,22 @@ static HRESULT (WINAPI *pImageList_CoCreateInstance)(REFCLSID,const IUnknown *, REFIID,void **); static HRESULT (WINAPI *pHIMAGELIST_QueryInterface)(HIMAGELIST,REFIID,void **); static int (WINAPI *pImageList_SetColorTable)(HIMAGELIST,int,int,RGBQUAD*); +static DWORD (WINAPI *pImageList_GetFlags)(HIMAGELIST); +static BOOL (WINAPI *pImageList_BeginDrag)(HIMAGELIST, int, int, int); +static HIMAGELIST (WINAPI *pImageList_GetDragImage)(POINT *, POINT *); +static void (WINAPI *pImageList_EndDrag)(void); +static INT (WINAPI *pImageList_GetImageCount)(HIMAGELIST); +static BOOL (WINAPI *pImageList_SetDragCursorImage)(HIMAGELIST, int, int, int); +static BOOL (WINAPI *pImageList_GetIconSize)(HIMAGELIST, int *, int *); +static BOOL (WINAPI *pImageList_Remove)(HIMAGELIST, int); +static INT (WINAPI *pImageList_ReplaceIcon)(HIMAGELIST, int, HICON); +static BOOL (WINAPI *pImageList_Replace)(HIMAGELIST, int, HBITMAP, HBITMAP); +static HIMAGELIST (WINAPI *pImageList_Merge)(HIMAGELIST, int, HIMAGELIST, int, int, int); +static BOOL (WINAPI *pImageList_GetImageInfo)(HIMAGELIST, int, IMAGEINFO *); +static BOOL (WINAPI *pImageList_Write)(HIMAGELIST, IStream *); +static HIMAGELIST (WINAPI *pImageList_Read)(IStream *); +static BOOL (WINAPI *pImageList_Copy)(HIMAGELIST, int, HIMAGELIST, int, UINT); +static HIMAGELIST (WINAPI *pImageList_LoadImageW)(HINSTANCE, LPCWSTR, int, int, COLORREF, UINT, UINT); static HINSTANCE hinst; @@ -69,6 +86,11 @@ static void force_redraw(HWND hwnd) Sleep(1000); } +static BOOL is_v6_test(void) +{ + return pHIMAGELIST_QueryInterface != NULL; +} + /* These macros build cursor/bitmap data in 4x4 pixel blocks */ #define B(x,y) ((x?0xf0:0)|(y?0xf:0)) #define ROW1(a,b,c,d,e,f,g,h) B(a,b),B(c,d),B(e,f),B(g,h) @@ -112,14 +134,16 @@ static const BYTE bitmap_bits[48*48/8] = static HIMAGELIST createImageList(int cx, int cy) { /* Create an ImageList and put an image into it */ - HIMAGELIST himl = ImageList_Create(cx, cy, ILC_COLOR, 1, 1); + HIMAGELIST himl = pImageList_Create(cx, cy, ILC_COLOR, 1, 1); HBITMAP hbm = CreateBitmap(48, 48, 1, 1, bitmap_bits); - ImageList_Add(himl, hbm, NULL); + + ok(himl != NULL, "Failed to create image list, %d x %d.\n", cx, cy); + pImageList_Add(himl, hbm, NULL); DeleteObject(hbm); return himl; } -static HWND create_a_window(void) +static HWND create_window(void) { char className[] = "bmwnd"; char winName[] = "Test Bitmap"; @@ -246,41 +270,41 @@ static void test_begindrag(void) int count; POINT hotspot; - count = ImageList_GetImageCount(himl); + count = pImageList_GetImageCount(himl); ok(count > 2, "Tests need an ImageList with more than 2 images\n"); /* Two BeginDrag() without EndDrag() in between */ - ret = ImageList_BeginDrag(himl, 1, 0, 0); - drag = ImageList_GetDragImage(NULL, NULL); + ret = pImageList_BeginDrag(himl, 1, 0, 0); + drag = pImageList_GetDragImage(NULL, NULL); ok(ret && drag, "ImageList_BeginDrag() failed\n"); - ret = ImageList_BeginDrag(himl, 0, 3, 5); + ret = pImageList_BeginDrag(himl, 0, 3, 5); ok(!ret, "ImageList_BeginDrag() returned TRUE\n"); - drag = ImageList_GetDragImage(NULL, &hotspot); + drag = pImageList_GetDragImage(NULL, &hotspot); ok(!!drag, "No active ImageList drag left\n"); ok(hotspot.x == 0 && hotspot.y == 0, "New ImageList drag was created\n"); - ImageList_EndDrag(); - drag = ImageList_GetDragImage(NULL, NULL); + pImageList_EndDrag(); + drag = pImageList_GetDragImage(NULL, NULL); ok(!drag, "ImageList drag was not destroyed\n"); /* Invalid image index */ - ImageList_BeginDrag(himl, 0, 0, 0); - ret = ImageList_BeginDrag(himl, count, 3, 5); + pImageList_BeginDrag(himl, 0, 0, 0); + ret = pImageList_BeginDrag(himl, count, 3, 5); ok(!ret, "ImageList_BeginDrag() returned TRUE\n"); - drag = ImageList_GetDragImage(NULL, &hotspot); + drag = pImageList_GetDragImage(NULL, &hotspot); ok(drag && hotspot.x == 0 && hotspot.y == 0, "Active drag should not have been canceled\n"); - ImageList_EndDrag(); - drag = ImageList_GetDragImage(NULL, NULL); + pImageList_EndDrag(); + drag = pImageList_GetDragImage(NULL, NULL); ok(!drag, "ImageList drag was not destroyed\n"); /* Invalid negative image indexes succeed */ - ret = ImageList_BeginDrag(himl, -17, 0, 0); - drag = ImageList_GetDragImage(NULL, NULL); + ret = pImageList_BeginDrag(himl, -17, 0, 0); + drag = pImageList_GetDragImage(NULL, NULL); ok(ret && drag, "ImageList drag was created\n"); - ImageList_EndDrag(); - ret = ImageList_BeginDrag(himl, -1, 0, 0); - drag = ImageList_GetDragImage(NULL, NULL); + pImageList_EndDrag(); + ret = pImageList_BeginDrag(himl, -1, 0, 0); + drag = pImageList_GetDragImage(NULL, NULL); ok(ret && drag, "ImageList drag was created\n"); - ImageList_EndDrag(); - ImageList_Destroy(himl); + pImageList_EndDrag(); + pImageList_Destroy(himl); } static void test_hotspot(void) @@ -304,7 +328,7 @@ static void test_hotspot(void) int i, j, ret; HIMAGELIST himl1 = createImageList(SIZEX1, SIZEY1); HIMAGELIST himl2 = createImageList(SIZEX2, SIZEY2); - HWND hwnd = create_a_window(); + HWND hwnd = create_window(); for (i = 0; i < HOTSPOTS_MAX; i++) { @@ -318,25 +342,25 @@ static void test_hotspot(void) HIMAGELIST himlNew; POINT ppt; - ret = ImageList_BeginDrag(himl1, 0, dx1, dy1); + ret = pImageList_BeginDrag(himl1, 0, dx1, dy1); ok(ret != 0, "BeginDrag failed for { %d, %d }\n", dx1, dy1); sprintf(loc, "BeginDrag (%d,%d)\n", i, j); show_image(hwnd, himl1, 0, max(SIZEX1, SIZEY1), loc, TRUE); /* check merging the dragged image with a second image */ - ret = ImageList_SetDragCursorImage(himl2, 0, dx2, dy2); + ret = pImageList_SetDragCursorImage(himl2, 0, dx2, dy2); ok(ret != 0, "SetDragCursorImage failed for {%d, %d}{%d, %d}\n", dx1, dy1, dx2, dy2); sprintf(loc, "SetDragCursorImage (%d,%d)\n", i, j); show_image(hwnd, himl2, 0, max(SIZEX2, SIZEY2), loc, TRUE); /* check new hotspot, it should be the same like the old one */ - himlNew = ImageList_GetDragImage(NULL, &ppt); + himlNew = pImageList_GetDragImage(NULL, &ppt); ok(ppt.x == dx1 && ppt.y == dy1, "Expected drag hotspot [%d,%d] got [%d,%d]\n", dx1, dy1, ppt.x, ppt.y); /* check size of new dragged image */ - ImageList_GetIconSize(himlNew, &newx, &newy); + pImageList_GetIconSize(himlNew, &newx, &newy); correctx = max(SIZEX1, max(SIZEX2 + dx2, SIZEX1 - dx2)); correcty = max(SIZEY1, max(SIZEY2 + dy2, SIZEY1 - dy2)); ok(newx == correctx && newy == correcty, @@ -344,7 +368,7 @@ static void test_hotspot(void) correctx, correcty, newx, newy); sprintf(loc, "GetDragImage (%d,%d)\n", i, j); show_image(hwnd, himlNew, 0, max(correctx, correcty), loc, TRUE); - ImageList_EndDrag(); + pImageList_EndDrag(); } } #undef SIZEX1 @@ -352,8 +376,8 @@ static void test_hotspot(void) #undef SIZEX2 #undef SIZEY2 #undef HOTSPOTS_MAX - ImageList_Destroy(himl2); - ImageList_Destroy(himl1); + pImageList_Destroy(himl2); + pImageList_Destroy(himl1); DestroyWindow(hwnd); } @@ -366,7 +390,7 @@ static void test_add_remove(void) HICON hicon3 ; /* create an imagelist to play with */ - himl = ImageList_Create(84, 84, ILC_COLOR16, 0, 3); + himl = pImageList_Create(84, 84, ILC_COLOR16, 0, 3); ok(himl!=0,"failed to create imagelist\n"); /* load the icons to add to the image list */ @@ -378,41 +402,41 @@ static void test_add_remove(void) ok(hicon3 != 0, "no hicon3\n"); /* remove when nothing exists */ - ok(!ImageList_Remove(himl,0),"removed nonexistent icon\n"); + ok(!pImageList_Remove(himl, 0), "Removed nonexistent icon.\n"); /* removing everything from an empty imagelist should succeed */ - ok(ImageList_RemoveAll(himl),"removed nonexistent icon\n"); + ok(pImageList_Remove(himl, -1), "Removed nonexistent icon\n"); /* add three */ - ok(0==ImageList_AddIcon(himl, hicon1),"failed to add icon1\n"); - ok(1==ImageList_AddIcon(himl, hicon2),"failed to add icon2\n"); - ok(2==ImageList_AddIcon(himl, hicon3),"failed to add icon3\n"); + ok(0 == pImageList_ReplaceIcon(himl, -1, hicon1), "Failed to add icon1.\n"); + ok(1 == pImageList_ReplaceIcon(himl, -1, hicon2), "Failed to add icon2.\n"); + ok(2 == pImageList_ReplaceIcon(himl, -1, hicon3), "Failed to add icon3.\n"); /* remove an index out of range */ - ok(!ImageList_Remove(himl,4711),"removed nonexistent icon\n"); + ok(!pImageList_Remove(himl, 4711), "removed nonexistent icon\n"); /* remove three */ - ok(ImageList_Remove(himl,0),"can't remove 0\n"); - ok(ImageList_Remove(himl,0),"can't remove 0\n"); - ok(ImageList_Remove(himl,0),"can't remove 0\n"); + ok(pImageList_Remove(himl, 0), "Can't remove 0\n"); + ok(pImageList_Remove(himl, 0), "Can't remove 0\n"); + ok(pImageList_Remove(himl, 0), "Can't remove 0\n"); /* remove one extra */ - ok(!ImageList_Remove(himl,0),"removed nonexistent icon\n"); + ok(!pImageList_Remove(himl, 0), "Removed nonexistent icon.\n"); /* destroy it */ - ok(ImageList_Destroy(himl),"destroy imagelist failed\n"); + ok(pImageList_Destroy(himl), "Failed to destroy imagelist.\n"); - ok(-1==ImageList_AddIcon((HIMAGELIST)0xdeadbeef, hicon1),"don't crash on bad handle\n"); + ok(-1 == pImageList_ReplaceIcon((HIMAGELIST)0xdeadbeef, -1, hicon1), "Don't crash on bad handle\n"); - ok(DestroyIcon(hicon1),"icon 1 wasn't deleted\n"); - ok(DestroyIcon(hicon2),"icon 2 wasn't deleted\n"); - ok(DestroyIcon(hicon3),"icon 3 wasn't deleted\n"); + ok(DestroyIcon(hicon1), "Failed to destroy icon 1.\n"); + ok(DestroyIcon(hicon2), "Failed to destroy icon 2.\n"); + ok(DestroyIcon(hicon3), "Failed to destroy icon 3.\n"); } static void test_imagecount(void) { HIMAGELIST himl; - ok(0==ImageList_GetImageCount((HIMAGELIST)0xdeadbeef),"don't crash on bad handle\n"); + ok(0 == pImageList_GetImageCount((HIMAGELIST)0xdeadbeef), "don't crash on bad handle\n"); if (!pImageList_SetImageCount) { @@ -420,17 +444,17 @@ static void test_imagecount(void) return; } - himl = ImageList_Create(84, 84, ILC_COLOR16, 0, 3); - ok(himl!=0,"failed to create imagelist\n"); + himl = pImageList_Create(84, 84, ILC_COLOR16, 0, 3); + ok(himl != 0, "Failed to create imagelist.\n"); ok(pImageList_SetImageCount(himl, 3), "couldn't increase image count\n"); - ok(ImageList_GetImageCount(himl) == 3, "invalid image count after increase\n"); + ok(pImageList_GetImageCount(himl) == 3, "invalid image count after increase\n"); ok(pImageList_SetImageCount(himl, 1), "couldn't decrease image count\n"); - ok(ImageList_GetImageCount(himl) == 1, "invalid image count after decrease to 1\n"); + ok(pImageList_GetImageCount(himl) == 1, "invalid image count after decrease to 1\n"); ok(pImageList_SetImageCount(himl, 0), "couldn't decrease image count\n"); - ok(ImageList_GetImageCount(himl) == 0, "invalid image count after decrease to 0\n"); + ok(pImageList_GetImageCount(himl) == 0, "invalid image count after decrease to 0\n"); - ok(ImageList_Destroy(himl), "destroy imagelist failed\n"); + ok(pImageList_Destroy(himl), "Failed to destroy imagelist.\n"); } static void test_DrawIndirect(void) @@ -451,13 +475,13 @@ static void test_DrawIndirect(void) return; } - hwndfortest = create_a_window(); + hwndfortest = create_window(); hdc = GetDC(hwndfortest); ok(hdc!=NULL, "couldn't get DC\n"); /* create an imagelist to play with */ - himl = ImageList_Create(48, 48, ILC_COLOR16, 0, 3); - ok(himl!=0,"failed to create imagelist\n"); + himl = pImageList_Create(48, 48, ILC_COLOR16, 0, 3); + ok(himl != 0, "Failed to create imagelist.\n"); /* load the icons to add to the image list */ hbm1 = CreateBitmap(48, 48, 1, 1, bitmap_bits); @@ -468,14 +492,14 @@ static void test_DrawIndirect(void) ok(hbm3 != 0, "no bitmap 3\n"); /* add three */ - ok(0==ImageList_Add(himl, hbm1, 0),"failed to add bitmap 1\n"); - ok(1==ImageList_Add(himl, hbm2, 0),"failed to add bitmap 2\n"); + ok(0 == ImageList_Add(himl, hbm1, 0),"failed to add bitmap 1\n"); + ok(1 == ImageList_Add(himl, hbm2, 0),"failed to add bitmap 2\n"); if (pImageList_SetImageCount) { ok(pImageList_SetImageCount(himl,3),"Setimage count failed\n"); /*ok(2==ImageList_Add(himl, hbm3, NULL),"failed to add bitmap 3\n"); */ - ok(ImageList_Replace(himl, 2, hbm3, 0),"failed to replace bitmap 3\n"); + ok(pImageList_Replace(himl, 2, hbm3, 0),"failed to replace bitmap 3\n"); } memset(&imldp, 0, sizeof (imldp)); @@ -504,12 +528,12 @@ static void test_DrawIndirect(void) ok(!pImageList_DrawIndirect(&imldp),"should fail\n"); /* remove three */ - ok(ImageList_Remove(himl, 0), "removing 1st bitmap\n"); - ok(ImageList_Remove(himl, 0), "removing 2nd bitmap\n"); - ok(ImageList_Remove(himl, 0), "removing 3rd bitmap\n"); + ok(pImageList_Remove(himl, 0), "removing 1st bitmap\n"); + ok(pImageList_Remove(himl, 0), "removing 2nd bitmap\n"); + ok(pImageList_Remove(himl, 0), "removing 3rd bitmap\n"); /* destroy it */ - ok(ImageList_Destroy(himl),"destroy imagelist failed\n"); + ok(pImageList_Destroy(himl), "Failed to destroy imagelist.\n"); /* bitmaps should not be deleted by the imagelist */ ok(DeleteObject(hbm1),"bitmap 1 can't be deleted\n"); @@ -550,12 +574,12 @@ static void test_merge_colors(void) for (i = 0; i < 8; i++) { - himl[i] = ImageList_Create(32, 32, sizes[i], 0, 3); + himl[i] = pImageList_Create(32, 32, sizes[i], 0, 3); ok(himl[i] != NULL, "failed to create himl[%d]\n", i); - ok(0 == ImageList_AddIcon(himl[i], hicon1), "add icon1 to himl[%d] failed\n", i); + ok(0 == pImageList_ReplaceIcon(himl[i], -1, hicon1), "Failed to add icon1 to himl[%d].\n", i); if (i == 0 || i == 1 || i == 7) { - ImageList_GetImageInfo(himl[i], 0, &info); + pImageList_GetImageInfo(himl[i], 0, &info); sizes[i] = get_color_format(info.hbmImage); } } @@ -563,10 +587,10 @@ static void test_merge_colors(void) for (i = 0; i < 8; i++) for (j = 0; j < 8; j++) { - hmerge = ImageList_Merge(himl[i], 0, himl[j], 0, 0, 0); + hmerge = pImageList_Merge(himl[i], 0, himl[j], 0, 0, 0); ok(hmerge != NULL, "merge himl[%d], himl[%d] failed\n", i, j); - ImageList_GetImageInfo(hmerge, 0, &info); + pImageList_GetImageInfo(hmerge, 0, &info); bpp = get_color_format(info.hbmImage); /* ILC_COLOR[X] is defined as [X] */ if (i == 4 && j == 7) @@ -577,23 +601,23 @@ static void test_merge_colors(void) "wrong biBitCount %d when merging lists %d (%d) and %d (%d)\n", bpp, i, sizes[i], j, sizes[j]); ok(info.hbmMask != 0, "Imagelist merged from %d and %d had no mask\n", i, j); - if (hmerge) ImageList_Destroy(hmerge); + pImageList_Destroy(hmerge); } for (i = 0; i < 8; i++) - ImageList_Destroy(himl[i]); + pImageList_Destroy(himl[i]); } static void test_merge(void) { HIMAGELIST himl1, himl2, hmerge; HICON hicon1; - HWND hwnd = create_a_window(); + HWND hwnd = create_window(); - himl1 = ImageList_Create(32,32,0,0,3); + himl1 = pImageList_Create(32, 32, 0, 0, 3); ok(himl1 != NULL,"failed to create himl1\n"); - himl2 = ImageList_Create(32,32,0,0,3); + himl2 = pImageList_Create(32, 32, 0, 0, 3); ok(himl2 != NULL,"failed to create himl2\n"); hicon1 = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits); @@ -602,60 +626,60 @@ static void test_merge(void) if (!himl1 || !himl2 || !hicon1) return; - ok(0==ImageList_AddIcon(himl2, hicon1),"add icon1 to himl2 failed\n"); + ok(0 == pImageList_ReplaceIcon(himl2, -1, hicon1), "Failed to add icon1 to himl2.\n"); check_bits(hwnd, himl2, 0, 32, icon_bits, "add icon1 to himl2"); /* If himl1 has no images, merge still succeeds */ - hmerge = ImageList_Merge(himl1, -1, himl2, 0, 0, 0); + hmerge = pImageList_Merge(himl1, -1, himl2, 0, 0, 0); ok(hmerge != NULL, "merge himl1,-1 failed\n"); check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl1,-1"); - if (hmerge) ImageList_Destroy(hmerge); + pImageList_Destroy(hmerge); - hmerge = ImageList_Merge(himl1, 0, himl2, 0, 0, 0); + hmerge = pImageList_Merge(himl1, 0, himl2, 0, 0, 0); ok(hmerge != NULL,"merge himl1,0 failed\n"); check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl1,0"); - if (hmerge) ImageList_Destroy(hmerge); + pImageList_Destroy(hmerge); /* Same happens if himl2 is empty */ - ImageList_Destroy(himl2); - himl2 = ImageList_Create(32,32,0,0,3); + pImageList_Destroy(himl2); + himl2 = pImageList_Create(32, 32, 0, 0, 3); ok(himl2 != NULL,"failed to recreate himl2\n"); if (!himl2) return; - hmerge = ImageList_Merge(himl1, -1, himl2, -1, 0, 0); + hmerge = pImageList_Merge(himl1, -1, himl2, -1, 0, 0); ok(hmerge != NULL, "merge himl2,-1 failed\n"); check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl2,-1"); - if (hmerge) ImageList_Destroy(hmerge); + pImageList_Destroy(hmerge); - hmerge = ImageList_Merge(himl1, -1, himl2, 0, 0, 0); + hmerge = pImageList_Merge(himl1, -1, himl2, 0, 0, 0); ok(hmerge != NULL, "merge himl2,0 failed\n"); check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl2,0"); - if (hmerge) ImageList_Destroy(hmerge); + pImageList_Destroy(hmerge); /* Now try merging an image with itself */ - ok(0==ImageList_AddIcon(himl2, hicon1),"re-add icon1 to himl2 failed\n"); + ok(0 == pImageList_ReplaceIcon(himl2, -1, hicon1), "Failed to re-add icon1 to himl2.\n"); - hmerge = ImageList_Merge(himl2, 0, himl2, 0, 0, 0); + hmerge = pImageList_Merge(himl2, 0, himl2, 0, 0, 0); ok(hmerge != NULL, "merge himl2 with itself failed\n"); check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl2 with itself"); - if (hmerge) ImageList_Destroy(hmerge); + pImageList_Destroy(hmerge); /* Try merging 2 different image lists */ - ok(0==ImageList_AddIcon(himl1, hicon1),"add icon1 to himl1 failed\n"); + ok(0 == pImageList_ReplaceIcon(himl1, -1, hicon1), "Failed to add icon1 to himl1.\n"); - hmerge = ImageList_Merge(himl1, 0, himl2, 0, 0, 0); + hmerge = pImageList_Merge(himl1, 0, himl2, 0, 0, 0); ok(hmerge != NULL, "merge himl1 with himl2 failed\n"); check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl1 with himl2"); - if (hmerge) ImageList_Destroy(hmerge); + pImageList_Destroy(hmerge); - hmerge = ImageList_Merge(himl1, 0, himl2, 0, 8, 16); + hmerge = pImageList_Merge(himl1, 0, himl2, 0, 8, 16); ok(hmerge != NULL, "merge himl1 with himl2 8,16 failed\n"); check_bits(hwnd, hmerge, 0, 32, empty_bits, "merge himl1 with himl2, 8,16"); - if (hmerge) ImageList_Destroy(hmerge); + pImageList_Destroy(hmerge); - ImageList_Destroy(himl1); - ImageList_Destroy(himl2); + pImageList_Destroy(himl1); + pImageList_Destroy(himl2); DestroyIcon(hicon1); DestroyWindow(hwnd); } @@ -708,11 +732,23 @@ static HRESULT STDMETHODCALLTYPE Test_Stream_Write(IStream *iface, const void *p return IStream_Write(stream->stream, pv, cb, pcbWritten); } -static HRESULT STDMETHODCALLTYPE Test_Stream_Seek(IStream *iface, LARGE_INTEGER dlibMove, - DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) +static HRESULT STDMETHODCALLTYPE Test_Stream_Seek(IStream *iface, LARGE_INTEGER offset, DWORD origin, + ULARGE_INTEGER *new_pos) { - ok(0, "unexpected call\n"); - return E_NOTIMPL; + struct memstream *stream = impl_from_IStream(iface); + + if (is_v6_test()) + { + ok(origin == STREAM_SEEK_CUR, "Unexpected origin %d.\n", origin); + ok(offset.QuadPart == 0, "Unexpected offset %s.\n", wine_dbgstr_longlong(offset.QuadPart)); + ok(new_pos != NULL, "Unexpected out position pointer.\n"); + return IStream_Seek(stream->stream, offset, origin, new_pos); + } + else + { + ok(0, "unexpected call\n"); + return E_NOTIMPL; + } } static HRESULT STDMETHODCALLTYPE Test_Stream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize) @@ -788,8 +824,11 @@ static const IStreamVtbl Test_Stream_Vtbl = static void init_memstream(struct memstream *stream) { + HRESULT hr; + stream->IStream_iface.lpVtbl = &Test_Stream_Vtbl; - CreateStreamOnHGlobal(NULL, TRUE, &stream->stream); + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream->stream); + ok(hr == S_OK, "Failed to create a stream, hr %#x.\n", hr); } static void cleanup_memstream(struct memstream *stream) @@ -797,15 +836,13 @@ static void cleanup_memstream(struct memstream *stream) IStream_Release(stream->stream); } - static INT DIB_GetWidthBytes( int width, int bpp ) { return ((width * bpp + 31) / 8) & ~3; } -static ULONG check_bitmap_data(const char *bm_data, ULONG bm_data_size, - INT width, INT height, INT bpp, - const char *comment) +static ULONG check_bitmap_data(const ILHEAD *header, const char *bm_data, + ULONG bm_data_size, const SIZE *bmpsize, INT bpp, const char *comment) { const BITMAPFILEHEADER *bmfh = (const BITMAPFILEHEADER *)bm_data; const BITMAPINFOHEADER *bmih = (const BITMAPINFOHEADER *)(bm_data + sizeof(*bmfh)); @@ -821,13 +858,13 @@ static ULONG check_bitmap_data(const char *bm_data, ULONG bm_data_size, ok(bmfh->bfOffBits == hdr_size, "wrong bfOffBits 0x%02x\n", bmfh->bfOffBits); ok(bmih->biSize == sizeof(*bmih), "wrong biSize %d\n", bmih->biSize); - ok(bmih->biWidth == width, "wrong biWidth %d (expected %d)\n", bmih->biWidth, width); - ok(bmih->biHeight == height, "wrong biHeight %d (expected %d)\n", bmih->biHeight, height); ok(bmih->biPlanes == 1, "wrong biPlanes %d\n", bmih->biPlanes); ok(bmih->biBitCount == bpp, "wrong biBitCount %d\n", bmih->biBitCount); image_size = DIB_GetWidthBytes(bmih->biWidth, bmih->biBitCount) * bmih->biHeight; ok(bmih->biSizeImage == image_size, "wrong biSizeImage %u\n", bmih->biSizeImage); + ok(bmih->biWidth == bmpsize->cx && bmih->biHeight == bmpsize->cy, "Unexpected bitmap size %d x %d, " + "expected %d x %d\n", bmih->biWidth, bmih->biHeight, bmpsize->cx, bmpsize->cy); if (0) { @@ -842,15 +879,23 @@ if (0) return hdr_size + image_size; } -static void check_ilhead_data(const char *ilh_data, INT cx, INT cy, INT cur, INT max, INT grow, INT flags) +static BOOL is_v6_header(const ILHEAD *header) { - const ILHEAD *ilh = (const ILHEAD *)ilh_data; + return (header->usVersion & 0xff00) == 0x600; +} +static void check_ilhead_data(const ILHEAD *ilh, INT cx, INT cy, INT cur, INT max, INT grow, INT flags) +{ ok(ilh->usMagic == IMAGELIST_MAGIC, "wrong usMagic %4x (expected %02x)\n", ilh->usMagic, IMAGELIST_MAGIC); - ok(ilh->usVersion == 0x101, "wrong usVersion %x (expected 0x101)\n", ilh->usVersion); + ok(ilh->usVersion == 0x101 || + ilh->usVersion == 0x600 || /* WinXP/W2k3 */ + ilh->usVersion == 0x620, "Unknown usVersion %#x.\n", ilh->usVersion); ok(ilh->cCurImage == cur, "wrong cCurImage %d (expected %d)\n", ilh->cCurImage, cur); - ok(ilh->cMaxImage == max, "wrong cMaxImage %d (expected %d)\n", ilh->cMaxImage, max); - ok(ilh->cGrow == grow, "wrong cGrow %d (expected %d)\n", ilh->cGrow, grow); + if (!is_v6_header(ilh)) + { + ok(ilh->cMaxImage == max, "wrong cMaxImage %d (expected %d)\n", ilh->cMaxImage, max); + ok(ilh->cGrow == grow, "Unexpected cGrow %d (expected %d)\n", ilh->cGrow, grow); + } ok(ilh->cx == cx, "wrong cx %d (expected %d)\n", ilh->cx, cx); ok(ilh->cy == cy, "wrong cy %d (expected %d)\n", ilh->cy, cy); ok(ilh->bkcolor == CLR_NONE, "wrong bkcolor %x\n", ilh->bkcolor); @@ -895,56 +940,78 @@ static HBITMAP create_bitmap(INT cx, INT cy, COLORREF color, const char *comment return hbmp; } +static inline void imagelist_get_bitmap_size(const ILHEAD *header, SIZE *sz) +{ + const int tile_count = 4; + + if (is_v6_header(header)) + { + sz->cx = header->cx; + sz->cy = header->cMaxImage * header->cy; + } + else + { + sz->cx = header->cx * tile_count; + sz->cy = ((header->cMaxImage + tile_count - 1) / tile_count) * header->cy; + } +} + static void check_iml_data(HIMAGELIST himl, INT cx, INT cy, INT cur, INT max, INT grow, - INT width, INT height, INT flags, const char *comment) + INT flags, const char *comment) { INT ret, cxx, cyy, size; struct memstream stream; + const ILHEAD *header; LARGE_INTEGER mv; HIMAGELIST himl2; HGLOBAL hglobal; STATSTG stat; char *data; HRESULT hr; + SIZE bmpsize; + BOOL b; - ret = ImageList_GetImageCount(himl); + ret = pImageList_GetImageCount(himl); ok(ret == cur, "%s: expected image count %d got %d\n", comment, cur, ret); - ret = ImageList_GetIconSize(himl, &cxx, &cyy); + ret = pImageList_GetIconSize(himl, &cxx, &cyy); ok(ret, "ImageList_GetIconSize failed\n"); ok(cxx == cx, "%s: wrong cx %d (expected %d)\n", comment, cxx, cx); ok(cyy == cy, "%s: wrong cy %d (expected %d)\n", comment, cyy, cy); init_memstream(&stream); - ret = ImageList_Write(himl, &stream.IStream_iface); - ok(ret, "%s: ImageList_Write failed\n", comment); + b = pImageList_Write(himl, &stream.IStream_iface); + ok(b, "%s: ImageList_Write failed\n", comment); hr = GetHGlobalFromStream(stream.stream, &hglobal); ok(hr == S_OK, "%s: Failed to get hglobal, %#x\n", comment, hr); - IStream_Stat(stream.stream, &stat, STATFLAG_NONAME); + hr = IStream_Stat(stream.stream, &stat, STATFLAG_NONAME); + ok(hr == S_OK, "Stat() failed, hr %#x.\n", hr); data = GlobalLock(hglobal); ok(data != 0, "%s: ImageList_Write didn't write any data\n", comment); ok(stat.cbSize.LowPart > sizeof(ILHEAD), "%s: ImageList_Write wrote not enough data\n", comment); - check_ilhead_data(data, cx, cy, cur, max, grow, flags); - size = check_bitmap_data(data + sizeof(ILHEAD), stat.cbSize.LowPart - sizeof(ILHEAD), - width, height, flags & 0xfe, comment); - if (size < stat.cbSize.LowPart - sizeof(ILHEAD)) /* mask is present */ + header = (const ILHEAD *)data; + check_ilhead_data(header, cx, cy, cur, max, grow, flags); + imagelist_get_bitmap_size(header, &bmpsize); + size = check_bitmap_data(header, data + sizeof(ILHEAD), stat.cbSize.LowPart - sizeof(ILHEAD), + &bmpsize, flags & 0xfe, comment); + if (!is_v6_header(header) && size < stat.cbSize.LowPart - sizeof(ILHEAD)) /* mask is present */ { ok( flags & ILC_MASK, "%s: extra data %u/%u but mask not expected\n", comment, stat.cbSize.LowPart, size ); - check_bitmap_data(data + sizeof(ILHEAD) + size, stat.cbSize.LowPart - sizeof(ILHEAD) - size, - width, height, 1, comment); + check_bitmap_data(header, data + sizeof(ILHEAD) + size, stat.cbSize.LowPart - sizeof(ILHEAD) - size, + &bmpsize, 1, comment); } /* rewind and reconstruct from stream */ mv.QuadPart = 0; IStream_Seek(stream.stream, mv, STREAM_SEEK_SET, NULL); - himl2 = ImageList_Read(&stream.IStream_iface); + himl2 = pImageList_Read(&stream.IStream_iface); ok(himl2 != NULL, "%s: Failed to deserialize imagelist\n", comment); - ImageList_Destroy(himl2); + pImageList_Destroy(himl2); GlobalUnlock(hglobal); cleanup_memstream(&stream); @@ -959,37 +1026,37 @@ static void image_list_init(HIMAGELIST himl) static const struct test_data { BYTE grey; - INT cx, cy, cur, max, grow, width, height, bpp; + INT cx, cy, cur, max, grow, bpp; const char *comment; } td[] = { - { 255, BMP_CX, BMP_CX, 1, 2, 4, BMP_CX * 4, BMP_CX * 1, 24, "total 1" }, - { 170, BMP_CX, BMP_CX, 2, 7, 4, BMP_CX * 4, BMP_CX * 2, 24, "total 2" }, - { 85, BMP_CX, BMP_CX, 3, 7, 4, BMP_CX * 4, BMP_CX * 2, 24, "total 3" }, - { 0, BMP_CX, BMP_CX, 4, 7, 4, BMP_CX * 4, BMP_CX * 2, 24, "total 4" }, - { 0, BMP_CX, BMP_CX, 5, 7, 4, BMP_CX * 4, BMP_CX * 2, 24, "total 5" }, - { 85, BMP_CX, BMP_CX, 6, 7, 4, BMP_CX * 4, BMP_CX * 2, 24, "total 6" }, - { 170, BMP_CX, BMP_CX, 7, 12, 4, BMP_CX * 4, BMP_CX * 3, 24, "total 7" }, - { 255, BMP_CX, BMP_CX, 8, 12, 4, BMP_CX * 4, BMP_CX * 3, 24, "total 8" }, - { 255, BMP_CX, BMP_CX, 9, 12, 4, BMP_CX * 4, BMP_CX * 3, 24, "total 9" }, - { 170, BMP_CX, BMP_CX, 10, 12, 4, BMP_CX * 4, BMP_CX * 3, 24, "total 10" }, - { 85, BMP_CX, BMP_CX, 11, 12, 4, BMP_CX * 4, BMP_CX * 3, 24, "total 11" }, - { 0, BMP_CX, BMP_CX, 12, 17, 4, BMP_CX * 4, BMP_CX * 5, 24, "total 12" }, - { 0, BMP_CX, BMP_CX, 13, 17, 4, BMP_CX * 4, BMP_CX * 5, 24, "total 13" }, - { 85, BMP_CX, BMP_CX, 14, 17, 4, BMP_CX * 4, BMP_CX * 5, 24, "total 14" }, - { 170, BMP_CX, BMP_CX, 15, 17, 4, BMP_CX * 4, BMP_CX * 5, 24, "total 15" }, - { 255, BMP_CX, BMP_CX, 16, 17, 4, BMP_CX * 4, BMP_CX * 5, 24, "total 16" }, - { 255, BMP_CX, BMP_CX, 17, 22, 4, BMP_CX * 4, BMP_CX * 6, 24, "total 17" }, - { 170, BMP_CX, BMP_CX, 18, 22, 4, BMP_CX * 4, BMP_CX * 6, 24, "total 18" }, - { 85, BMP_CX, BMP_CX, 19, 22, 4, BMP_CX * 4, BMP_CX * 6, 24, "total 19" }, - { 0, BMP_CX, BMP_CX, 20, 22, 4, BMP_CX * 4, BMP_CX * 6, 24, "total 20" }, - { 0, BMP_CX, BMP_CX, 21, 22, 4, BMP_CX * 4, BMP_CX * 6, 24, "total 21" }, - { 85, BMP_CX, BMP_CX, 22, 27, 4, BMP_CX * 4, BMP_CX * 7, 24, "total 22" }, - { 170, BMP_CX, BMP_CX, 23, 27, 4, BMP_CX * 4, BMP_CX * 7, 24, "total 23" }, - { 255, BMP_CX, BMP_CX, 24, 27, 4, BMP_CX * 4, BMP_CX * 7, 24, "total 24" } + { 255, BMP_CX, BMP_CX, 1, 2, 4, 24, "total 1" }, + { 170, BMP_CX, BMP_CX, 2, 7, 4, 24, "total 2" }, + { 85, BMP_CX, BMP_CX, 3, 7, 4, 24, "total 3" }, + { 0, BMP_CX, BMP_CX, 4, 7, 4, 24, "total 4" }, + { 0, BMP_CX, BMP_CX, 5, 7, 4, 24, "total 5" }, + { 85, BMP_CX, BMP_CX, 6, 7, 4, 24, "total 6" }, + { 170, BMP_CX, BMP_CX, 7, 12, 4, 24, "total 7" }, + { 255, BMP_CX, BMP_CX, 8, 12, 4, 24, "total 8" }, + { 255, BMP_CX, BMP_CX, 9, 12, 4, 24, "total 9" }, + { 170, BMP_CX, BMP_CX, 10, 12, 4, 24, "total 10" }, + { 85, BMP_CX, BMP_CX, 11, 12, 4, 24, "total 11" }, + { 0, BMP_CX, BMP_CX, 12, 17, 4, 24, "total 12" }, + { 0, BMP_CX, BMP_CX, 13, 17, 4, 24, "total 13" }, + { 85, BMP_CX, BMP_CX, 14, 17, 4, 24, "total 14" }, + { 170, BMP_CX, BMP_CX, 15, 17, 4, 24, "total 15" }, + { 255, BMP_CX, BMP_CX, 16, 17, 4, 24, "total 16" }, + { 255, BMP_CX, BMP_CX, 17, 22, 4, 24, "total 17" }, + { 170, BMP_CX, BMP_CX, 18, 22, 4, 24, "total 18" }, + { 85, BMP_CX, BMP_CX, 19, 22, 4, 24, "total 19" }, + { 0, BMP_CX, BMP_CX, 20, 22, 4, 24, "total 20" }, + { 0, BMP_CX, BMP_CX, 21, 22, 4, 24, "total 21" }, + { 85, BMP_CX, BMP_CX, 22, 27, 4, 24, "total 22" }, + { 170, BMP_CX, BMP_CX, 23, 27, 4, 24, "total 23" }, + { 255, BMP_CX, BMP_CX, 24, 27, 4, 24, "total 24" } }; - check_iml_data(himl, BMP_CX, BMP_CX, 0, 2, 4, BMP_CX * 4, BMP_CX * 1, ILC_COLOR24, "total 0"); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 2, 4, ILC_COLOR24, "total 0"); #define add_bitmap(grey) \ sprintf(comment, "%d", n++); \ @@ -1000,8 +1067,7 @@ static void image_list_init(HIMAGELIST himl) for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) { add_bitmap(td[i].grey); - check_iml_data(himl, td[i].cx, td[i].cy, td[i].cur, td[i].max, td[i].grow, - td[i].width, td[i].height, td[i].bpp, td[i].comment); + check_iml_data(himl, td[i].cx, td[i].cy, td[i].cur, td[i].max, td[i].grow, td[i].bpp, td[i].comment); } #undef add_bitmap } @@ -1013,211 +1079,201 @@ static void test_imagelist_storage(void) HICON icon; INT ret; - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 1, 1); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 1, 1); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 2, 4, BMP_CX * 4, BMP_CX * 1, ILC_COLOR24, "empty"); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 2, 4, ILC_COLOR24, "empty"); image_list_init(himl); - check_iml_data(himl, BMP_CX, BMP_CX, 24, 27, 4, BMP_CX * 4, BMP_CX * 7, ILC_COLOR24, "orig"); + check_iml_data(himl, BMP_CX, BMP_CX, 24, 27, 4, ILC_COLOR24, "orig"); - ret = ImageList_Remove(himl, 4); + ret = pImageList_Remove(himl, 4); ok(ret, "ImageList_Remove failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 23, 27, 4, BMP_CX * 4, BMP_CX * 7, ILC_COLOR24, "1"); + check_iml_data(himl, BMP_CX, BMP_CX, 23, 27, 4, ILC_COLOR24, "1"); - ret = ImageList_Remove(himl, 5); + ret = pImageList_Remove(himl, 5); ok(ret, "ImageList_Remove failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 22, 27, 4, BMP_CX * 4, BMP_CX * 7, ILC_COLOR24, "2"); + check_iml_data(himl, BMP_CX, BMP_CX, 22, 27, 4, ILC_COLOR24, "2"); - ret = ImageList_Remove(himl, 6); + ret = pImageList_Remove(himl, 6); ok(ret, "ImageList_Remove failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 21, 27, 4, BMP_CX * 4, BMP_CX * 7, ILC_COLOR24, "3"); + check_iml_data(himl, BMP_CX, BMP_CX, 21, 27, 4, ILC_COLOR24, "3"); - ret = ImageList_Remove(himl, 7); + ret = pImageList_Remove(himl, 7); ok(ret, "ImageList_Remove failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 20, 27, 4, BMP_CX * 4, BMP_CX * 7, ILC_COLOR24, "4"); + check_iml_data(himl, BMP_CX, BMP_CX, 20, 27, 4, ILC_COLOR24, "4"); - ret = ImageList_Remove(himl, -2); + ret = pImageList_Remove(himl, -2); ok(!ret, "ImageList_Remove(-2) should fail\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 20, 27, 4, BMP_CX * 4, BMP_CX * 7, ILC_COLOR24, "5"); + check_iml_data(himl, BMP_CX, BMP_CX, 20, 27, 4, ILC_COLOR24, "5"); - ret = ImageList_Remove(himl, 20); + ret = pImageList_Remove(himl, 20); ok(!ret, "ImageList_Remove(20) should fail\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 20, 27, 4, BMP_CX * 4, BMP_CX * 7, ILC_COLOR24, "6"); + check_iml_data(himl, BMP_CX, BMP_CX, 20, 27, 4, ILC_COLOR24, "6"); - ret = ImageList_Remove(himl, -1); + ret = pImageList_Remove(himl, -1); ok(ret, "ImageList_Remove(-1) failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 4, 4, BMP_CX * 4, BMP_CX * 1, ILC_COLOR24, "7"); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 4, 4, ILC_COLOR24, "7"); - ret = ImageList_Destroy(himl); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); /* test ImageList_Create storage allocation */ - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 0, 32); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 0, 32); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 1, 32, BMP_CX * 4, BMP_CX * 1, ILC_COLOR24, "init 0 grow 32"); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 1, 32, ILC_COLOR24, "init 0 grow 32"); hbm = create_bitmap(BMP_CX * 9, BMP_CX, 0, "9"); - ret = ImageList_Add(himl, hbm, NULL); + ret = pImageList_Add(himl, hbm, NULL); ok(ret == 0, "ImageList_Add returned %d, expected 0\n", ret); - check_iml_data(himl, BMP_CX, BMP_CX, 1, 34, 32, BMP_CX * 4, BMP_CX * 9, ILC_COLOR24, "add 1 x 9"); + check_iml_data(himl, BMP_CX, BMP_CX, 1, 34, 32, ILC_COLOR24, "add 1 x 9"); DeleteObject(hbm); - ret = ImageList_Destroy(himl); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 4, 4); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 4, 4); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR24, "init 4 grow 4"); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, ILC_COLOR24, "init 4 grow 4"); hbm = create_bitmap(BMP_CX, BMP_CX * 9, 0, "9"); - ret = ImageList_Add(himl, hbm, NULL); + ret = pImageList_Add(himl, hbm, NULL); ok(ret == 0, "ImageList_Add returned %d, expected 0\n", ret); - check_iml_data(himl, BMP_CX, BMP_CX, 9, 15, 4, BMP_CX * 4, BMP_CX * 4, ILC_COLOR24, "add 9 x 1"); - ret = ImageList_Add(himl, hbm, NULL); + check_iml_data(himl, BMP_CX, BMP_CX, 9, 15, 4, ILC_COLOR24, "add 9 x 1"); + ret = pImageList_Add(himl, hbm, NULL); ok(ret == 9, "ImageList_Add returned %d, expected 9\n", ret); - check_iml_data(himl, BMP_CX, BMP_CX, 18, 25, 4, BMP_CX * 4, BMP_CX * 7, ILC_COLOR24, "add 9 x 1"); + check_iml_data(himl, BMP_CX, BMP_CX, 18, 25, 4, ILC_COLOR24, "add 9 x 1"); DeleteObject(hbm); - ret = ImageList_Destroy(himl); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 207, 209); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 207, 209); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 208, 212, BMP_CX * 4, BMP_CX * 52, ILC_COLOR24, "init 207 grow 209"); - ret = ImageList_Destroy(himl); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 208, 212, ILC_COLOR24, "init 207 grow 209"); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 209, 207); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 209, 207); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 210, 208, BMP_CX * 4, BMP_CX * 53, ILC_COLOR24, "init 209 grow 207"); - ret = ImageList_Destroy(himl); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 210, 208, ILC_COLOR24, "init 209 grow 207"); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 14, 4); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 14, 4); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 15, 4, BMP_CX * 4, BMP_CX * 4, ILC_COLOR24, "init 14 grow 4"); - ret = ImageList_Destroy(himl); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 15, 4, ILC_COLOR24, "init 14 grow 4"); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 5, 9); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 5, 9); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 6, 12, BMP_CX * 4, BMP_CX * 2, ILC_COLOR24, "init 5 grow 9"); - ret = ImageList_Destroy(himl); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 6, 12, ILC_COLOR24, "init 5 grow 9"); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 9, 5); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 9, 5); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 10, 8, BMP_CX * 4, BMP_CX * 3, ILC_COLOR24, "init 9 grow 5"); - ret = ImageList_Destroy(himl); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 10, 8, ILC_COLOR24, "init 9 grow 5"); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 2, 4); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 2, 4); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 4, BMP_CX * 4, BMP_CX * 1, ILC_COLOR24, "init 2 grow 4"); - ret = ImageList_Destroy(himl); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 4, ILC_COLOR24, "init 2 grow 4"); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 4, 2); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24, 4, 2); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR24, "init 4 grow 2"); - ret = ImageList_Destroy(himl); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, ILC_COLOR24, "init 4 grow 2"); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR8, 4, 2); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR8, 4, 2); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR8, "bpp 8"); - ret = ImageList_Destroy(himl); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, ILC_COLOR8, "bpp 8"); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4, 4, 2); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4, 4, 2); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR4, "bpp 4"); - ret = ImageList_Destroy(himl); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, ILC_COLOR4, "bpp 4"); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, 0, 4, 2); + himl = pImageList_Create(BMP_CX, BMP_CX, 0, 4, 2); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR4, "bpp default"); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, ILC_COLOR4, "bpp default"); icon = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits); - ok( ImageList_AddIcon(himl, icon) == 0,"failed to add icon\n"); - ok( ImageList_AddIcon(himl, icon) == 1,"failed to add icon\n"); + ok( pImageList_ReplaceIcon(himl, -1, icon) == 0, "Failed to add icon.\n"); + ok( pImageList_ReplaceIcon(himl, -1, icon) == 1, "Failed to add icon.\n"); DestroyIcon( icon ); - check_iml_data(himl, BMP_CX, BMP_CX, 2, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR4, "bpp default"); - ret = ImageList_Destroy(himl); + check_iml_data(himl, BMP_CX, BMP_CX, 2, 5, 4, ILC_COLOR4, "bpp default"); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24|ILC_MASK, 4, 2); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR24|ILC_MASK, 4, 2); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR24|ILC_MASK, - "bpp 24 + mask"); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, ILC_COLOR24|ILC_MASK, "bpp 24 + mask"); icon = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits); - ok( ImageList_AddIcon(himl, icon) == 0,"failed to add icon\n"); - ok( ImageList_AddIcon(himl, icon) == 1,"failed to add icon\n"); + ok( pImageList_ReplaceIcon(himl, -1, icon) == 0, "Failed to add icon.\n"); + ok( pImageList_ReplaceIcon(himl, -1, icon) == 1, "Failed to add icon.\n"); DestroyIcon( icon ); - check_iml_data(himl, BMP_CX, BMP_CX, 2, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR24|ILC_MASK, - "bpp 24 + mask"); - ret = ImageList_Destroy(himl); + check_iml_data(himl, BMP_CX, BMP_CX, 2, 5, 4, ILC_COLOR24|ILC_MASK, "bpp 24 + mask"); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 4, 2); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 4, 2); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR4|ILC_MASK, - "bpp 4 + mask"); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 5, 4, ILC_COLOR4|ILC_MASK, "bpp 4 + mask"); icon = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits); - ok( ImageList_AddIcon(himl, icon) == 0,"failed to add icon\n"); - ok( ImageList_AddIcon(himl, icon) == 1,"failed to add icon\n"); + ok( pImageList_ReplaceIcon(himl, -1, icon) == 0, "Failed to add icon.\n"); + ok( pImageList_ReplaceIcon(himl, -1, icon) == 1, "Failed to add icon.\n"); DestroyIcon( icon ); - check_iml_data(himl, BMP_CX, BMP_CX, 2, 5, 4, BMP_CX * 4, BMP_CX * 2, ILC_COLOR4|ILC_MASK, - "bpp 4 + mask"); - ret = ImageList_Destroy(himl); + check_iml_data(himl, BMP_CX, BMP_CX, 2, 5, 4, ILC_COLOR4|ILC_MASK, "bpp 4 + mask"); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 2, 99); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 2, 99); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 100, BMP_CX * 4, BMP_CX, ILC_COLOR4|ILC_MASK, - "init 2 grow 99"); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 100, ILC_COLOR4|ILC_MASK, "init 2 grow 99"); icon = CreateIcon(hinst, 32, 32, 1, 1, icon_bits, icon_bits); - ok( ImageList_AddIcon(himl, icon) == 0,"failed to add icon\n"); - ok( ImageList_AddIcon(himl, icon) == 1,"failed to add icon\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 2, 3, 100, BMP_CX * 4, BMP_CX, ILC_COLOR4|ILC_MASK, - "init 2 grow 99 2 icons"); - ok( ImageList_AddIcon(himl, icon) == 2,"failed to add icon\n"); + ok( pImageList_ReplaceIcon(himl, -1, icon) == 0, "Failed to add icon.\n"); + ok( pImageList_ReplaceIcon(himl, -1, icon) == 1, "Failed to add icon.\n"); + check_iml_data(himl, BMP_CX, BMP_CX, 2, 3, 100, ILC_COLOR4|ILC_MASK, "init 2 grow 99 2 icons"); + ok( pImageList_ReplaceIcon(himl, -1, icon) == 2, "Failed to add icon\n"); DestroyIcon( icon ); - check_iml_data(himl, BMP_CX, BMP_CX, 3, 104, 100, BMP_CX * 4, BMP_CX * 104/4, ILC_COLOR4|ILC_MASK, - "init 2 grow 99 3 icons"); - ok( ImageList_Remove(himl, -1) == TRUE,"failed to remove icon\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 100, 100, BMP_CX * 4, BMP_CX * 100/4, ILC_COLOR4|ILC_MASK, - "init 2 grow 99 empty"); - ok( ImageList_SetImageCount(himl, 22) == TRUE,"failed to set image count\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 22, 23, 100, BMP_CX * 4, BMP_CX * 24/4, ILC_COLOR4|ILC_MASK, - "init 2 grow 99 set count 22"); - ok( ImageList_SetImageCount(himl, 0) == TRUE,"failed to set image count\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 1, 100, BMP_CX * 4, BMP_CX, ILC_COLOR4|ILC_MASK, - "init 2 grow 99 set count 0"); - ok( ImageList_SetImageCount(himl, 42) == TRUE,"failed to set image count\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 42, 43, 100, BMP_CX * 4, BMP_CX * 44/4, ILC_COLOR4|ILC_MASK, - "init 2 grow 99 set count 42"); - ret = ImageList_Destroy(himl); + check_iml_data(himl, BMP_CX, BMP_CX, 3, 104, 100, ILC_COLOR4|ILC_MASK, "init 2 grow 99 3 icons"); + ok( pImageList_Remove(himl, -1) == TRUE, "Failed to remove icon.\n"); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 100, 100, ILC_COLOR4|ILC_MASK, "init 2 grow 99 empty"); + ok( pImageList_SetImageCount(himl, 22) == TRUE, "Failed to set image count.\n"); + check_iml_data(himl, BMP_CX, BMP_CX, 22, 23, 100, ILC_COLOR4|ILC_MASK, "init 2 grow 99 set count 22"); + ok( pImageList_SetImageCount(himl, 0) == TRUE, "Failed to set image count.\n"); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 1, 100, ILC_COLOR4|ILC_MASK, "init 2 grow 99 set count 0"); + ok( pImageList_SetImageCount(himl, 42) == TRUE, "Failed to set image count.\n"); + check_iml_data(himl, BMP_CX, BMP_CX, 42, 43, 100, ILC_COLOR4|ILC_MASK, "init 2 grow 99 set count 42"); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 2, 65536+12); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 2, -20); ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 12, BMP_CX * 4, BMP_CX, ILC_COLOR4|ILC_MASK, - "init 2 grow 65536+12"); - ret = ImageList_Destroy(himl); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 4, ILC_COLOR4|ILC_MASK, "init 2 grow -20"); + ret = pImageList_Destroy(himl); ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 2, 65535); - ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 0, BMP_CX * 4, BMP_CX, ILC_COLOR4|ILC_MASK, - "init 2 grow 65535"); - ret = ImageList_Destroy(himl); - ok(ret, "ImageList_Destroy failed\n"); + /* Version 6 implementation hangs on large grow values. */ + if (!is_v6_test()) + { + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 2, 65536+12); + ok(himl != 0, "ImageList_Create failed\n"); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 12, ILC_COLOR4|ILC_MASK, "init 2 grow 65536+12"); + ret = pImageList_Destroy(himl); + ok(ret, "ImageList_Destroy failed\n"); - himl = ImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 2, -20); - ok(himl != 0, "ImageList_Create failed\n"); - check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 4, BMP_CX * 4, BMP_CX, ILC_COLOR4|ILC_MASK, - "init 2 grow -20"); - ret = ImageList_Destroy(himl); - ok(ret, "ImageList_Destroy failed\n"); + himl = pImageList_Create(BMP_CX, BMP_CX, ILC_COLOR4|ILC_MASK, 2, 65535); + ok(himl != 0, "ImageList_Create failed\n"); + check_iml_data(himl, BMP_CX, BMP_CX, 0, 3, 0, ILC_COLOR4|ILC_MASK, "init 2 grow 65535"); + ret = pImageList_Destroy(himl); + ok(ret, "ImageList_Destroy failed\n"); + } } static void test_shell_imagelist(void) @@ -1553,7 +1609,7 @@ static void test_iimagelist(void) if (hr == S_OK) IImageList_Release(imgl); - ImageList_Destroy(himl); + pImageList_Destroy(himl); /* IImageList2 */ hr = pImageList_CoCreateInstance(&CLSID_ImageList, NULL, &IID_IImageList2, (void**)&imagelist); @@ -1566,81 +1622,6 @@ static void test_iimagelist(void) IImageList2_Release(imagelist); } -static void test_hotspot_v6(void) -{ - struct hotspot { - int dx; - int dy; - }; - -#define SIZEX1 47 -#define SIZEY1 31 -#define SIZEX2 11 -#define SIZEY2 17 -#define HOTSPOTS_MAX 4 /* Number of entries in hotspots */ - static const struct hotspot hotspots[HOTSPOTS_MAX] = { - { 10, 7 }, - { SIZEX1, SIZEY1 }, - { -9, -8 }, - { -7, 35 } - }; - int i, j; - HIMAGELIST himl1 = createImageList(SIZEX1, SIZEY1); - HIMAGELIST himl2 = createImageList(SIZEX2, SIZEY2); - IImageList *imgl1, *imgl2; - HRESULT hr; - - /* cast to IImageList */ - imgl1 = (IImageList *) himl1; - imgl2 = (IImageList *) himl2; - - for (i = 0; i < HOTSPOTS_MAX; i++) { - for (j = 0; j < HOTSPOTS_MAX; j++) { - int dx1 = hotspots[i].dx; - int dy1 = hotspots[i].dy; - int dx2 = hotspots[j].dx; - int dy2 = hotspots[j].dy; - int correctx, correcty, newx, newy; - char loc[256]; - IImageList *imglNew; - POINT ppt; - - hr = IImageList_BeginDrag(imgl1, 0, dx1, dy1); - ok(SUCCEEDED(hr), "BeginDrag failed for { %d, %d }\n", dx1, dy1); - sprintf(loc, "BeginDrag (%d,%d)\n", i, j); - - /* check merging the dragged image with a second image */ - hr = IImageList_SetDragCursorImage(imgl2, (IUnknown *) imgl2, 0, dx2, dy2); - ok(SUCCEEDED(hr), "SetDragCursorImage failed for {%d, %d}{%d, %d}\n", - dx1, dy1, dx2, dy2); - sprintf(loc, "SetDragCursorImage (%d,%d)\n", i, j); - - /* check new hotspot, it should be the same like the old one */ - hr = IImageList_GetDragImage(imgl2, NULL, &ppt, &IID_IImageList, (PVOID *) &imglNew); - ok(SUCCEEDED(hr), "GetDragImage failed\n"); - ok(ppt.x == dx1 && ppt.y == dy1, - "Expected drag hotspot [%d,%d] got [%d,%d]\n", - dx1, dy1, ppt.x, ppt.y); - /* check size of new dragged image */ - IImageList_GetIconSize(imglNew, &newx, &newy); - correctx = max(SIZEX1, max(SIZEX2 + dx2, SIZEX1 - dx2)); - correcty = max(SIZEY1, max(SIZEY2 + dy2, SIZEY1 - dy2)); - ok(newx == correctx && newy == correcty, - "Expected drag image size [%d,%d] got [%d,%d]\n", - correctx, correcty, newx, newy); - sprintf(loc, "GetDragImage (%d,%d)\n", i, j); - IImageList_EndDrag(imgl2); - } - } -#undef SIZEX1 -#undef SIZEY1 -#undef SIZEX2 -#undef SIZEY2 -#undef HOTSPOTS_MAX - IImageList_Release(imgl2); - IImageList_Release(imgl1); -} - static void test_IImageList_Add_Remove(void) { IImageList *imgl; @@ -1748,7 +1729,7 @@ static void test_IImageList_Draw(void) HDC hdc; int ret; - hwndfortest = create_a_window(); + hwndfortest = create_window(); hdc = GetDC(hwndfortest); ok(hdc!=NULL, "couldn't get DC\n"); @@ -1826,7 +1807,7 @@ static void test_IImageList_Merge(void) HIMAGELIST himl1, himl2; IImageList *imgl1, *imgl2, *merge; HICON hicon1; - HWND hwnd = create_a_window(); + HWND hwnd = create_window(); HRESULT hr; int ret; @@ -1913,94 +1894,89 @@ static void test_iconsize(void) INT cx, cy; BOOL ret; - himl = ImageList_Create(16, 16, ILC_COLOR16, 0, 3); + himl = pImageList_Create(16, 16, ILC_COLOR16, 0, 3); /* null pointers, not zero imagelist dimensions */ - ret = ImageList_GetIconSize(himl, NULL, NULL); + ret = pImageList_GetIconSize(himl, NULL, NULL); ok(!ret, "got %d\n", ret); /* doesn't touch return pointers */ cx = 0x1abe11ed; - ret = ImageList_GetIconSize(himl, &cx, NULL); + ret = pImageList_GetIconSize(himl, &cx, NULL); ok(!ret, "got %d\n", ret); ok(cx == 0x1abe11ed, "got %d\n", cx); cy = 0x1abe11ed; - ret = ImageList_GetIconSize(himl, NULL, &cy); + ret = pImageList_GetIconSize(himl, NULL, &cy); ok(!ret, "got %d\n", ret); ok(cy == 0x1abe11ed, "got %d\n", cy); - ImageList_Destroy(himl); + pImageList_Destroy(himl); - ret = ImageList_GetIconSize((HIMAGELIST)0xdeadbeef, &cx, &cy); + ret = pImageList_GetIconSize((HIMAGELIST)0xdeadbeef, &cx, &cy); ok(!ret, "got %d\n", ret); } static void test_create_destroy(void) { HIMAGELIST himl; - IImageList *imgl; INT cx, cy; BOOL rc; - HRESULT hr; INT ret; /* list with zero or negative image dimensions */ - himl = ImageList_Create(0, 0, ILC_COLOR16, 0, 3); + himl = pImageList_Create(0, 0, ILC_COLOR16, 0, 3); ok(himl == NULL, "got %p\n", himl); - himl = ImageList_Create(0, 16, ILC_COLOR16, 0, 3); + himl = pImageList_Create(0, 16, ILC_COLOR16, 0, 3); ok(himl == NULL, "got %p\n", himl); - himl = ImageList_Create(16, 0, ILC_COLOR16, 0, 3); + himl = pImageList_Create(16, 0, ILC_COLOR16, 0, 3); ok(himl == NULL, "got %p\n", himl); - himl = ImageList_Create(16, -1, ILC_COLOR16, 0, 3); + himl = pImageList_Create(16, -1, ILC_COLOR16, 0, 3); ok(himl == NULL, "got %p\n", himl); - himl = ImageList_Create(-1, 16, ILC_COLOR16, 0, 3); + himl = pImageList_Create(-1, 16, ILC_COLOR16, 0, 3); ok(himl == NULL, "got %p\n", himl); - rc = ImageList_Destroy((HIMAGELIST)0xdeadbeef); + rc = pImageList_Destroy((HIMAGELIST)0xdeadbeef); ok(rc == FALSE, "ImageList_Destroy(0xdeadbeef) should fail and not crash\n"); /* DDB image lists */ - himl = ImageList_Create(0, 14, ILC_COLORDDB, 4, 4); + himl = pImageList_Create(0, 14, ILC_COLORDDB, 4, 4); ok(himl != NULL, "got %p\n", himl); - imgl = (IImageList*)himl; - IImageList_GetIconSize(imgl, &cx, &cy); + + pImageList_GetIconSize(himl, &cx, &cy); ok (cx == 0, "Wrong cx (%i)\n", cx); ok (cy == 14, "Wrong cy (%i)\n", cy); - ImageList_Destroy(himl); + pImageList_Destroy(himl); - himl = ImageList_Create(0, 0, ILC_COLORDDB, 4, 4); + himl = pImageList_Create(0, 0, ILC_COLORDDB, 4, 4); ok(himl != NULL, "got %p\n", himl); - imgl = (IImageList*)himl; - IImageList_GetIconSize(imgl, &cx, &cy); + pImageList_GetIconSize(himl, &cx, &cy); ok (cx == 0, "Wrong cx (%i)\n", cx); ok (cy == 0, "Wrong cy (%i)\n", cy); - ImageList_Destroy(himl); + pImageList_Destroy(himl); - himl = ImageList_Create(0, 0, ILC_COLORDDB, 0, 4); + himl = pImageList_Create(0, 0, ILC_COLORDDB, 0, 4); ok(himl != NULL, "got %p\n", himl); - imgl = (IImageList*)himl; - IImageList_GetIconSize(imgl, &cx, &cy); + pImageList_GetIconSize(himl, &cx, &cy); ok (cx == 0, "Wrong cx (%i)\n", cx); ok (cy == 0, "Wrong cy (%i)\n", cy); - hr = IImageList_SetImageCount(imgl, 3); - ok(hr == S_OK, "got 0x%08x\n", hr); - hr = IImageList_GetImageCount(imgl, &ret); - ok(hr == S_OK && ret == 3, "invalid image count after increase\n"); + pImageList_SetImageCount(himl, 3); + ret = pImageList_GetImageCount(himl); + ok(ret == 3, "Unexpected image count after increase\n"); /* Trying to actually add an image causes a crash on Windows */ - ImageList_Destroy(himl); + pImageList_Destroy(himl); /* Negative values fail */ - himl = ImageList_Create(-1, -1, ILC_COLORDDB, 4, 4); + himl = pImageList_Create(-1, -1, ILC_COLORDDB, 4, 4); ok(himl == NULL, "got %p\n", himl); - himl = ImageList_Create(-1, 1, ILC_COLORDDB, 4, 4); + himl = pImageList_Create(-1, 1, ILC_COLORDDB, 4, 4); ok(himl == NULL, "got %p\n", himl); - himl = ImageList_Create(1, -1, ILC_COLORDDB, 4, 4); + himl = pImageList_Create(1, -1, ILC_COLORDDB, 4, 4); ok(himl == NULL, "got %p\n", himl); } @@ -2013,7 +1989,7 @@ static void check_color_table(const char *name, HDC hdc, HIMAGELIST himl, UINT i BITMAPINFO *bmi = (BITMAPINFO *)bmi_buffer; int i, depth = ilc & 0xfe; - ret = ImageList_GetImageInfo(himl, 0, &info); + ret = pImageList_GetImageInfo(himl, 0, &info); ok(ret, "got %d\n", ret); ok(info.hbmImage != NULL, "got %p\n", info.hbmImage); @@ -2031,9 +2007,9 @@ static void check_color_table(const char *name, HDC hdc, HIMAGELIST himl, UINT i ok((bmi->bmiColors[i].rgbRed == expect[i].rgbRed && bmi->bmiColors[i].rgbGreen == expect[i].rgbGreen && bmi->bmiColors[i].rgbBlue == expect[i].rgbBlue) || - (broken_expect && broken(bmi->bmiColors[i].rgbRed == broken_expect[i].rgbRed && + broken(bmi->bmiColors[i].rgbRed == broken_expect[i].rgbRed && bmi->bmiColors[i].rgbGreen == broken_expect[i].rgbGreen && - bmi->bmiColors[i].rgbBlue == broken_expect[i].rgbBlue)), + bmi->bmiColors[i].rgbBlue == broken_expect[i].rgbBlue), "%d: %s: got color[%d] %02x %02x %02x expect %02x %02x %02x\n", depth, name, i, bmi->bmiColors[i].rgbRed, bmi->bmiColors[i].rgbGreen, bmi->bmiColors[i].rgbBlue, expect[i].rgbRed, expect[i].rgbGreen, expect[i].rgbBlue); @@ -2097,7 +2073,7 @@ static void test_color_table(UINT ilc) get_default_color_table(hdc, ilc & 0xfe, default_table); - himl = ImageList_Create(16, 16, ilc, 0, 3); + himl = pImageList_Create(16, 16, ilc, 0, 3); ok(himl != NULL, "got %p\n", himl); memset(bmi_buffer, 0, sizeof(bmi_buffer)); @@ -2131,17 +2107,17 @@ static void test_color_table(UINT ilc) dib32 = CreateDIBSection(hdc, bmi, DIB_RGB_COLORS, NULL, NULL, 0); /* add 32 first then 8. This won't set the color table */ - ret = ImageList_Add(himl, dib32, NULL); + ret = pImageList_Add(himl, dib32, NULL); ok(ret == 0, "got %d\n", ret); - ret = ImageList_Add(himl, dib8, NULL); + ret = pImageList_Add(himl, dib8, NULL); ok(ret == 1, "got %d\n", ret); check_color_table("add 32, 8", hdc, himl, ilc, default_table, NULL); /* since the previous _Adds didn't set the color table, this one will */ - ret = ImageList_Remove(himl, -1); + ret = pImageList_Remove(himl, -1); ok(ret, "got %d\n", ret); - ret = ImageList_Add(himl, dib8, NULL); + ret = pImageList_Add(himl, dib8, NULL); ok(ret == 0, "got %d\n", ret); memset(rgb, 0, sizeof(rgb)); @@ -2152,18 +2128,18 @@ static void test_color_table(UINT ilc) /* remove all, add 4. Color table remains the same since it's inplicitly been set by the previous _Add */ - ret = ImageList_Remove(himl, -1); + ret = pImageList_Remove(himl, -1); ok(ret, "got %d\n", ret); - ret = ImageList_Add(himl, dib4, NULL); + ret = pImageList_Add(himl, dib4, NULL); ok(ret == 0, "got %d\n", ret); check_color_table("remove all, add 4", hdc, himl, ilc, rgb, default_table); - ImageList_Destroy(himl); - himl = ImageList_Create(16, 16, ilc, 0, 3); + pImageList_Destroy(himl); + himl = pImageList_Create(16, 16, ilc, 0, 3); ok(himl != NULL, "got %p\n", himl); /* add 4 */ - ret = ImageList_Add(himl, dib4, NULL); + ret = pImageList_Add(himl, dib4, NULL); ok(ret == 0, "got %d\n", ret); memset(rgb, 0, 16 * sizeof(rgb[0])); @@ -2177,12 +2153,12 @@ static void test_color_table(UINT ilc) check_color_table("add 4", hdc, himl, ilc, rgb, default_table); - ImageList_Destroy(himl); - himl = ImageList_Create(16, 16, ilc, 0, 3); + pImageList_Destroy(himl); + himl = pImageList_Create(16, 16, ilc, 0, 3); ok(himl != NULL, "got %p\n", himl); /* set color table, add 8 */ - ret = ImageList_Remove(himl, -1); + ret = pImageList_Remove(himl, -1); ok(ret, "got %d\n", ret); memset(rgb, 0, sizeof(rgb)); rgb[0].rgbRed = 0xcc; @@ -2190,7 +2166,7 @@ static void test_color_table(UINT ilc) ret = pImageList_SetColorTable(himl, 0, 2, rgb); ok(ret == 2, "got %d\n", ret); /* the table is set, so this doesn't change it */ - ret = ImageList_Add(himl, dib8, NULL); + ret = pImageList_Add(himl, dib8, NULL); ok(ret == 0, "got %d\n", ret); memcpy(rgb + 2, default_table + 2, 254 * sizeof(rgb[0])); @@ -2200,7 +2176,7 @@ static void test_color_table(UINT ilc) DeleteObject(dib8); DeleteObject(dib4); DeleteDC(hdc); - ImageList_Destroy(himl); + pImageList_Destroy(himl); } static void test_copy(void) @@ -2209,21 +2185,41 @@ static void test_copy(void) BOOL ret; int count; - dst = ImageList_Create(5, 11, ILC_COLOR, 1, 1); - count = ImageList_GetImageCount(dst); + dst = pImageList_Create(5, 11, ILC_COLOR, 1, 1); + count = pImageList_GetImageCount(dst); ok(!count, "ImageList not empty.\n"); src = createImageList(7, 13); - count = ImageList_GetImageCount(src); + count = pImageList_GetImageCount(src); ok(count > 2, "Tests need an ImageList with more than 2 images\n"); /* ImageList_Copy() cannot copy between two ImageLists */ - ret = ImageList_Copy(dst, 0, src, 2, ILCF_MOVE); + ret = pImageList_Copy(dst, 0, src, 2, ILCF_MOVE); ok(!ret, "ImageList_Copy() should have returned FALSE\n"); - count = ImageList_GetImageCount(dst); + count = pImageList_GetImageCount(dst); ok(count == 0, "Expected no image in dst ImageList, got %d\n", count); - ImageList_Destroy(dst); - ImageList_Destroy(src); + pImageList_Destroy(dst); + pImageList_Destroy(src); +} + +static void test_loadimage(void) +{ + HIMAGELIST list; + DWORD flags; + + list = pImageList_LoadImageW( hinst, MAKEINTRESOURCEW(IDB_BITMAP_128x15), 16, 1, CLR_DEFAULT, + IMAGE_BITMAP, LR_CREATEDIBSECTION ); + ok( list != NULL, "got %p\n", list ); + flags = pImageList_GetFlags( list ); + ok( flags == (ILC_COLOR4 | ILC_MASK), "got %08x\n", flags ); + pImageList_Destroy( list ); + + list = pImageList_LoadImageW( hinst, MAKEINTRESOURCEW(IDB_BITMAP_128x15), 16, 1, CLR_NONE, + IMAGE_BITMAP, LR_CREATEDIBSECTION ); + ok( list != NULL, "got %p\n", list ); + flags = pImageList_GetFlags( list ); + ok( flags == ILC_COLOR4, "got %08x\n", flags ); + pImageList_Destroy( list ); } static void test_IImageList_Clone(void) @@ -2233,7 +2229,7 @@ static void test_IImageList_Clone(void) HRESULT hr; ULONG ref; - himl = ImageList_Create(16, 16, ILC_COLOR16, 0, 3); + himl = pImageList_Create(16, 16, ILC_COLOR16, 0, 3); imgl = (IImageList*)himl; if (0) @@ -2257,7 +2253,7 @@ static void test_IImageList_GetBkColor(void) COLORREF color; HRESULT hr; - himl = ImageList_Create(16, 16, ILC_COLOR16, 0, 3); + himl = pImageList_Create(16, 16, ILC_COLOR16, 0, 3); imgl = (IImageList*)himl; if (0) @@ -2279,7 +2275,7 @@ static void test_IImageList_SetBkColor(void) COLORREF color; HRESULT hr; - himl = ImageList_Create(16, 16, ILC_COLOR16, 0, 3); + himl = pImageList_Create(16, 16, ILC_COLOR16, 0, 3); imgl = (IImageList*)himl; if (0) @@ -2309,7 +2305,7 @@ static void test_IImageList_GetImageCount(void) int count; HRESULT hr; - himl = ImageList_Create(16, 16, ILC_COLOR16, 0, 3); + himl = pImageList_Create(16, 16, ILC_COLOR16, 0, 3); imgl = (IImageList*)himl; if (0) @@ -2333,7 +2329,7 @@ static void test_IImageList_GetIconSize(void) int cx, cy; HRESULT hr; - himl = ImageList_Create(16, 16, ILC_COLOR16, 0, 3); + himl = pImageList_Create(16, 16, ILC_COLOR16, 0, 3); imgl = (IImageList*)himl; hr = IImageList_GetIconSize(imgl, NULL, NULL); @@ -2348,17 +2344,47 @@ static void test_IImageList_GetIconSize(void) IImageList_Release(imgl); } +static void init_functions(void) +{ + HMODULE hComCtl32 = GetModuleHandleA("comctl32.dll"); + +#define X(f) p##f = (void*)GetProcAddress(hComCtl32, #f); +#define X2(f, ord) p##f = (void*)GetProcAddress(hComCtl32, (const char *)ord); + X(ImageList_Create); + X(ImageList_Destroy); + X(ImageList_Add); + X(ImageList_DrawIndirect); + X(ImageList_SetImageCount); + X(ImageList_SetImageCount); + X2(ImageList_SetColorTable, 390); + X(ImageList_GetFlags); + X(ImageList_BeginDrag); + X(ImageList_GetDragImage); + X(ImageList_EndDrag); + X(ImageList_GetImageCount); + X(ImageList_SetDragCursorImage); + X(ImageList_GetIconSize); + X(ImageList_Remove); + X(ImageList_ReplaceIcon); + X(ImageList_Replace); + X(ImageList_Merge); + X(ImageList_GetImageInfo); + X(ImageList_Write); + X(ImageList_Read); + X(ImageList_Copy); + X(ImageList_LoadImageW); + X(ImageList_CoCreateInstance); + X(HIMAGELIST_QueryInterface); +#undef X +#undef X2 +} + START_TEST(imagelist) { ULONG_PTR ctx_cookie; HANDLE hCtx; - HMODULE hComCtl32 = GetModuleHandleA("comctl32.dll"); - pImageList_Create = NULL; /* These are not needed for non-v6.0 tests*/ - pImageList_Add = NULL; - pImageList_DrawIndirect = (void*)GetProcAddress(hComCtl32, "ImageList_DrawIndirect"); - pImageList_SetImageCount = (void*)GetProcAddress(hComCtl32, "ImageList_SetImageCount"); - pImageList_SetColorTable = (void*)GetProcAddress(hComCtl32, (const char*)390); + init_functions(); hinst = GetModuleHandleA(NULL); @@ -2377,31 +2403,32 @@ START_TEST(imagelist) test_color_table(ILC_COLOR4); test_color_table(ILC_COLOR8); test_copy(); - - FreeLibrary(hComCtl32); + test_loadimage(); /* Now perform v6 tests */ - if (!load_v6_module(&ctx_cookie, &hCtx)) return; - /* Reload comctl32 */ - hComCtl32 = LoadLibraryA("comctl32.dll"); - pImageList_Create = (void*)GetProcAddress(hComCtl32, "ImageList_Create"); - pImageList_Add = (void*)GetProcAddress(hComCtl32, "ImageList_Add"); - pImageList_DrawIndirect = (void*)GetProcAddress(hComCtl32, "ImageList_DrawIndirect"); - pImageList_SetImageCount = (void*)GetProcAddress(hComCtl32, "ImageList_SetImageCount"); - pImageList_CoCreateInstance = (void*)GetProcAddress(hComCtl32, "ImageList_CoCreateInstance"); - pHIMAGELIST_QueryInterface = (void*)GetProcAddress(hComCtl32, "HIMAGELIST_QueryInterface"); + init_functions(); CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); /* Do v6.0 tests */ + test_add_remove(); + test_imagecount(); + test_DrawIndirect(); + test_merge(); + test_imagelist_storage(); + test_iconsize(); + test_color_table(ILC_COLOR4); + test_color_table(ILC_COLOR8); + test_copy(); + test_loadimage(); + test_ImageList_DrawIndirect(); test_shell_imagelist(); test_iimagelist(); - test_hotspot_v6(); test_IImageList_Add_Remove(); test_IImageList_Get_SetImageCount(); test_IImageList_Draw(); diff --git a/modules/rostests/winetests/comctl32/listbox.c b/modules/rostests/winetests/comctl32/listbox.c new file mode 100644 index 00000000000..d24aaa22166 --- /dev/null +++ b/modules/rostests/winetests/comctl32/listbox.c @@ -0,0 +1,1851 @@ +/* Unit test suite for list boxes. + * + * Copyright 2003 Ferenc Wagner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "precomp.h" + +static const char * const strings[4] = { + "First added", + "Second added", + "Third added", + "Fourth added which is very long because at some time we only had a 256 byte character buffer and " + "that was overflowing in one of those applications that had a common dialog file open box and tried " + "to add a 300 characters long custom filter string which of course the code did not like and crashed. " + "Just make sure this string is longer than 256 characters." +}; + +static const char BAD_EXTENSION[] = "*.badtxt"; + +static HWND create_listbox(DWORD add_style, HWND parent) +{ + INT_PTR ctl_id = 0; + HWND handle; + + if (parent) + ctl_id=1; + + handle = CreateWindowA("LISTBOX", "TestList", (LBS_STANDARD & ~LBS_SORT) | add_style, 0, 0, 100, 100, + parent, (HMENU)ctl_id, NULL, 0); + ok(handle != NULL, "Failed to create listbox window.\n"); + + SendMessageA(handle, LB_ADDSTRING, 0, (LPARAM) strings[0]); + SendMessageA(handle, LB_ADDSTRING, 0, (LPARAM) strings[1]); + SendMessageA(handle, LB_ADDSTRING, 0, (LPARAM) strings[2]); + SendMessageA(handle, LB_ADDSTRING, 0, (LPARAM) strings[3]); + + return handle; +} + +struct listbox_prop +{ + DWORD add_style; +}; + +struct listbox_stat +{ + int selected, anchor, caret, selcount; +}; + +struct listbox_test +{ + struct listbox_prop prop; + struct listbox_stat init, init_todo; + struct listbox_stat click, click_todo; + struct listbox_stat step, step_todo; + struct listbox_stat sel, sel_todo; +}; + +static void listbox_query(HWND handle, struct listbox_stat *results) +{ + results->selected = SendMessageA(handle, LB_GETCURSEL, 0, 0); + results->anchor = SendMessageA(handle, LB_GETANCHORINDEX, 0, 0); + results->caret = SendMessageA(handle, LB_GETCARETINDEX, 0, 0); + results->selcount = SendMessageA(handle, LB_GETSELCOUNT, 0, 0); +} + +static void buttonpress(HWND handle, WORD x, WORD y) +{ + LPARAM lp = x + (y << 16); + + SendMessageA(handle, WM_LBUTTONDOWN, MK_LBUTTON, lp); + SendMessageA(handle, WM_LBUTTONUP, 0, lp); +} + +static void keypress(HWND handle, WPARAM keycode, BYTE scancode, BOOL extended) +{ + LPARAM lp = 1 + (scancode << 16) + (extended ? KEYEVENTF_EXTENDEDKEY : 0); + + SendMessageA(handle, WM_KEYDOWN, keycode, lp); + SendMessageA(handle, WM_KEYUP , keycode, lp | 0xc000000); +} + +#define listbox_field_ok(t, s, f, got) \ + ok (t.s.f==got.f, "style %#x, step " #s ", field " #f \ + ": expected %d, got %d\n", (unsigned int)t.prop.add_style, \ + t.s.f, got.f) + +#define listbox_todo_field_ok(t, s, f, got) \ + todo_wine_if (t.s##_todo.f) { listbox_field_ok(t, s, f, got); } + +#define listbox_ok(t, s, got) \ + listbox_todo_field_ok(t, s, selected, got); \ + listbox_todo_field_ok(t, s, anchor, got); \ + listbox_todo_field_ok(t, s, caret, got); \ + listbox_todo_field_ok(t, s, selcount, got) + +static void run_test(const struct listbox_test test) +{ + struct listbox_stat answer; + HWND hLB=create_listbox (test.prop.add_style, 0); + RECT second_item; + int i, res; + + listbox_query (hLB, &answer); + listbox_ok (test, init, answer); + + SendMessageA(hLB, LB_GETITEMRECT, 1, (LPARAM) &second_item); + buttonpress(hLB, (WORD)second_item.left, (WORD)second_item.top); + + listbox_query(hLB, &answer); + listbox_ok(test, click, answer); + + keypress(hLB, VK_DOWN, 0x50, TRUE); + + listbox_query(hLB, &answer); + listbox_ok(test, step, answer); + + DestroyWindow(hLB); + + hLB = create_listbox(test.prop.add_style, 0); + + SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 2)); + listbox_query(hLB, &answer); + listbox_ok(test, sel, answer); + + for (i = 0; i < 4; i++) + { + DWORD size = SendMessageA(hLB, LB_GETTEXTLEN, i, 0); + int resA, resW; + WCHAR *txtw; + CHAR *txt; + + txt = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size + 1); + resA = SendMessageA(hLB, LB_GETTEXT, i, (LPARAM)txt); + ok(!strcmp(txt, strings[i]), "returned string for item %d does not match %s vs %s\n", i, txt, strings[i]); + + txtw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (size + 1) * sizeof(*txtw)); + resW = SendMessageW(hLB, LB_GETTEXT, i, (LPARAM)txtw); + if (resA != resW) + trace("SendMessageW(LB_GETTEXT) not supported on this platform (resA=%d resW=%d), skipping...\n", resA, resW); + else + { + WideCharToMultiByte(CP_ACP, 0, txtw, -1, txt, size, NULL, NULL); + ok(!strcmp (txt, strings[i]), "returned string for item %d does not match %s vs %s\n", i, txt, strings[i]); + } + + HeapFree(GetProcessHeap(), 0, txtw); + HeapFree(GetProcessHeap(), 0, txt); + } + + /* Confirm the count of items, and that an invalid delete does not remove anything */ + res = SendMessageA(hLB, LB_GETCOUNT, 0, 0); + ok(res == 4, "Expected 4 items, got %d\n", res); + res = SendMessageA(hLB, LB_DELETESTRING, -1, 0); + ok(res == LB_ERR, "Expected LB_ERR items, got %d\n", res); + res = SendMessageA(hLB, LB_DELETESTRING, 4, 0); + ok(res == LB_ERR, "Expected LB_ERR items, got %d\n", res); + res = SendMessageA(hLB, LB_GETCOUNT, 0, 0); + ok(res == 4, "Expected 4 items, got %d\n", res); + + DestroyWindow(hLB); +} + +static void test_item_height(void) +{ + INT itemHeight; + TEXTMETRICA tm; + HFONT font; + HWND hLB; + HDC hdc; + + hLB = create_listbox (0, 0); + ok ((hdc = GetDCEx( hLB, 0, DCX_CACHE )) != 0, "Can't get hdc\n"); + ok ((font = GetCurrentObject(hdc, OBJ_FONT)) != 0, "Can't get the current font\n"); + ok (GetTextMetricsA( hdc, &tm ), "Can't read font metrics\n"); + ReleaseDC( hLB, hdc); + + ok (SendMessageA(hLB, WM_SETFONT, (WPARAM)font, 0) == 0, "Can't set font\n"); + + itemHeight = SendMessageA(hLB, LB_GETITEMHEIGHT, 0, 0); + ok (itemHeight == tm.tmHeight, "Item height wrong, got %d, expecting %d\n", itemHeight, tm.tmHeight); + + DestroyWindow (hLB); + + hLB = CreateWindowA("LISTBOX", "TestList", LBS_OWNERDRAWVARIABLE, 0, 0, 100, 100, NULL, NULL, NULL, 0); + + itemHeight = SendMessageA(hLB, LB_GETITEMHEIGHT, 0, 0); + ok(itemHeight > 0 && itemHeight <= tm.tmHeight, "Unexpected item height %d, expected %d.\n", + itemHeight, tm.tmHeight); + itemHeight = SendMessageA(hLB, LB_GETITEMHEIGHT, 5, 0); + ok(itemHeight > 0 && itemHeight <= tm.tmHeight, "Unexpected item height %d, expected %d.\n", + itemHeight, tm.tmHeight); + itemHeight = SendMessageA(hLB, LB_GETITEMHEIGHT, -5, 0); + ok(itemHeight > 0 && itemHeight <= tm.tmHeight, "Unexpected item height %d, expected %d.\n", + itemHeight, tm.tmHeight); + + DestroyWindow (hLB); +} + +static int got_selchange; + +static LRESULT WINAPI main_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_DRAWITEM: + { + RECT rc_item, rc_client, rc_clip; + DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lparam; + + ok(wparam == dis->CtlID, "got wParam=%08lx instead of %08x\n", wparam, dis->CtlID); + ok(dis->CtlType == ODT_LISTBOX, "wrong CtlType %04x\n", dis->CtlType); + + GetClientRect(dis->hwndItem, &rc_client); + GetClipBox(dis->hDC, &rc_clip); + ok(EqualRect(&rc_client, &rc_clip) || IsRectEmpty(&rc_clip), + "client rect of the listbox should be equal to the clip box," + "or the clip box should be empty\n"); + + SendMessageA(dis->hwndItem, LB_GETITEMRECT, dis->itemID, (LPARAM)&rc_item); + ok(EqualRect(&dis->rcItem, &rc_item), "item rects are not equal\n"); + + break; + } + + case WM_COMMAND: + if (HIWORD( wparam ) == LBN_SELCHANGE) got_selchange++; + break; + + default: + break; + } + + return DefWindowProcA(hwnd, msg, wparam, lparam); +} + +static HWND create_parent( void ) +{ + static ATOM class; + WNDCLASSA cls; + + if (!class) + { + cls.style = 0; + cls.lpfnWndProc = main_window_proc; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = GetModuleHandleA(NULL); + cls.hIcon = 0; + cls.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW); + cls.hbrBackground = GetStockObject(WHITE_BRUSH); + cls.lpszMenuName = NULL; + cls.lpszClassName = "main_window_class"; + class = RegisterClassA( &cls ); + } + + return CreateWindowExA(0, "main_window_class", NULL, WS_POPUP | WS_VISIBLE, 100, 100, 400, 400, GetDesktopWindow(), + 0, GetModuleHandleA(NULL), NULL); +} + +static void test_ownerdraw(void) +{ + HWND parent, hLB; + INT ret; + RECT rc; + + parent = create_parent(); + assert(parent); + + hLB = create_listbox(LBS_OWNERDRAWFIXED | WS_CHILD | WS_VISIBLE, parent); + assert(hLB); + + SetForegroundWindow(hLB); + UpdateWindow(hLB); + + /* make height short enough */ + SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc); + SetWindowPos(hLB, 0, 0, 0, 100, rc.bottom - rc.top + 1, SWP_NOZORDER | SWP_NOMOVE); + + /* make 0 item invisible */ + SendMessageA(hLB, LB_SETTOPINDEX, 1, 0); + ret = SendMessageA(hLB, LB_GETTOPINDEX, 0, 0); + ok(ret == 1, "wrong top index %d\n", ret); + + SendMessageA(hLB, LB_GETITEMRECT, 0, (LPARAM)&rc); + ok(!IsRectEmpty(&rc), "empty item rect\n"); + ok(rc.top < 0, "rc.top is not negative (%d)\n", rc.top); + + DestroyWindow(hLB); + DestroyWindow(parent); +} + +#define listbox_test_query(exp, got) \ + ok(exp.selected == got.selected, "expected selected %d, got %d\n", exp.selected, got.selected); \ + ok(exp.anchor == got.anchor, "expected anchor %d, got %d\n", exp.anchor, got.anchor); \ + ok(exp.caret == got.caret, "expected caret %d, got %d\n", exp.caret, got.caret); \ + ok(exp.selcount == got.selcount, "expected selcount %d, got %d\n", exp.selcount, got.selcount); + +static void test_LB_SELITEMRANGE(void) +{ + static const struct listbox_stat test_nosel = { 0, LB_ERR, 0, 0 }; + static const struct listbox_stat test_1 = { 0, LB_ERR, 0, 2 }; + static const struct listbox_stat test_2 = { 0, LB_ERR, 0, 3 }; + static const struct listbox_stat test_3 = { 0, LB_ERR, 0, 4 }; + struct listbox_stat answer; + HWND hLB; + INT ret; + + hLB = create_listbox(LBS_EXTENDEDSEL, 0); + assert(hLB); + + listbox_query(hLB, &answer); + listbox_test_query(test_nosel, answer); + + ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, 2)); + ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret); + listbox_query(hLB, &answer); + listbox_test_query(test_1, answer); + + SendMessageA(hLB, LB_SETSEL, FALSE, -1); + listbox_query(hLB, &answer); + listbox_test_query(test_nosel, answer); + + ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(0, 4)); + ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret); + listbox_query(hLB, &answer); + listbox_test_query(test_3, answer); + + SendMessageA(hLB, LB_SETSEL, FALSE, -1); + listbox_query(hLB, &answer); + listbox_test_query(test_nosel, answer); + + ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(-5, 5)); + ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret); + listbox_query(hLB, &answer); + listbox_test_query(test_nosel, answer); + + SendMessageA(hLB, LB_SETSEL, FALSE, -1); + listbox_query(hLB, &answer); + listbox_test_query(test_nosel, answer); + + ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(2, 10)); + ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret); + listbox_query(hLB, &answer); + listbox_test_query(test_1, answer); + + SendMessageA(hLB, LB_SETSEL, FALSE, -1); + listbox_query(hLB, &answer); + listbox_test_query(test_nosel, answer); + + ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(4, 10)); + ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret); + listbox_query(hLB, &answer); + listbox_test_query(test_nosel, answer); + + SendMessageA(hLB, LB_SETSEL, FALSE, -1); + listbox_query(hLB, &answer); + listbox_test_query(test_nosel, answer); + + ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(10, 1)); + ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret); + listbox_query(hLB, &answer); + listbox_test_query(test_2, answer); + + SendMessageA(hLB, LB_SETSEL, FALSE, -1); + listbox_query(hLB, &answer); + listbox_test_query(test_nosel, answer); + + ret = SendMessageA(hLB, LB_SELITEMRANGE, TRUE, MAKELPARAM(1, -1)); + ok(ret == LB_OKAY, "LB_SELITEMRANGE returned %d instead of LB_OKAY\n", ret); + listbox_query(hLB, &answer); + listbox_test_query(test_2, answer); + + DestroyWindow(hLB); +} + +static void test_LB_SETCURSEL(void) +{ + HWND parent, hLB; + INT ret; + + parent = create_parent(); + ok(parent != NULL, "Failed to create parent window.\n"); + + hLB = create_listbox(LBS_NOINTEGRALHEIGHT | WS_CHILD, parent); + ok(hLB != NULL, "Failed to create listbox.\n"); + + SendMessageA(hLB, LB_SETITEMHEIGHT, 0, 32); + + ret = SendMessageA(hLB, LB_SETCURSEL, 2, 0); + ok(ret == 2, "LB_SETCURSEL returned %d instead of 2\n", ret); + ret = GetScrollPos(hLB, SB_VERT); + ok(ret == 0, "expected vscroll 0, got %d\n", ret); + + ret = SendMessageA(hLB, LB_SETCURSEL, 3, 0); + ok(ret == 3, "LB_SETCURSEL returned %d instead of 3\n", ret); + ret = GetScrollPos(hLB, SB_VERT); + ok(ret == 1, "expected vscroll 1, got %d\n", ret); + + DestroyWindow(hLB); +} + +static void test_listbox_height(void) +{ + HWND hList; + int r, id; + + hList = CreateWindowA( "ListBox", "list test", 0, + 1, 1, 600, 100, NULL, NULL, NULL, NULL ); + ok( hList != NULL, "failed to create listbox\n"); + + id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi"); + ok( id == 0, "item id wrong\n"); + + r = SendMessageA( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 20, 0 )); + ok( r == 0, "send message failed\n"); + + r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 ); + ok( r == 20, "height wrong\n"); + + r = SendMessageA( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 0, 30 )); + ok( r == -1, "send message failed\n"); + + r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 ); + ok( r == 20, "height wrong\n"); + + r = SendMessageA( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 256, 0 )); + ok( r == -1, "Failed to set item height, %d.\n", r); + + r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 ); + ok( r == 20, "Unexpected item height %d.\n", r); + + r = SendMessageA( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 0xff, 0 )); + ok( r == 0, "send message failed\n"); + + r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 ); + ok( r == 0xff, "height wrong\n"); + + DestroyWindow( hList ); +} + +static void test_itemfrompoint(void) +{ + /* WS_POPUP is required in order to have a more accurate size calculation ( + without caption). LBS_NOINTEGRALHEIGHT is required in order to test + behavior of partially-displayed item. + */ + HWND hList = CreateWindowA( "ListBox", "list test", + WS_VISIBLE|WS_POPUP|LBS_NOINTEGRALHEIGHT, + 1, 1, 600, 100, NULL, NULL, NULL, NULL ); + ULONG r, id; + RECT rc; + + /* For an empty listbox win2k returns 0x1ffff, win98 returns 0x10000, nt4 returns 0xffffffff */ + r = SendMessageA(hList, LB_ITEMFROMPOINT, 0, MAKELPARAM( /* x */ 30, /* y */ 30 )); + ok( r == 0x1ffff || r == 0x10000 || r == 0xffffffff, "ret %x\n", r ); + + r = SendMessageA(hList, LB_ITEMFROMPOINT, 0, MAKELPARAM( 700, 30 )); + ok( r == 0x1ffff || r == 0x10000 || r == 0xffffffff, "ret %x\n", r ); + + r = SendMessageA(hList, LB_ITEMFROMPOINT, 0, MAKELPARAM( 30, 300 )); + ok( r == 0x1ffff || r == 0x10000 || r == 0xffffffff, "ret %x\n", r ); + + id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi"); + ok( id == 0, "item id wrong\n"); + id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi1"); + ok( id == 1, "item id wrong\n"); + + r = SendMessageA(hList, LB_ITEMFROMPOINT, 0, MAKELPARAM( /* x */ 30, /* y */ 30 )); + ok( r == 0x1, "ret %x\n", r ); + + r = SendMessageA(hList, LB_ITEMFROMPOINT, 0, MAKELPARAM( /* x */ 30, /* y */ 601 )); + ok( r == 0x10001, "ret %x\n", r ); + + /* Resize control so that below assertions about sizes are valid */ + r = SendMessageA( hList, LB_GETITEMRECT, 0, (LPARAM)&rc); + ok( r == 1, "ret %x\n", r); + r = MoveWindow(hList, 1, 1, 600, (rc.bottom - rc.top + 1) * 9 / 2, TRUE); + ok( r != 0, "ret %x\n", r); + + id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi2"); + ok( id == 2, "item id wrong\n"); + id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi3"); + ok( id == 3, "item id wrong\n"); + id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi4"); + ok( id == 4, "item id wrong\n"); + id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi5"); + ok( id == 5, "item id wrong\n"); + id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi6"); + ok( id == 6, "item id wrong\n"); + id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi7"); + ok( id == 7, "item id wrong\n"); + + /* Set the listbox up so that id 1 is at the top, this leaves 5 + partially visible at the bottom and 6, 7 are invisible */ + + SendMessageA( hList, LB_SETTOPINDEX, 1, 0); + r = SendMessageA( hList, LB_GETTOPINDEX, 0, 0); + ok( r == 1, "top %d\n", r); + + r = SendMessageA( hList, LB_GETITEMRECT, 5, (LPARAM)&rc); + ok( r == 1, "ret %x\n", r); + r = SendMessageA( hList, LB_GETITEMRECT, 6, (LPARAM)&rc); + ok( r == 0, "ret %x\n", r); + + r = SendMessageA( hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(/* x */ 10, /* y */ 10) ); + ok( r == 1, "ret %x\n", r); + + r = SendMessageA( hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(1000, 10) ); + ok( r == 0x10001, "ret %x\n", r ); + + r = SendMessageA( hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(10, -10) ); + ok( r == 0x10001, "ret %x\n", r ); + + r = SendMessageA( hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(10, 100) ); + ok( r == 0x10005, "item %x\n", r ); + + r = SendMessageA( hList, LB_ITEMFROMPOINT, 0, MAKELPARAM(10, 200) ); + ok( r == 0x10005, "item %x\n", r ); + + DestroyWindow( hList ); +} + +static void test_listbox_item_data(void) +{ + HWND hList; + int r, id; + + hList = CreateWindowA( "ListBox", "list test", 0, + 1, 1, 600, 100, NULL, NULL, NULL, NULL ); + ok( hList != NULL, "failed to create listbox\n"); + + id = SendMessageA( hList, LB_ADDSTRING, 0, (LPARAM) "hi"); + ok( id == 0, "item id wrong\n"); + + r = SendMessageA( hList, LB_SETITEMDATA, 0, MAKELPARAM( 20, 0 )); + ok(r == TRUE, "LB_SETITEMDATA returned %d instead of TRUE\n", r); + + r = SendMessageA( hList, LB_GETITEMDATA, 0, 0); + ok( r == 20, "get item data failed\n"); + + DestroyWindow( hList ); +} + +static void test_listbox_LB_DIR(void) +{ + HWND hList; + int res, itemCount; + int itemCount_justFiles; + int itemCount_justDrives; + int itemCount_allFiles; + int itemCount_allDirs; + int i; + char pathBuffer[MAX_PATH]; + char * p; + char driveletter; + const char *wildcard = "*"; + HANDLE file; + + file = CreateFileA( "wtest1.tmp.c", GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL ); + ok(file != INVALID_HANDLE_VALUE, "Error creating the test file: %d\n", GetLastError()); + CloseHandle( file ); + + /* NOTE: for this test to succeed, there must be no subdirectories + under the current directory. In addition, there must be at least + one file that fits the wildcard w*.c . Normally, the test + directory itself satisfies both conditions. + */ + hList = CreateWindowA( "ListBox", "list test", WS_VISIBLE|WS_POPUP, + 1, 1, 600, 100, NULL, NULL, NULL, NULL ); + assert(hList); + + /* Test for standard usage */ + + /* This should list all the files in the test directory. */ + strcpy(pathBuffer, wildcard); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, 0, (LPARAM)pathBuffer); + if (res == -1) /* "*" wildcard doesn't work on win9x */ + { + wildcard = "*.*"; + strcpy(pathBuffer, wildcard); + res = SendMessageA(hList, LB_DIR, 0, (LPARAM)pathBuffer); + } + ok (res >= 0, "SendMessage(LB_DIR, 0, *) failed - 0x%08x\n", GetLastError()); + + /* There should be some content in the listbox */ + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount > 0, "SendMessage(LB_DIR) did NOT fill the listbox!\n"); + itemCount_allFiles = itemCount; + ok(res + 1 == itemCount, + "SendMessage(LB_DIR, 0, *) returned incorrect index (expected %d got %d)!\n", + itemCount - 1, res); + + /* This tests behavior when no files match the wildcard */ + strcpy(pathBuffer, BAD_EXTENSION); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, 0, (LPARAM)pathBuffer); + ok (res == -1, "SendMessage(LB_DIR, 0, %s) returned %d, expected -1\n", BAD_EXTENSION, res); + + /* There should be NO content in the listbox */ + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount == 0, "SendMessage(LB_DIR) DID fill the listbox!\n"); + + + /* This should list all the w*.c files in the test directory + * As of this writing, this includes win.c, winstation.c, wsprintf.c + */ + strcpy(pathBuffer, "w*.c"); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, 0, (LPARAM)pathBuffer); + ok (res >= 0, "SendMessage(LB_DIR, 0, w*.c) failed - 0x%08x\n", GetLastError()); + + /* Path specification does NOT converted to uppercase */ + ok (!strcmp(pathBuffer, "w*.c"), + "expected no change to pathBuffer, got %s\n", pathBuffer); + + /* There should be some content in the listbox */ + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount > 0, "SendMessage(LB_DIR) did NOT fill the listbox!\n"); + itemCount_justFiles = itemCount; + ok(res + 1 == itemCount, + "SendMessage(LB_DIR, 0, w*.c) returned incorrect index (expected %d got %d)!\n", + itemCount - 1, res); + + /* Every single item in the control should start with a w and end in .c */ + for (i = 0; i < itemCount; i++) + { + memset(pathBuffer, 0, MAX_PATH); + SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer); + p = pathBuffer + strlen(pathBuffer); + ok(((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') && + (*(p-1) == 'c' || *(p-1) == 'C') && + (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer); + } + + /* Test DDL_DIRECTORY */ + strcpy(pathBuffer, wildcard); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY, (LPARAM)pathBuffer); + ok (res > 0, "SendMessage(LB_DIR, DDL_DIRECTORY, *) failed - 0x%08x\n", GetLastError()); + + /* There should be some content in the listbox. + * All files plus "[..]" + */ + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + itemCount_allDirs = itemCount - itemCount_allFiles; + ok (itemCount >= itemCount_allFiles, + "SendMessage(LB_DIR, DDL_DIRECTORY, *) filled with %d entries, expected > %d\n", + itemCount, itemCount_allFiles); + ok(res + 1 == itemCount, + "SendMessage(LB_DIR, DDL_DIRECTORY, *) returned incorrect index (expected %d got %d)!\n", + itemCount - 1, res); + + /* This tests behavior when no files match the wildcard */ + strcpy(pathBuffer, BAD_EXTENSION); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY, (LPARAM)pathBuffer); + ok (res == -1, "SendMessage(LB_DIR, DDL_DIRECTORY, %s) returned %d, expected -1\n", BAD_EXTENSION, res); + + /* There should be NO content in the listbox */ + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount == 0, "SendMessage(LB_DIR) DID fill the listbox!\n"); + + /* Test DDL_DIRECTORY */ + strcpy(pathBuffer, "w*.c"); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY, (LPARAM)pathBuffer); + ok (res >= 0, "SendMessage(LB_DIR, DDL_DIRECTORY, w*.c) failed - 0x%08x\n", GetLastError()); + + /* There should be some content in the listbox. Since the parent directory does not + * fit w*.c, there should be exactly the same number of items as without DDL_DIRECTORY + */ + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_justFiles, + "SendMessage(LB_DIR, DDL_DIRECTORY, w*.c) filled with %d entries, expected %d\n", + itemCount, itemCount_justFiles); + ok(res + 1 == itemCount, + "SendMessage(LB_DIR, DDL_DIRECTORY, w*.c) returned incorrect index (expected %d got %d)!\n", + itemCount - 1, res); + + /* Every single item in the control should start with a w and end in .c. */ + for (i = 0; i < itemCount; i++) + { + memset(pathBuffer, 0, MAX_PATH); + SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer); + p = pathBuffer + strlen(pathBuffer); + ok( + ((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') && + (*(p-1) == 'c' || *(p-1) == 'C') && + (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer); + } + + /* Test DDL_DRIVES|DDL_EXCLUSIVE */ + strcpy(pathBuffer, wildcard); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, (LPARAM)pathBuffer); + ok (res >= 0, "SendMessage(LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, *) failed - 0x%08x\n", GetLastError()); + + /* There should be some content in the listbox. In particular, there should + * be at least one element before, since the string "[-c-]" should + * have been added. Depending on the user setting, more drives might have + * been added. + */ + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount >= 1, + "SendMessage(LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, *) filled with %d entries, expected at least %d\n", + itemCount, 1); + itemCount_justDrives = itemCount; + ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, *) returned incorrect index!\n"); + + /* Every single item in the control should fit the format [-c-] */ + for (i = 0; i < itemCount; i++) + { + memset(pathBuffer, 0, MAX_PATH); + driveletter = '\0'; + SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer); + ok( strlen(pathBuffer) == 5, "Length of drive string is not 5\n" ); + ok( sscanf(pathBuffer, "[-%c-]", &driveletter) == 1, "Element %d (%s) does not fit [-X-]\n", i, pathBuffer); + ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter); + if (!(driveletter >= 'a' && driveletter <= 'z')) + { + /* Correct after invalid entry is found */ + itemCount_justDrives--; + } + } + + /* This tests behavior when no files match the wildcard */ + strcpy(pathBuffer, BAD_EXTENSION); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, (LPARAM)pathBuffer); + ok (res == itemCount_justDrives -1, "SendMessage(LB_DIR, DDL_DRIVES|DDL_EXCLUSIVE, %s) returned %d, expected %d\n", + BAD_EXTENSION, res, itemCount_justDrives -1); + + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_justDrives, "SendMessage(LB_DIR) returned %d expected %d\n", + itemCount, itemCount_justDrives); + + /* Test DDL_DRIVES. */ + strcpy(pathBuffer, wildcard); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DRIVES, (LPARAM)pathBuffer); + ok (res > 0, "SendMessage(LB_DIR, DDL_DRIVES, *) failed - 0x%08x\n", GetLastError()); + + /* There should be some content in the listbox. In particular, there should + * be at least one element before, since the string "[-c-]" should + * have been added. Depending on the user setting, more drives might have + * been added. + */ + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_justDrives + itemCount_allFiles, + "SendMessage(LB_DIR, DDL_DRIVES, *) filled with %d entries, expected %d\n", + itemCount, itemCount_justDrives + itemCount_allFiles); + ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DRIVES, *) returned incorrect index!\n"); + + /* This tests behavior when no files match the wildcard */ + strcpy(pathBuffer, BAD_EXTENSION); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DRIVES, (LPARAM)pathBuffer); + ok (res == itemCount_justDrives -1, "SendMessage(LB_DIR, DDL_DRIVES, %s) returned %d, expected %d\n", + BAD_EXTENSION, res, itemCount_justDrives -1); + + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount == res + 1, "SendMessage(LB_DIR) returned %d expected %d\n", itemCount, res + 1); + + /* Test DDL_DRIVES. */ + strcpy(pathBuffer, "w*.c"); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DRIVES, (LPARAM)pathBuffer); + ok (res > 0, "SendMessage(LB_DIR, DDL_DRIVES, w*.c) failed - 0x%08x\n", GetLastError()); + + /* There should be some content in the listbox. In particular, there should + * be at least one element before, since the string "[-c-]" should + * have been added. Depending on the user setting, more drives might have + * been added. + */ + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_justDrives + itemCount_justFiles, + "SendMessage(LB_DIR, DDL_DRIVES, w*.c) filled with %d entries, expected %d\n", + itemCount, itemCount_justDrives + itemCount_justFiles); + ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DRIVES, w*.c) returned incorrect index!\n"); + + /* Every single item in the control should fit the format [-c-], or w*.c */ + for (i = 0; i < itemCount; i++) + { + memset(pathBuffer, 0, MAX_PATH); + driveletter = '\0'; + SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer); + p = pathBuffer + strlen(pathBuffer); + if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1) + { + ok( strlen(pathBuffer) == 5, "Length of drive string is not 5\n" ); + ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter); + } + else + { + ok( + ((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') && + (*(p-1) == 'c' || *(p-1) == 'C') && + (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer); + } + } + + /* Test DDL_DIRECTORY|DDL_DRIVES. This does *not* imply DDL_EXCLUSIVE */ + strcpy(pathBuffer, wildcard); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES, (LPARAM)pathBuffer); + ok (res > 0, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES, *) failed - 0x%08x\n", GetLastError()); + + /* There should be some content in the listbox. In particular, there should + * be exactly the number of plain files, plus the number of mapped drives. + */ + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_allFiles + itemCount_justDrives + itemCount_allDirs, + "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES) filled with %d entries, expected %d\n", + itemCount, itemCount_allFiles + itemCount_justDrives + itemCount_allDirs); + ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES, w*.c) returned incorrect index!\n"); + + /* Every single item in the control should start with a w and end in .c, + * except for the "[..]" string, which should appear exactly as it is, + * and the mapped drives in the format "[-X-]". + */ + for (i = 0; i < itemCount; i++) + { + memset(pathBuffer, 0, MAX_PATH); + driveletter = '\0'; + SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer); + if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1) + ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter); + } + + /* This tests behavior when no files match the wildcard */ + strcpy(pathBuffer, BAD_EXTENSION); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES, (LPARAM)pathBuffer); + ok (res == itemCount_justDrives -1, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES, %s) returned %d, expected %d\n", + BAD_EXTENSION, res, itemCount_justDrives -1); + + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount == res + 1, "SendMessage(LB_DIR) returned %d expected %d\n", itemCount, res + 1); + + /* Test DDL_DIRECTORY|DDL_DRIVES. */ + strcpy(pathBuffer, "w*.c"); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES, (LPARAM)pathBuffer); + ok (res > 0, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES, w*.c) failed - 0x%08x\n", GetLastError()); + + /* There should be some content in the listbox. In particular, there should + * be exactly the number of plain files, plus the number of mapped drives. + */ + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_justFiles + itemCount_justDrives, + "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES) filled with %d entries, expected %d\n", + itemCount, itemCount_justFiles + itemCount_justDrives); + ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES, w*.c) returned incorrect index!\n"); + + /* Every single item in the control should start with a w and end in .c, + * except the mapped drives in the format "[-X-]". The "[..]" directory + * should not appear. + */ + for (i = 0; i < itemCount; i++) + { + memset(pathBuffer, 0, MAX_PATH); + driveletter = '\0'; + SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer); + p = pathBuffer + strlen(pathBuffer); + if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1) + ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter); + else + ok( + ((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') && + (*(p-1) == 'c' || *(p-1) == 'C') && + (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer); + } + + /* Test DDL_DIRECTORY|DDL_EXCLUSIVE. */ + strcpy(pathBuffer, wildcard); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, (LPARAM)pathBuffer); + ok (res != -1 || broken(res == -1), "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, *) failed err %u\n", + GetLastError()); + + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_allDirs, + "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE) filled with %d entries, expected %d\n", + itemCount, itemCount_allDirs); + ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, *) returned incorrect index!\n"); + + if (itemCount && GetCurrentDirectoryA( MAX_PATH, pathBuffer ) > 3) /* there's no [..] in drive root */ + { + memset(pathBuffer, 0, MAX_PATH); + SendMessageA(hList, LB_GETTEXT, 0, (LPARAM)pathBuffer); + ok( !strcmp(pathBuffer, "[..]"), "First element is not [..]\n"); + } + + /* This tests behavior when no files match the wildcard */ + strcpy(pathBuffer, BAD_EXTENSION); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, (LPARAM)pathBuffer); + ok (res == -1, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, %s) returned %d, expected %d\n", + BAD_EXTENSION, res, -1); + + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount == res + 1, "SendMessage(LB_DIR) returned %d expected %d\n", itemCount, res + 1); + + + /* Test DDL_DIRECTORY|DDL_EXCLUSIVE. */ + strcpy(pathBuffer, "w*.c"); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, (LPARAM)pathBuffer); + ok (res == LB_ERR, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE, w*.c) returned %d expected %d\n", res, LB_ERR); + + /* There should be no elements, since "[..]" does not fit w*.c */ + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount == 0, + "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_EXCLUSIVE) filled with %d entries, expected %d\n", + itemCount, 0); + + /* Test DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE. */ + strcpy(pathBuffer, wildcard); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, (LPARAM)pathBuffer); + ok (res > 0, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, w*.c,) failed - 0x%08x\n", GetLastError()); + + /* There should be no plain files on the listbox */ + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_justDrives + itemCount_allDirs, + "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE) filled with %d entries, expected %d\n", + itemCount, itemCount_justDrives + itemCount_allDirs); + ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, w*.c) returned incorrect index!\n"); + + for (i = 0; i < itemCount; i++) + { + memset(pathBuffer, 0, MAX_PATH); + driveletter = '\0'; + SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer); + if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1) + ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter); + else + ok( pathBuffer[0] == '[' && pathBuffer[strlen(pathBuffer)-1] == ']', + "Element %d (%s) does not fit expected [...]\n", i, pathBuffer); + } + + /* This tests behavior when no files match the wildcard */ + strcpy(pathBuffer, BAD_EXTENSION); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, (LPARAM)pathBuffer); + ok (res == itemCount_justDrives -1, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, %s) returned %d, expected %d\n", + BAD_EXTENSION, res, itemCount_justDrives -1); + + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount == res + 1, "SendMessage(LB_DIR) returned %d expected %d\n", itemCount, res + 1); + + /* Test DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE. */ + strcpy(pathBuffer, "w*.c"); + SendMessageA(hList, LB_RESETCONTENT, 0, 0); + res = SendMessageA(hList, LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, (LPARAM)pathBuffer); + ok (res >= 0, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, w*.c,) failed - 0x%08x\n", GetLastError()); + + /* There should be no plain files on the listbox, and no [..], since it does not fit w*.c */ + itemCount = SendMessageA(hList, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_justDrives, + "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE) filled with %d entries, expected %d\n", + itemCount, itemCount_justDrives); + ok(res + 1 == itemCount, "SendMessage(LB_DIR, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE, w*.c) returned incorrect index!\n"); + + for (i = 0; i < itemCount; i++) + { + memset(pathBuffer, 0, MAX_PATH); + driveletter = '\0'; + SendMessageA(hList, LB_GETTEXT, i, (LPARAM)pathBuffer); + ok (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1, "Element %d (%s) does not fit [-X-]\n", i, pathBuffer); + ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter); + } + DestroyWindow(hList); + + DeleteFileA( "wtest1.tmp.c" ); +} + +static HWND g_listBox; +static HWND g_label; + +#define ID_TEST_LABEL 1001 +#define ID_TEST_LISTBOX 1002 + +static BOOL on_listbox_container_create(HWND hwnd, CREATESTRUCTA *lpcs) +{ + g_label = CreateWindowA("Static", "Contents of static control before DlgDirList.", + WS_CHILD | WS_VISIBLE, 10, 10, 512, 32, hwnd, (HMENU)ID_TEST_LABEL, NULL, 0); + if (!g_label) return FALSE; + + g_listBox = CreateWindowA("ListBox", "DlgDirList test", + WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER | WS_VSCROLL, 10, 60, 256, 256, + hwnd, (HMENU)ID_TEST_LISTBOX, NULL, 0); + if (!g_listBox) return FALSE; + + return TRUE; +} + +static LRESULT CALLBACK listbox_container_window_procA(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam) +{ + LRESULT result = 0; + + switch (uiMsg) + { + case WM_DESTROY: + PostQuitMessage(0); + break; + case WM_CREATE: + result = on_listbox_container_create(hwnd, (CREATESTRUCTA *)lParam) ? 0 : (LRESULT)-1; + break; + default: + result = DefWindowProcA(hwnd, uiMsg, wParam, lParam); + break; + } + return result; +} + +static BOOL RegisterListboxWindowClass(HINSTANCE hInst) +{ + WNDCLASSA cls; + + cls.style = 0; + cls.cbClsExtra = 0; + cls.cbWndExtra = 0; + cls.hInstance = hInst; + cls.hIcon = NULL; + cls.hCursor = LoadCursorA (NULL, (LPCSTR)IDC_ARROW); + cls.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + cls.lpszMenuName = NULL; + cls.lpfnWndProc = listbox_container_window_procA; + cls.lpszClassName = "ListboxContainerClass"; + if (!RegisterClassA (&cls)) return FALSE; + + return TRUE; +} + +static void test_listbox_dlgdir(void) +{ + HINSTANCE hInst; + HWND hWnd; + int res, itemCount; + int itemCount_allDirs; + int itemCount_justFiles; + int itemCount_justDrives; + int i; + char pathBuffer[MAX_PATH]; + char itemBuffer[MAX_PATH]; + char tempBuffer[MAX_PATH]; + char * p; + char driveletter; + HANDLE file; + + file = CreateFileA( "wtest1.tmp.c", GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL ); + ok(file != INVALID_HANDLE_VALUE, "Error creating the test file: %d\n", GetLastError()); + CloseHandle( file ); + + /* NOTE: for this test to succeed, there must be no subdirectories + under the current directory. In addition, there must be at least + one file that fits the wildcard w*.c . Normally, the test + directory itself satisfies both conditions. + */ + + hInst = GetModuleHandleA(0); + if (!RegisterListboxWindowClass(hInst)) assert(0); + + hWnd = CreateWindowA("ListboxContainerClass", "ListboxContainerClass", + WS_OVERLAPPEDWINDOW | WS_VISIBLE, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, hInst, 0); + ok(hWnd != NULL, "Failed to create container window.\n"); + + /* Test for standard usage */ + + /* The following should be overwritten by the directory path */ + SendMessageA(g_label, WM_SETTEXT, 0, (LPARAM)"default contents"); + + /* This should list all the w*.c files in the test directory + * As of this writing, this includes win.c, winstation.c, wsprintf.c + */ + strcpy(pathBuffer, "w*.c"); + res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, 0); + ok (res == 1, "DlgDirList(*.c, 0) returned %d - expected 1 - 0x%08x\n", res, GetLastError()); + + /* Path specification gets converted to uppercase */ + ok (!strcmp(pathBuffer, "W*.C"), + "expected conversion to uppercase, got %s\n", pathBuffer); + + /* Loaded path should have overwritten the label text */ + SendMessageA(g_label, WM_GETTEXT, MAX_PATH, (LPARAM)pathBuffer); + ok (strcmp("default contents", pathBuffer), "DlgDirList() did not modify static control!\n"); + + /* There should be some content in the listbox */ + itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0); + ok (itemCount > 0, "DlgDirList() did NOT fill the listbox!\n"); + itemCount_justFiles = itemCount; + + /* Every single item in the control should start with a w and end in .c */ + for (i = 0; i < itemCount; i++) + { + memset(pathBuffer, 0, MAX_PATH); + SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer); + p = pathBuffer + strlen(pathBuffer); + ok(((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') && + (*(p-1) == 'c' || *(p-1) == 'C') && + (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer); + } + + /* Test behavior when no files match the wildcard */ + strcpy(pathBuffer, BAD_EXTENSION); + res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, 0); + ok (res == 1, "DlgDirList(%s, 0) returned %d expected 1\n", BAD_EXTENSION, res); + + itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0); + ok (itemCount == 0, "DlgDirList() DID fill the listbox!\n"); + + /* Test DDL_DIRECTORY */ + strcpy(pathBuffer, "w*.c"); + res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY); + ok (res == 1, "DlgDirList(*.c, DDL_DIRECTORY) failed - 0x%08x\n", GetLastError()); + + /* There should be some content in the listbox. In particular, there should + * be exactly more elements than before, since the directories should + * have been added. + */ + itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0); + itemCount_allDirs = itemCount - itemCount_justFiles; + ok (itemCount >= itemCount_justFiles, "DlgDirList(DDL_DIRECTORY) filled with %d entries, expected > %d\n", + itemCount, itemCount_justFiles); + + /* Every single item in the control should start with a w and end in .c, + * except for the "[..]" string, which should appear exactly as it is. + */ + for (i = 0; i < itemCount; i++) + { + memset(pathBuffer, 0, MAX_PATH); + SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer); + p = pathBuffer + strlen(pathBuffer); + ok( (pathBuffer[0] == '[' && pathBuffer[strlen(pathBuffer)-1] == ']') || + ((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') && + (*(p-1) == 'c' || *(p-1) == 'C') && + (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer); + } + + /* Test behavior when no files match the wildcard */ + strcpy(pathBuffer, BAD_EXTENSION); + res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY); + ok (res == 1, "DlgDirList(%s, DDL_DIRECTORY) returned %d expected 1\n", BAD_EXTENSION, res); + + itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_allDirs, "DlgDirList() incorrectly filled the listbox! (expected %d got %d)\n", + itemCount_allDirs, itemCount); + for (i = 0; i < itemCount; i++) + { + memset(pathBuffer, 0, MAX_PATH); + SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer); + ok( pathBuffer[0] == '[' && pathBuffer[strlen(pathBuffer)-1] == ']', + "Element %d (%s) does not fit requested [...]\n", i, pathBuffer); + } + + /* Test DDL_DRIVES. At least on WinXP-SP2, this implies DDL_EXCLUSIVE */ + strcpy(pathBuffer, "w*.c"); + res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DRIVES); + ok (res == 1, "DlgDirList(*.c, DDL_DRIVES) failed - 0x%08x\n", GetLastError()); + + /* There should be some content in the listbox. In particular, there should + * be at least one element before, since the string "[-c-]" should + * have been added. Depending on the user setting, more drives might have + * been added. + */ + itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0); + ok (itemCount >= 1, + "DlgDirList(DDL_DRIVES) filled with %d entries, expected at least %d\n", + itemCount, 1); + itemCount_justDrives = itemCount; + + /* Every single item in the control should fit the format [-c-] */ + for (i = 0; i < itemCount; i++) + { + memset(pathBuffer, 0, MAX_PATH); + driveletter = '\0'; + SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer); + ok( strlen(pathBuffer) == 5, "Length of drive string is not 5\n" ); + ok( sscanf(pathBuffer, "[-%c-]", &driveletter) == 1, "Element %d (%s) does not fit [-X-]\n", i, pathBuffer); + ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter); + if (!(driveletter >= 'a' && driveletter <= 'z')) { + /* Correct after invalid entry is found */ + trace("removing count of invalid entry %s\n", pathBuffer); + itemCount_justDrives--; + } + } + + /* Test behavior when no files match the wildcard */ + strcpy(pathBuffer, BAD_EXTENSION); + res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DRIVES); + ok (res == 1, "DlgDirList(%s, DDL_DRIVES) returned %d expected 1\n", BAD_EXTENSION, res); + + itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_justDrives, "DlgDirList() incorrectly filled the listbox!\n"); + + /* Test DDL_DIRECTORY|DDL_DRIVES. This does *not* imply DDL_EXCLUSIVE */ + strcpy(pathBuffer, "w*.c"); + res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_DRIVES); + ok (res == 1, "DlgDirList(*.c, DDL_DIRECTORY|DDL_DRIVES) failed - 0x%08x\n", GetLastError()); + + /* There should be some content in the listbox. In particular, there should + * be exactly the number of plain files, plus the number of mapped drives, + * plus one "[..]" + */ + itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_justFiles + itemCount_justDrives + itemCount_allDirs, + "DlgDirList(DDL_DIRECTORY|DDL_DRIVES) filled with %d entries, expected %d\n", + itemCount, itemCount_justFiles + itemCount_justDrives + itemCount_allDirs); + + /* Every single item in the control should start with a w and end in .c, + * except for the "[..]" string, which should appear exactly as it is, + * and the mapped drives in the format "[-X-]". + */ + for (i = 0; i < itemCount; i++) + { + memset(pathBuffer, 0, MAX_PATH); + driveletter = '\0'; + SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer); + p = pathBuffer + strlen(pathBuffer); + if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1) + ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter); + else + ok( (pathBuffer[0] == '[' && pathBuffer[strlen(pathBuffer)-1] == ']') || + ((pathBuffer[0] == 'w' || pathBuffer[0] == 'W') && + (*(p-1) == 'c' || *(p-1) == 'C') && + (*(p-2) == '.')), "Element %d (%s) does not fit requested w*.c\n", i, pathBuffer); + } + + /* Test behavior when no files match the wildcard */ + strcpy(pathBuffer, BAD_EXTENSION); + res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_DRIVES); + ok (res == 1, "DlgDirList(%s, DDL_DIRECTORY|DDL_DRIVES) returned %d expected 1\n", BAD_EXTENSION, res); + + itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_justDrives + itemCount_allDirs, + "DlgDirList() incorrectly filled the listbox! (expected %d got %d)\n", + itemCount_justDrives + itemCount_allDirs, itemCount); + + /* Test DDL_DIRECTORY|DDL_EXCLUSIVE. */ + strcpy(pathBuffer, "w*.c"); + res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_EXCLUSIVE); + ok (res == 1, "DlgDirList(*.c, DDL_DIRECTORY|DDL_EXCLUSIVE) failed - 0x%08x\n", GetLastError()); + + /* There should be exactly one element: "[..]" */ + itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_allDirs, + "DlgDirList(DDL_DIRECTORY|DDL_EXCLUSIVE) filled with %d entries, expected %d\n", + itemCount, itemCount_allDirs); + + if (itemCount && GetCurrentDirectoryA( MAX_PATH, pathBuffer ) > 3) /* there's no [..] in drive root */ + { + memset(pathBuffer, 0, MAX_PATH); + SendMessageA(g_listBox, LB_GETTEXT, 0, (LPARAM)pathBuffer); + ok( !strcmp(pathBuffer, "[..]"), "First (and only) element is not [..]\n"); + } + + /* Test behavior when no files match the wildcard */ + strcpy(pathBuffer, BAD_EXTENSION); + res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_EXCLUSIVE); + ok (res == 1, "DlgDirList(%s, DDL_DIRECTORY|DDL_EXCLUSIVE) returned %d expected 1\n", BAD_EXTENSION, res); + + itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_allDirs, "DlgDirList() incorrectly filled the listbox!\n"); + + /* Test DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE. */ + strcpy(pathBuffer, "w*.c"); + res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE); + ok (res == 1, "DlgDirList(*.c, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE) failed - 0x%08x\n", GetLastError()); + + /* There should be no plain files on the listbox */ + itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_justDrives + itemCount_allDirs, + "DlgDirList(DDL_DIRECTORY|DDL_EXCLUSIVE) filled with %d entries, expected %d\n", + itemCount, itemCount_justDrives + itemCount_allDirs); + + for (i = 0; i < itemCount; i++) + { + memset(pathBuffer, 0, MAX_PATH); + driveletter = '\0'; + SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)pathBuffer); + if (sscanf(pathBuffer, "[-%c-]", &driveletter) == 1) + ok( driveletter >= 'a' && driveletter <= 'z', "Drive letter not in range a..z, got ascii %d\n", driveletter); + else + ok( pathBuffer[0] == '[' && pathBuffer[strlen(pathBuffer)-1] == ']', + "Element %d (%s) does not fit expected [...]\n", i, pathBuffer); + } + + /* Test behavior when no files match the wildcard */ + strcpy(pathBuffer, BAD_EXTENSION); + res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE); + ok (res == 1, "DlgDirList(%s, DDL_DIRECTORY|DDL_DRIVES|DDL_EXCLUSIVE) returned %d expected 1\n", BAD_EXTENSION, res); + + itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0); + ok (itemCount == itemCount_justDrives + itemCount_allDirs, "DlgDirList() incorrectly filled the listbox!\n"); + + /* Now test DlgDirSelectEx() in normal operation */ + /* Fill with everything - drives, directory and all plain files. */ + strcpy(pathBuffer, "*"); + res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, ID_TEST_LABEL, DDL_DIRECTORY|DDL_DRIVES); + ok (res != 0, "DlgDirList(*, DDL_DIRECTORY|DDL_DRIVES) failed - 0x%08x\n", GetLastError()); + + SendMessageA(g_listBox, LB_SETCURSEL, -1, 0); /* Unselect any current selection */ + memset(pathBuffer, 0, MAX_PATH); + SetLastError(0xdeadbeef); + res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX); + ok (GetLastError() == 0xdeadbeef, + "DlgDirSelectEx() with no selection modified last error code from 0xdeadbeef to 0x%08x\n", + GetLastError()); + ok (res == 0, "DlgDirSelectEx() with no selection returned %d, expected 0\n", res); + /* WinXP-SP2 leaves pathBuffer untouched, but Win98 fills it with garbage. */ + /* + ok (strlen(pathBuffer) == 0, "DlgDirSelectEx() with no selection filled buffer with %s\n", pathBuffer); + */ + /* Test proper drive/dir/file recognition */ + itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0); + for (i = 0; i < itemCount; i++) + { + memset(itemBuffer, 0, MAX_PATH); + memset(pathBuffer, 0, MAX_PATH); + memset(tempBuffer, 0, MAX_PATH); + driveletter = '\0'; + SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)itemBuffer); + res = SendMessageA(g_listBox, LB_SETCURSEL, i, 0); + ok (res == i, "SendMessageA(LB_SETCURSEL, %d) failed\n", i); + if (sscanf(itemBuffer, "[-%c-]", &driveletter) == 1) + { + /* Current item is a drive letter */ + SetLastError(0xdeadbeef); + res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX); + ok (GetLastError() == 0xdeadbeef, + "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n", + i, GetLastError()); + ok(res == 1, "DlgDirSelectEx() thinks %s (%s) is not a drive/directory!\n", itemBuffer, pathBuffer); + + /* For drive letters, DlgDirSelectEx tacks on a colon */ + ok (pathBuffer[0] == driveletter && pathBuffer[1] == ':' && pathBuffer[2] == '\0', + "%d: got \"%s\" expected \"%c:\"\n", i, pathBuffer, driveletter); + } + else if (itemBuffer[0] == '[') + { + /* Current item is the parent directory */ + SetLastError(0xdeadbeef); + res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX); + ok (GetLastError() == 0xdeadbeef, + "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n", + i, GetLastError()); + ok(res == 1, "DlgDirSelectEx() thinks %s (%s) is not a drive/directory!\n", itemBuffer, pathBuffer); + + /* For directories, DlgDirSelectEx tacks on a backslash */ + p = pathBuffer + strlen(pathBuffer); + ok (*(p-1) == '\\', "DlgDirSelectEx did NOT tack on a backslash to dir, got %s\n", pathBuffer); + + tempBuffer[0] = '['; + lstrcpynA(tempBuffer + 1, pathBuffer, strlen(pathBuffer)); + strcat(tempBuffer, "]"); + ok (!strcmp(tempBuffer, itemBuffer), "Formatted directory should be %s, got %s\n", tempBuffer, itemBuffer); + } + else + { + /* Current item is a plain file */ + SetLastError(0xdeadbeef); + res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX); + ok (GetLastError() == 0xdeadbeef, + "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n", + i, GetLastError()); + ok(res == 0, "DlgDirSelectEx() thinks %s (%s) is a drive/directory!\n", itemBuffer, pathBuffer); + + /* NOTE: WinXP tacks a period on all files that lack an extension. This affects + * for example, "Makefile", which gets reported as "Makefile." + */ + strcpy(tempBuffer, itemBuffer); + if (strchr(tempBuffer, '.') == NULL) strcat(tempBuffer, "."); + ok (!strcmp(pathBuffer, tempBuffer), "Formatted file should be %s, got %s\n", tempBuffer, pathBuffer); + } + } + + DeleteFileA( "wtest1.tmp.c" ); + + /* Now test DlgDirSelectEx() in abnormal operation */ + /* Fill list with bogus entries, that look somewhat valid */ + SendMessageA(g_listBox, LB_RESETCONTENT, 0, 0); + SendMessageA(g_listBox, LB_ADDSTRING, 0, (LPARAM)"[notexist.dir]"); + SendMessageA(g_listBox, LB_ADDSTRING, 0, (LPARAM)"notexist.fil"); + itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0); + for (i = 0; i < itemCount; i++) + { + memset(itemBuffer, 0, MAX_PATH); + memset(pathBuffer, 0, MAX_PATH); + memset(tempBuffer, 0, MAX_PATH); + driveletter = '\0'; + SendMessageA(g_listBox, LB_GETTEXT, i, (LPARAM)itemBuffer); + res = SendMessageA(g_listBox, LB_SETCURSEL, i, 0); + ok (res == i, "SendMessage(LB_SETCURSEL, %d) failed\n", i); + if (sscanf(itemBuffer, "[-%c-]", &driveletter) == 1) + { + /* Current item is a drive letter */ + SetLastError(0xdeadbeef); + res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX); + ok (GetLastError() == 0xdeadbeef, + "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n", + i, GetLastError()); + ok(res == 1, "DlgDirSelectEx() thinks %s (%s) is not a drive/directory!\n", itemBuffer, pathBuffer); + + /* For drive letters, DlgDirSelectEx tacks on a colon */ + ok (pathBuffer[0] == driveletter && pathBuffer[1] == ':' && pathBuffer[2] == '\0', + "%d: got \"%s\" expected \"%c:\"\n", i, pathBuffer, driveletter); + } + else if (itemBuffer[0] == '[') + { + /* Current item is the parent directory */ + SetLastError(0xdeadbeef); + res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX); + ok (GetLastError() == 0xdeadbeef, + "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n", + i, GetLastError()); + ok(res == 1, "DlgDirSelectEx() thinks %s (%s) is not a drive/directory!\n", itemBuffer, pathBuffer); + + /* For directories, DlgDirSelectEx tacks on a backslash */ + p = pathBuffer + strlen(pathBuffer); + ok (*(p-1) == '\\', "DlgDirSelectEx did NOT tack on a backslash to dir, got %s\n", pathBuffer); + + tempBuffer[0] = '['; + lstrcpynA(tempBuffer + 1, pathBuffer, strlen(pathBuffer)); + strcat(tempBuffer, "]"); + ok (!strcmp(tempBuffer, itemBuffer), "Formatted directory should be %s, got %s\n", tempBuffer, itemBuffer); + } + else + { + /* Current item is a plain file */ + SetLastError(0xdeadbeef); + res = DlgDirSelectExA(hWnd, pathBuffer, MAX_PATH, ID_TEST_LISTBOX); + ok (GetLastError() == 0xdeadbeef, + "DlgDirSelectEx() with selection at %d modified last error code from 0xdeadbeef to 0x%08x\n", + i, GetLastError()); + ok(res == 0, "DlgDirSelectEx() thinks %s (%s) is a drive/directory!\n", itemBuffer, pathBuffer); + + /* NOTE: WinXP and Win98 tack a period on all files that lack an extension. + * This affects for example, "Makefile", which gets reported as "Makefile." + */ + strcpy(tempBuffer, itemBuffer); + if (strchr(tempBuffer, '.') == NULL) strcat(tempBuffer, "."); + ok (!strcmp(pathBuffer, tempBuffer), "Formatted file should be %s, got %s\n", tempBuffer, pathBuffer); + } + } + + /* Test behavior when loading folders from root with and without wildcard */ + strcpy(pathBuffer, "C:\\"); + res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, 0, DDL_DIRECTORY | DDL_EXCLUSIVE); + ok(res, "DlgDirList failed to list C:\\ folders\n"); + todo_wine ok(!strcmp(pathBuffer, "*"), "DlgDirList set the invalid path spec '%s', expected '*'\n", pathBuffer); + + strcpy(pathBuffer, "C:\\*"); + res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, 0, DDL_DIRECTORY | DDL_EXCLUSIVE); + ok(res, "DlgDirList failed to list C:\\* folders\n"); + ok(!strcmp(pathBuffer, "*"), "DlgDirList set the invalid path spec '%s', expected '*'\n", pathBuffer); + + /* Try loading files from an invalid folder */ + SetLastError(0xdeadbeef); + strcpy(pathBuffer, "C:\\INVALID$$DIR"); + res = DlgDirListA(hWnd, pathBuffer, ID_TEST_LISTBOX, 0, DDL_DIRECTORY | DDL_EXCLUSIVE); + todo_wine ok(!res, "DlgDirList should have failed with 0 but %d was returned\n", res); + todo_wine ok(GetLastError() == ERROR_NO_WILDCARD_CHARACTERS, + "GetLastError should return 0x589, got 0x%X\n",GetLastError()); + + DestroyWindow(hWnd); +} + +static void test_set_count( void ) +{ + HWND parent, listbox; + LONG ret; + RECT r; + + parent = create_parent(); + listbox = create_listbox( LBS_OWNERDRAWFIXED | LBS_NODATA | WS_CHILD | WS_VISIBLE, parent ); + + UpdateWindow( listbox ); + GetUpdateRect( listbox, &r, TRUE ); + ok( IsRectEmpty( &r ), "got non-empty rect\n"); + + ret = SendMessageA( listbox, LB_SETCOUNT, 100, 0 ); + ok( ret == 0, "got %d\n", ret ); + ret = SendMessageA( listbox, LB_GETCOUNT, 0, 0 ); + ok( ret == 100, "got %d\n", ret ); + + GetUpdateRect( listbox, &r, TRUE ); + ok( !IsRectEmpty( &r ), "got empty rect\n"); + + ValidateRect( listbox, NULL ); + GetUpdateRect( listbox, &r, TRUE ); + ok( IsRectEmpty( &r ), "got non-empty rect\n"); + + ret = SendMessageA( listbox, LB_SETCOUNT, 99, 0 ); + ok( ret == 0, "got %d\n", ret ); + + GetUpdateRect( listbox, &r, TRUE ); + ok( !IsRectEmpty( &r ), "got empty rect\n"); + + DestroyWindow( listbox ); + DestroyWindow( parent ); +} + +static int lb_getlistboxinfo; + +static LRESULT WINAPI listbox_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA); + + if (message == LB_GETLISTBOXINFO) + lb_getlistboxinfo++; + + return CallWindowProcA(oldproc, hwnd, message, wParam, lParam); +} + +static void test_GetListBoxInfo(void) +{ + HWND listbox, parent; + WNDPROC oldproc; + DWORD ret; + + parent = create_parent(); + listbox = create_listbox(WS_CHILD | WS_VISIBLE, parent); + + oldproc = (WNDPROC)SetWindowLongPtrA(listbox, GWLP_WNDPROC, (LONG_PTR)listbox_subclass_proc); + SetWindowLongPtrA(listbox, GWLP_USERDATA, (LONG_PTR)oldproc); + + lb_getlistboxinfo = 0; + ret = GetListBoxInfo(listbox); + ok(ret > 0, "got %d\n", ret); + ok(lb_getlistboxinfo == 1, "got %d\n", lb_getlistboxinfo); + + DestroyWindow(listbox); + DestroyWindow(parent); +} + +static void test_missing_lbuttonup(void) +{ + HWND listbox, parent, capture; + + parent = create_parent(); + listbox = create_listbox(WS_CHILD | WS_VISIBLE, parent); + + /* Send button down without a corresponding button up */ + SendMessageA(listbox, WM_LBUTTONDOWN, 0, MAKELPARAM(10, 10)); + capture = GetCapture(); + ok(capture == listbox, "got %p expected %p\n", capture, listbox); + + /* Capture is released and LBN_SELCHANGE sent during WM_KILLFOCUS */ + got_selchange = 0; + SetFocus(NULL); + capture = GetCapture(); + ok(capture == NULL, "got %p\n", capture); + ok(got_selchange, "got %d\n", got_selchange); + + DestroyWindow(listbox); + DestroyWindow(parent); +} + +static void test_extents(void) +{ + HWND listbox, parent; + SCROLLINFO sinfo; + DWORD res; + BOOL br; + + parent = create_parent(); + + listbox = create_listbox(WS_CHILD | WS_VISIBLE, parent); + + res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0); + ok(res == 0, "Got wrong initial horizontal extent: %u\n", res); + + sinfo.cbSize = sizeof(sinfo); + sinfo.fMask = SIF_RANGE; + br = GetScrollInfo(listbox, SB_HORZ, &sinfo); + ok(br == TRUE, "GetScrollInfo failed\n"); + ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin); + ok(sinfo.nMax == 100, "got wrong max: %u\n", sinfo.nMax); + ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0, + "List box should not have a horizontal scroll bar\n"); + + /* horizontal extent < width */ + SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 64, 0); + + res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0); + ok(res == 64, "Got wrong horizontal extent: %u\n", res); + + sinfo.cbSize = sizeof(sinfo); + sinfo.fMask = SIF_RANGE; + br = GetScrollInfo(listbox, SB_HORZ, &sinfo); + ok(br == TRUE, "GetScrollInfo failed\n"); + ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin); + ok(sinfo.nMax == 100, "got wrong max: %u\n", sinfo.nMax); + ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0, + "List box should not have a horizontal scroll bar\n"); + + /* horizontal extent > width */ + SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 184, 0); + + res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0); + ok(res == 184, "Got wrong horizontal extent: %u\n", res); + + sinfo.cbSize = sizeof(sinfo); + sinfo.fMask = SIF_RANGE; + br = GetScrollInfo(listbox, SB_HORZ, &sinfo); + ok(br == TRUE, "GetScrollInfo failed\n"); + ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin); + ok(sinfo.nMax == 100, "got wrong max: %u\n", sinfo.nMax); + ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0, + "List box should not have a horizontal scroll bar\n"); + + DestroyWindow(listbox); + + listbox = create_listbox(WS_CHILD | WS_VISIBLE | WS_HSCROLL, parent); + + res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0); + ok(res == 0, "Got wrong initial horizontal extent: %u\n", res); + + sinfo.cbSize = sizeof(sinfo); + sinfo.fMask = SIF_RANGE; + br = GetScrollInfo(listbox, SB_HORZ, &sinfo); + ok(br == TRUE, "GetScrollInfo failed\n"); + ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin); + ok(sinfo.nMax == 100, "got wrong max: %u\n", sinfo.nMax); + ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0, + "List box should not have a horizontal scroll bar\n"); + + /* horizontal extent < width */ + SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 64, 0); + + res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0); + ok(res == 64, "Got wrong horizontal extent: %u\n", res); + + sinfo.cbSize = sizeof(sinfo); + sinfo.fMask = SIF_RANGE; + br = GetScrollInfo(listbox, SB_HORZ, &sinfo); + ok(br == TRUE, "GetScrollInfo failed\n"); + ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin); + ok(sinfo.nMax == 63, "got wrong max: %u\n", sinfo.nMax); + ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0, + "List box should not have a horizontal scroll bar\n"); + + /* horizontal extent > width */ + SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 184, 0); + + res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0); + ok(res == 184, "Got wrong horizontal extent: %u\n", res); + + sinfo.cbSize = sizeof(sinfo); + sinfo.fMask = SIF_RANGE; + br = GetScrollInfo(listbox, SB_HORZ, &sinfo); + ok(br == TRUE, "GetScrollInfo failed\n"); + ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin); + ok(sinfo.nMax == 183, "got wrong max: %u\n", sinfo.nMax); + ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) != 0, + "List box should have a horizontal scroll bar\n"); + + SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 0, 0); + + res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0); + ok(res == 0, "Got wrong horizontal extent: %u\n", res); + + sinfo.cbSize = sizeof(sinfo); + sinfo.fMask = SIF_RANGE; + br = GetScrollInfo(listbox, SB_HORZ, &sinfo); + ok(br == TRUE, "GetScrollInfo failed\n"); + ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin); + ok(sinfo.nMax == 0, "got wrong max: %u\n", sinfo.nMax); + ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) == 0, + "List box should not have a horizontal scroll bar\n"); + + DestroyWindow(listbox); + + + listbox = create_listbox(WS_CHILD | WS_VISIBLE | WS_HSCROLL | LBS_DISABLENOSCROLL, parent); + + res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0); + ok(res == 0, "Got wrong initial horizontal extent: %u\n", res); + + sinfo.cbSize = sizeof(sinfo); + sinfo.fMask = SIF_RANGE; + br = GetScrollInfo(listbox, SB_HORZ, &sinfo); + ok(br == TRUE, "GetScrollInfo failed\n"); + ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin); + ok(sinfo.nMax == 0, "got wrong max: %u\n", sinfo.nMax); + ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) != 0, + "List box should have a horizontal scroll bar\n"); + + /* horizontal extent < width */ + SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 64, 0); + + res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0); + ok(res == 64, "Got wrong horizontal extent: %u\n", res); + + sinfo.cbSize = sizeof(sinfo); + sinfo.fMask = SIF_RANGE; + br = GetScrollInfo(listbox, SB_HORZ, &sinfo); + ok(br == TRUE, "GetScrollInfo failed\n"); + ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin); + ok(sinfo.nMax == 63, "got wrong max: %u\n", sinfo.nMax); + ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) != 0, + "List box should have a horizontal scroll bar\n"); + + /* horizontal extent > width */ + SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 184, 0); + + res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0); + ok(res == 184, "Got wrong horizontal extent: %u\n", res); + + sinfo.cbSize = sizeof(sinfo); + sinfo.fMask = SIF_RANGE; + br = GetScrollInfo(listbox, SB_HORZ, &sinfo); + ok(br == TRUE, "GetScrollInfo failed\n"); + ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin); + ok(sinfo.nMax == 183, "got wrong max: %u\n", sinfo.nMax); + ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) != 0, + "List box should have a horizontal scroll bar\n"); + + SendMessageA(listbox, LB_SETHORIZONTALEXTENT, 0, 0); + + res = SendMessageA(listbox, LB_GETHORIZONTALEXTENT, 0, 0); + ok(res == 0, "Got wrong horizontal extent: %u\n", res); + + sinfo.cbSize = sizeof(sinfo); + sinfo.fMask = SIF_RANGE; + br = GetScrollInfo(listbox, SB_HORZ, &sinfo); + ok(br == TRUE, "GetScrollInfo failed\n"); + ok(sinfo.nMin == 0, "got wrong min: %u\n", sinfo.nMin); + ok(sinfo.nMax == 0, "got wrong max: %u\n", sinfo.nMax); + ok((GetWindowLongA(listbox, GWL_STYLE) & WS_HSCROLL) != 0, + "List box should have a horizontal scroll bar\n"); + + DestroyWindow(listbox); + + DestroyWindow(parent); +} + +static void test_listbox(void) +{ + static const struct listbox_test SS = + /* {add_style} */ + {{0}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + { 1, 1, 1, LB_ERR}, {0,0,0,0}, + { 2, 2, 2, LB_ERR}, {0,0,0,0}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; + + /* {selected, anchor, caret, selcount}{TODO fields} */ + static const struct listbox_test SS_NS = + {{LBS_NOSEL}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + { 1, 1, 1, LB_ERR}, {0,0,0,0}, + { 2, 2, 2, LB_ERR}, {0,0,0,0}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; + + static const struct listbox_test MS = + {{LBS_MULTIPLESEL}, + { 0, LB_ERR, 0, 0}, {0,0,0,0}, + { 1, 1, 1, 1}, {0,0,0,0}, + { 2, 1, 2, 1}, {0,0,0,0}, + { 0, LB_ERR, 0, 2}, {0,0,0,0}}; + + static const struct listbox_test MS_NS = + {{LBS_MULTIPLESEL | LBS_NOSEL}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + { 1, 1, 1, LB_ERR}, {0,0,0,0}, + { 2, 2, 2, LB_ERR}, {0,0,0,0}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; + + static const struct listbox_test ES = + {{LBS_EXTENDEDSEL}, + { 0, LB_ERR, 0, 0}, {0,0,0,0}, + { 1, 1, 1, 1}, {0,0,0,0}, + { 2, 2, 2, 1}, {0,0,0,0}, + { 0, LB_ERR, 0, 2}, {0,0,0,0}}; + + static const struct listbox_test ES_NS = + {{LBS_EXTENDEDSEL | LBS_NOSEL}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + { 1, 1, 1, LB_ERR}, {0,0,0,0}, + { 2, 2, 2, LB_ERR}, {0,0,0,0}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; + + static const struct listbox_test EMS = + {{LBS_EXTENDEDSEL | LBS_MULTIPLESEL}, + { 0, LB_ERR, 0, 0}, {0,0,0,0}, + { 1, 1, 1, 1}, {0,0,0,0}, + { 2, 2, 2, 1}, {0,0,0,0}, + { 0, LB_ERR, 0, 2}, {0,0,0,0}}; + + static const struct listbox_test EMS_NS = + {{LBS_EXTENDEDSEL | LBS_MULTIPLESEL | LBS_NOSEL}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}, + { 1, 1, 1, LB_ERR}, {0,0,0,0}, + { 2, 2, 2, LB_ERR}, {0,0,0,0}, + {LB_ERR, LB_ERR, 0, LB_ERR}, {0,0,0,0}}; + + run_test(SS); + run_test(SS_NS); + run_test(MS); + run_test(MS_NS); + run_test(ES); + run_test(ES_NS); + run_test(EMS); + run_test(EMS_NS); +} + +START_TEST(listbox) +{ + ULONG_PTR ctx_cookie; + HANDLE hCtx; + + if (!load_v6_module(&ctx_cookie, &hCtx)) + return; + + test_listbox(); + test_item_height(); + test_ownerdraw(); + test_LB_SELITEMRANGE(); + test_LB_SETCURSEL(); + test_listbox_height(); + test_itemfrompoint(); + test_listbox_item_data(); + test_listbox_LB_DIR(); + test_listbox_dlgdir(); + test_set_count(); + test_GetListBoxInfo(); + test_missing_lbuttonup(); + test_extents(); + + unload_v6_module(ctx_cookie, hCtx); +} diff --git a/modules/rostests/winetests/comctl32/listview.c b/modules/rostests/winetests/comctl32/listview.c index 2210fb3a8ad..90fce7439f6 100644 --- a/modules/rostests/winetests/comctl32/listview.c +++ b/modules/rostests/winetests/comctl32/listview.c @@ -187,6 +187,12 @@ static const struct message empty_seq[] = { { 0 } }; +static const struct message parent_focus_change_ownerdata_seq[] = { + { WM_NOTIFY, sent|id, 0, 0, LVN_ITEMCHANGED }, + { WM_NOTIFY, sent|id, 0, 0, LVN_GETDISPINFOA }, + { 0 } +}; + static const struct message forward_erasebkgnd_parent_seq[] = { { WM_ERASEBKGND, sent }, { 0 } @@ -628,14 +634,6 @@ static LRESULT WINAPI listview_subclass_proc(HWND hwnd, UINT message, WPARAM wPa LRESULT ret; struct message msg; - /* some debug output for style changing */ - if ((message == WM_STYLECHANGING || - message == WM_STYLECHANGED) && lParam) - { - STYLESTRUCT *style = (STYLESTRUCT*)lParam; - trace("\told style: 0x%08x, new style: 0x%08x\n", style->styleOld, style->styleNew); - } - msg.message = message; msg.flags = sent|wparam|lparam; if (defwndproc_counter) msg.flags |= defwinproc; @@ -1517,6 +1515,8 @@ static LRESULT CALLBACK create_test_wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, static void test_create(void) { + static const WCHAR testtextW[] = {'t','e','s','t',' ','t','e','x','t',0}; + char buff[16]; HWND hList; HWND hHeader; LONG_PTR ret; @@ -1537,15 +1537,6 @@ static void test_create(void) hList = CreateWindowA("MyListView32", "Test", WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, GetModuleHandleA(NULL), 0); ok((HIMAGELIST)SendMessageA(hList, LVM_GETIMAGELIST, 0, 0) == test_create_imagelist, "Image list not obtained\n"); hHeader = (HWND)SendMessageA(hList, LVM_GETHEADER, 0, 0); - - if (!IsWindow(hHeader)) - { - /* version 4.0 */ - win_skip("LVM_GETHEADER not implemented. Skipping.\n"); - DestroyWindow(hList); - return; - } - ok(IsWindow(hHeader) && IsWindowVisible(hHeader), "Listview not in report mode\n"); ok(hHeader == GetDlgItem(hList, 0), "Expected header as dialog item\n"); DestroyWindow(hList); @@ -1728,6 +1719,23 @@ static void test_create(void) ok_sequence(sequences, PARENT_SEQ_INDEX, create_ownerdrawfixed_parent_seq, "created with LVS_OWNERDRAWFIXED|LVS_REPORT - parent seq", FALSE); DestroyWindow(hList); + + /* Test that window text is preserved. */ + hList = CreateWindowExA(0, WC_LISTVIEWA, "test text", WS_CHILD | WS_BORDER | WS_VISIBLE, + 0, 0, 100, 100, hwndparent, NULL, GetModuleHandleA(NULL), NULL); + ok(hList != NULL, "Failed to create ListView window.\n"); + *buff = 0; + GetWindowTextA(hList, buff, sizeof(buff)); + ok(!strcmp(buff, "test text"), "Unexpected window text %s.\n", buff); + DestroyWindow(hList); + + hList = CreateWindowExW(0, WC_LISTVIEWW, testtextW, WS_CHILD | WS_BORDER | WS_VISIBLE, + 0, 0, 100, 100, hwndparent, NULL, GetModuleHandleA(NULL), NULL); + ok(hList != NULL, "Failed to create ListView window.\n"); + *buff = 0; + GetWindowTextA(hList, buff, sizeof(buff)); + ok(!strcmp(buff, "test text"), "Unexpected window text %s.\n", buff); + DestroyWindow(hList); } static void test_redraw(void) @@ -1826,16 +1834,12 @@ static LRESULT WINAPI cd_wndproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM clr = GetBkColor(nmlvcd->nmcd.hdc); ok(nmlvcd->clrTextBk == CLR_DEFAULT, "got 0x%x\n", nmlvcd->clrTextBk); ok(nmlvcd->clrText == RGB(0, 255, 0), "got 0x%x\n", nmlvcd->clrText); - if (!(GetWindowLongW(nmhdr->hwndFrom, GWL_STYLE) & LVS_SHOWSELALWAYS)) - { - todo_wine_if(nmlvcd->iSubItem) - ok(clr == c0ffee, "clr=%.8x\n", clr); - } + todo_wine_if(nmlvcd->iSubItem) + ok(clr == c0ffee, "clr=%.8x\n", clr); return CDRF_NOTIFYPOSTPAINT; case CDDS_ITEMPOSTPAINT | CDDS_SUBITEM: clr = GetBkColor(nmlvcd->nmcd.hdc); - if (!(GetWindowLongW(nmhdr->hwndFrom, GWL_STYLE) & LVS_SHOWSELALWAYS)) - todo_wine ok(clr == c0ffee, "clr=%.8x\n", clr); + todo_wine ok(clr == c0ffee, "clr=%.8x\n", clr); ok(nmlvcd->clrTextBk == CLR_DEFAULT, "got 0x%x\n", nmlvcd->clrTextBk); ok(nmlvcd->clrText == RGB(0, 255, 0), "got 0x%x\n", nmlvcd->clrText); return CDRF_DODEFAULT; @@ -1851,7 +1855,6 @@ static void test_customdraw(void) { HWND hwnd; WNDPROC oldwndproc; - LVITEMA item; hwnd = create_listview_control(LVS_REPORT); @@ -1871,18 +1874,6 @@ static void test_customdraw(void) UpdateWindow(hwnd); ok_sequence(sequences, PARENT_CD_SEQ_INDEX, parent_report_cd_seq, "parent customdraw, LVS_REPORT", FALSE); - /* check colors when item is selected */ - SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) | LVS_SHOWSELALWAYS); - item.mask = LVIF_STATE; - item.stateMask = LVIS_SELECTED; - item.state = LVIS_SELECTED; - SendMessageA(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item); - - flush_sequences(sequences, NUM_MSG_SEQUENCES); - InvalidateRect(hwnd, NULL, TRUE); - UpdateWindow(hwnd); - ok_sequence(sequences, PARENT_CD_SEQ_INDEX, parent_report_cd_seq, "parent customdraw, LVS_REPORT, selection", FALSE); - DestroyWindow(hwnd); hwnd = create_listview_control(LVS_LIST); @@ -1931,13 +1922,6 @@ static void test_icon_spacing(void) "Expected %d, got %d\n", MAKELONG(w, h), r); r = SendMessageA(hwnd, LVM_SETICONSPACING, 0, MAKELPARAM(25, 35)); - if (r == 0) - { - /* version 4.0 */ - win_skip("LVM_SETICONSPACING unimplemented. Skipping.\n"); - DestroyWindow(hwnd); - return; - } expect(MAKELONG(20,30), r); r = SendMessageA(hwnd, LVM_SETICONSPACING, 0, MAKELPARAM(-1,-1)); @@ -4386,19 +4370,11 @@ static void test_notifyformat(void) r = SendMessageA(hwnd, LVM_SETUNICODEFORMAT, 1, 0); expect(0, r); r = SendMessageA(hwnd, LVM_GETUNICODEFORMAT, 0, 0); - if (r == 1) - { - r = SendMessageA(hwnd, LVM_SETUNICODEFORMAT, 0, 0); - expect(1, r); - r = SendMessageA(hwnd, LVM_GETUNICODEFORMAT, 0, 0); - expect(0, r); - } - else - { - win_skip("LVM_GETUNICODEFORMAT is unsupported\n"); - DestroyWindow(hwnd); - return; - } + ok(r == 1, "Unexpected return value %d.\n", r); + r = SendMessageA(hwnd, LVM_SETUNICODEFORMAT, 0, 0); + expect(1, r); + r = SendMessageA(hwnd, LVM_GETUNICODEFORMAT, 0, 0); + expect(0, r); DestroyWindow(hwnd); @@ -4978,13 +4954,18 @@ static void test_LVS_EX_TRANSPARENTBKGND(void) static void test_approximate_viewrect(void) { - HWND hwnd; - DWORD ret; + static CHAR test[] = "abracadabra, a very long item label"; + DWORD item_width, item_height, header_height; + static CHAR column_header[] = "Header"; + unsigned const column_width = 100; + DWORD ret, item_count; HIMAGELIST himl; - HBITMAP hbmp; LVITEMA itema; - static CHAR test[] = "abracadabra, a very long item label"; + LVCOLUMNA col; + HBITMAP hbmp; + HWND hwnd; + /* LVS_ICON */ hwnd = create_listview_control(LVS_ICON); himl = ImageList_Create(40, 40, 0, 4, 4); ok(himl != NULL, "failed to create imagelist\n"); @@ -5003,12 +4984,7 @@ static void test_approximate_viewrect(void) expect(0, ret); ret = SendMessageA(hwnd, LVM_SETICONSPACING, 0, MAKELPARAM(75, 75)); - if (ret == 0) - { - /* version 4.0 */ - win_skip("LVM_SETICONSPACING unimplemented. Skipping.\n"); - return; - } + ok(ret != 0, "Unexpected return value %#x.\n", ret); ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 11, MAKELPARAM(100,100)); expect(MAKELONG(77,827), ret); @@ -5046,6 +5022,129 @@ static void test_approximate_viewrect(void) expect(MAKELONG(152,152), ret); DestroyWindow(hwnd); + + /* LVS_REPORT */ + hwnd = create_listview_control(LVS_REPORT); + + /* Empty control without columns */ + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 0, MAKELPARAM(100, 100)); +todo_wine + ok(LOWORD(ret) == 0, "Unexpected width %d.\n", LOWORD(ret)); + ok(HIWORD(ret) != 0, "Unexpected height %d.\n", HIWORD(ret)); + + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 0, 0); + ok(LOWORD(ret) == 0, "Unexpected width %d.\n", LOWORD(ret)); +todo_wine + ok(HIWORD(ret) != 0, "Unexpected height %d.\n", HIWORD(ret)); + + header_height = HIWORD(ret); + + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 1, 0); + ok(LOWORD(ret) == 0, "Unexpected width %d.\n", LOWORD(ret)); +todo_wine + ok(HIWORD(ret) > header_height, "Unexpected height %d.\n", HIWORD(ret)); + + item_height = HIWORD(ret) - header_height; + + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, -2, 0); + ok(LOWORD(ret) == 0, "Unexpected width %d.\n", LOWORD(ret)); + ok(HIWORD(ret) == (header_height - 2 * item_height), "Unexpected height %d.\n", HIWORD(ret)) ; + + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, -1, 0); + ok(LOWORD(ret) == 0, "Unexpected width %d.\n", LOWORD(ret)); + ok(HIWORD(ret) == header_height, "Unexpected height.\n"); + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 2, 0); + ok(LOWORD(ret) == 0, "Unexpected width %d.\n", LOWORD(ret)); + ok(HIWORD(ret) == header_height + 2 * item_height, "Unexpected height %d.\n", HIWORD(ret)); + + /* Insert column */ + col.mask = LVCF_TEXT | LVCF_WIDTH; + col.pszText = column_header; + col.cx = column_width; + ret = SendMessageA(hwnd, LVM_INSERTCOLUMNA, 0, (LPARAM)&col); + ok(ret == 0, "Unexpected return value %d.\n", ret); + + /* Empty control with column */ + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 0, 0); +todo_wine { + ok(LOWORD(ret) >= column_width, "Unexpected width %d.\n", LOWORD(ret)); + ok(HIWORD(ret) != 0, "Unexpected height %d.\n", HIWORD(ret)); +} + header_height = HIWORD(ret); + item_width = LOWORD(ret); + + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 1, 0); + ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret)); +todo_wine + ok(HIWORD(ret) > header_height, "Unexpected height %d.\n", HIWORD(ret)); + + item_height = HIWORD(ret) - header_height; + + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, -2, 0); + ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret)); + ok(HIWORD(ret) == header_height - 2 * item_height, "Unexpected height %d.\n", HIWORD(ret)); + + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, -1, 0); + ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret)); + ok(HIWORD(ret) == header_height, "Unexpected height %d.\n", HIWORD(ret)); + + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 2, 0); + ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret)); + ok(HIWORD(ret) == header_height + 2 * item_height, "Unexpected height %d.\n", HIWORD(ret)); + + for (item_count = 1; item_count <= 2; ++item_count) + { + itema.mask = LVIF_TEXT; + itema.iItem = 0; + itema.iSubItem = 0; + itema.pszText = test; + ret = SendMessageA(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&itema); + ok(ret == 0, "Unexpected return value %d.\n", ret); + + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 0, 0); + ok(LOWORD(ret) >= column_width, "Unexpected width %d.\n", LOWORD(ret)); + todo_wine + ok(HIWORD(ret) != 0, "Unexpected height %d.\n", HIWORD(ret)); + + header_height = HIWORD(ret); + item_width = LOWORD(ret); + + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 1, 0); + ok(LOWORD(ret) == item_width, "Unexpected width %d, item %d\n", LOWORD(ret), item_count - 1); + ok(HIWORD(ret) > header_height, "Unexpected height %d. item %d.\n", HIWORD(ret), item_count - 1); + + item_height = HIWORD(ret) - header_height; + + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, -2, 0); + ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret)); + todo_wine + ok(HIWORD(ret) == header_height - 2 * item_height, "Unexpected height %d.\n", HIWORD(ret)); + + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, -1, 0); + ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret)); + ok(HIWORD(ret) == header_height + item_count * item_height, "Unexpected height %d.\n", HIWORD(ret)); + + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 2, 0); + ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret)); + ok(HIWORD(ret) == header_height + 2 * item_height, "Unexpected height %d.\n", HIWORD(ret)); + + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, 2, MAKELONG(item_width * 2, header_height + 3 * item_height)); + ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret)); + ok(HIWORD(ret) == header_height + 2 * item_height, "Unexpected height %d.\n", HIWORD(ret)); + + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, -2, MAKELONG(item_width * 2, 0)); + ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret)); + todo_wine + ok(HIWORD(ret) == header_height - 2 * item_height, "Unexpected height %d.\n", HIWORD(ret)); + + ret = SendMessageA(hwnd, LVM_APPROXIMATEVIEWRECT, -2, MAKELONG(-1, -1)); + ok(LOWORD(ret) == item_width, "Unexpected width %d.\n", LOWORD(ret)); + todo_wine + ok(HIWORD(ret) == header_height - 2 * item_height, "Unexpected height %d.\n", HIWORD(ret)); + } + + DestroyWindow(hwnd); + } static void test_finditem(void) @@ -5102,12 +5201,6 @@ static void test_finditem(void) fi.flags = LVFI_SUBSTRING; fi.psz = f; r = SendMessageA(hwnd, LVM_FINDITEMA, -1, (LPARAM)&fi); - if (r == -1) - { - win_skip("LVFI_SUBSTRING not supported\n"); - DestroyWindow(hwnd); - return; - } expect(0, r); strcpy(f, "f"); fi.flags = LVFI_SUBSTRING; @@ -5895,6 +5988,7 @@ static void test_oneclickactivate(void) static void test_callback_mask(void) { + LVITEMA item; DWORD mask; HWND hwnd; BOOL ret; @@ -5911,6 +6005,209 @@ static void test_callback_mask(void) ok(mask == ~0u, "got 0x%08x\n", mask); DestroyWindow(hwnd); + + /* LVS_OWNERDATA, mask LVIS_FOCUSED */ + hwnd = create_listview_control(LVS_REPORT | LVS_OWNERDATA); + + mask = SendMessageA(hwnd, LVM_GETCALLBACKMASK, 0, 0); + ok(mask == 0, "Unexpected callback mask %#x.\n", mask); + + ret = SendMessageA(hwnd, LVM_SETCALLBACKMASK, LVIS_FOCUSED, 0); + ok(ret, "Failed to set callback mask, %d\n", ret); + + mask = SendMessageA(hwnd, LVM_GETCALLBACKMASK, 0, 0); + ok(mask == LVIS_FOCUSED, "Unexpected callback mask %#x.\n", mask); + + ret = SendMessageA(hwnd, LVM_SETITEMCOUNT, 1, 0); + ok(ret, "Failed to set item count.\n"); + + ret = SendMessageA(hwnd, LVM_GETSELECTIONMARK, 0, 0); + ok(ret == -1, "Unexpected selection mark, %d\n", ret); + + item.stateMask = LVIS_FOCUSED; + item.state = LVIS_FOCUSED; + ret = SendMessageA(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item); + ok(ret, "Failed to set item state.\n"); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + ret = SendMessageA(hwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED); +todo_wine + ok(ret == 0, "Unexpected focused item, ret %d\n", ret); + + ret = SendMessageA(hwnd, LVM_GETSELECTIONMARK, 0, 0); +todo_wine + ok(ret == 0, "Unexpected selection mark, %d\n", ret); + + ret = SendMessageA(hwnd, LVM_SETITEMCOUNT, 0, 0); + ok(ret, "Failed to set item count.\n"); + + ret = SendMessageA(hwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED); + ok(ret == -1, "Unexpected focused item, ret %d\n", ret); + + ret = SendMessageA(hwnd, LVM_GETSELECTIONMARK, 0, 0); + ok(ret == -1, "Unexpected selection mark, %d\n", ret); + + ret = SendMessageA(hwnd, LVM_SETITEMCOUNT, 1, 0); + ok(ret, "Failed to set item count.\n"); + + ret = SendMessageA(hwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED); + ok(ret == -1, "Unexpected focused item, ret %d\n", ret); + + ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "parent seq, owner data/focus 1", FALSE); + + /* LVS_OWNDERDATA, empty mask */ + ret = SendMessageA(hwnd, LVM_SETCALLBACKMASK, 0, 0); + ok(ret, "Failed to set callback mask, %d\n", ret); + + ret = SendMessageA(hwnd, LVM_SETITEMCOUNT, 1, 0); + ok(ret, "Failed to set item count.\n"); + + ret = SendMessageA(hwnd, LVM_GETSELECTIONMARK, 0, 0); + ok(ret == -1, "Unexpected selection mark, %d\n", ret); + + item.stateMask = LVIS_FOCUSED; + item.state = LVIS_FOCUSED; + ret = SendMessageA(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item); + ok(ret, "Failed to set item state.\n"); + + ret = SendMessageA(hwnd, LVM_GETSELECTIONMARK, 0, 0); + ok(ret == 0, "Unexpected selection mark, %d\n", ret); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + ret = SendMessageA(hwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED); + ok(ret == 0, "Unexpected focused item, ret %d\n", ret); + + ret = SendMessageA(hwnd, LVM_SETITEMCOUNT, 0, 0); + ok(ret, "Failed to set item count.\n"); + + ret = SendMessageA(hwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED); + ok(ret == -1, "Unexpected focused item, ret %d\n", ret); + + ret = SendMessageA(hwnd, LVM_GETSELECTIONMARK, 0, 0); +todo_wine + ok(ret == -1, "Unexpected selection mark, %d\n", ret); + + ret = SendMessageA(hwnd, LVM_SETITEMCOUNT, 1, 0); + ok(ret, "Failed to set item count.\n"); + + ret = SendMessageA(hwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED); + ok(ret == -1, "Unexpected focused item, ret %d\n", ret); + + ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "parent seq, owner data/focus 2", FALSE); + + /* 2 items, focus on index 0, reduce to 1 item. */ + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + ret = SendMessageA(hwnd, LVM_SETITEMCOUNT, 2, 0); + ok(ret, "Failed to set item count.\n"); + + ret = SendMessageA(hwnd, LVM_SETITEMSTATE, 0, (LPARAM)&item); + ok(ret, "Failed to set item state.\n"); + + ret = SendMessageA(hwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED); + ok(ret == 0, "Unexpected focused item, ret %d\n", ret); + + ret = SendMessageA(hwnd, LVM_SETITEMCOUNT, 1, 0); + ok(ret, "Failed to set item count.\n"); + + ret = SendMessageA(hwnd, LVM_GETNEXTITEM, -1, LVNI_FOCUSED); + ok(ret == 0, "Unexpected focused item, ret %d\n", ret); + + ok_sequence(sequences, PARENT_SEQ_INDEX, parent_focus_change_ownerdata_seq, + "parent seq, owner data/focus 3", TRUE); + + DestroyWindow(hwnd); +} + +static void test_state_image(void) +{ + static const DWORD styles[] = + { + LVS_ICON, + LVS_REPORT, + LVS_SMALLICON, + LVS_LIST, + }; + int i; + + for (i = 0; i < sizeof(styles)/sizeof(styles[0]); i++) + { + static char text[] = "Item"; + static char subtext[] = "Subitem"; + char buff[16]; + LVITEMA item; + HWND hwnd; + int r; + + hwnd = create_listview_control(styles[i]); + + insert_column(hwnd, 0); + insert_column(hwnd, 1); + + item.mask = LVIF_TEXT; + item.iItem = 0; + item.iSubItem = 0; + item.pszText = text; + r = SendMessageA(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&item); + ok(r == 0, "Failed to insert an item.\n"); + + item.mask = LVIF_STATE; + item.state = INDEXTOSTATEIMAGEMASK(1) | LVIS_SELECTED; + item.stateMask = LVIS_STATEIMAGEMASK | LVIS_SELECTED; + item.iItem = 0; + item.iSubItem = 0; + r = SendMessageA(hwnd, LVM_SETITEMA, 0, (LPARAM)&item); + ok(r, "Failed to set item state.\n"); + + item.mask = LVIF_TEXT; + item.iItem = 0; + item.iSubItem = 1; + item.pszText = subtext; + r = SendMessageA(hwnd, LVM_SETITEMA, 0, (LPARAM)&item); + ok(r, "Failed to set subitem text.\n"); + + item.mask = LVIF_STATE; + item.stateMask = LVIS_STATEIMAGEMASK | LVIS_SELECTED; + item.state = 0; + item.iItem = 0; + item.iSubItem = 0; + r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); + ok(r, "Failed to get item state.\n"); + ok(item.state == (INDEXTOSTATEIMAGEMASK(1) | LVIS_SELECTED), "Unexpected item state %#x.\n", item.state); + + item.mask = LVIF_STATE; + item.stateMask = LVIS_STATEIMAGEMASK | LVIS_SELECTED; + item.state = INDEXTOSTATEIMAGEMASK(2); + item.iItem = 0; + item.iSubItem = 1; + r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); + ok(r, "Failed to get subitem state.\n"); + todo_wine + ok(item.state == 0, "Unexpected state %#x.\n", item.state); + + item.mask = LVIF_STATE; + item.stateMask = LVIS_STATEIMAGEMASK | LVIS_SELECTED; + item.state = INDEXTOSTATEIMAGEMASK(2); + item.iItem = 0; + item.iSubItem = 2; + r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); + ok(r, "Failed to get subitem state.\n"); + todo_wine + ok(item.state == 0, "Unexpected state %#x.\n", item.state); + + item.mask = LVIF_TEXT; + item.iItem = 0; + item.iSubItem = 1; + item.pszText = buff; + item.cchTextMax = sizeof(buff); + r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); + ok(r, "Failed to get subitem text %d.\n", r); + ok(!strcmp(buff, subtext), "Unexpected subitem text %s.\n", buff); + + DestroyWindow(hwnd); + } } START_TEST(listview) @@ -5985,6 +6282,7 @@ START_TEST(listview) test_header_proc(); test_oneclickactivate(); test_callback_mask(); + test_state_image(); if (!load_v6_module(&ctx_cookie, &hCtx)) { @@ -6003,6 +6301,26 @@ START_TEST(listview) test_multiselect(); test_insertitem(); test_header_proc(); + test_images(); + test_checkboxes(); + test_items(); + test_color(); + test_columns(); + test_sorting(); + test_ownerdata(); + test_norecompute(); + test_nosortheader(); + test_columnscreation(); + test_indentation(); + test_finditem(); + test_hover(); + test_destroynotify(); + test_createdragimage(); + test_dispinfo(); + test_LVM_SETITEMTEXT(); + test_LVM_REDRAWITEMS(); + test_oneclickactivate(); + test_state_image(); unload_v6_module(ctx_cookie, hCtx); diff --git a/modules/rostests/winetests/comctl32/misc.c b/modules/rostests/winetests/comctl32/misc.c index 9fe3ae378e1..2ce35368594 100644 --- a/modules/rostests/winetests/comctl32/misc.c +++ b/modules/rostests/winetests/comctl32/misc.c @@ -189,7 +189,7 @@ static void test_Alloc(void) /* reallocate a NULL ptr */ p = pReAlloc(NULL, 2); - ok(p != NULL, "Expectd non-NULL ptr\n"); + ok(p != NULL, "Expected non-NULL ptr\n"); res = pFree(p); ok(res == TRUE, "Expected TRUE, got %d\n", res); diff --git a/modules/rostests/winetests/comctl32/msg.h b/modules/rostests/winetests/comctl32/msg.h index fe9969daa07..74b4477867a 100644 --- a/modules/rostests/winetests/comctl32/msg.h +++ b/modules/rostests/winetests/comctl32/msg.h @@ -163,11 +163,12 @@ static void dump_sequence( struct msg_sequence **seq, int sequence_index, } static inline void ok_sequence_(struct msg_sequence **seq, int sequence_index, - const struct message *expected, const char *context, BOOL todo, + const struct message *expected_list, const char *context, BOOL todo, const char *file, int line) { - struct msg_sequence *msg_seq = seq[sequence_index]; static const struct message end_of_sequence = {0, 0, 0, 0}; + struct msg_sequence *msg_seq = seq[sequence_index]; + const struct message *expected = expected_list; const struct message *actual, *sequence; int failcount = 0, dump = 0; @@ -377,7 +378,7 @@ static inline void ok_sequence_(struct msg_sequence **seq, int sequence_index, } done: - if (dump) dump_sequence( seq, sequence_index, expected, context, file, line ); + if (dump) dump_sequence( seq, sequence_index, expected_list, context, file, line ); flush_sequence(seq, sequence_index); } diff --git a/modules/rostests/winetests/comctl32/propsheet.c b/modules/rostests/winetests/comctl32/propsheet.c index fd9f997dcb9..35e942c6b57 100644 --- a/modules/rostests/winetests/comctl32/propsheet.c +++ b/modules/rostests/winetests/comctl32/propsheet.c @@ -25,11 +25,19 @@ static HWND parenthwnd; static HWND sheethwnd; +static BOOL rtl; static LONG active_page = -1; #define IDC_APPLY_BUTTON 12321 +static void detect_locale(void) +{ + DWORD reading_layout; + rtl = GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IREADINGLAYOUT | LOCALE_RETURN_NUMBER, + (void *)&reading_layout, sizeof(reading_layout)) && reading_layout == 1; +} + /* try to make sure pending X events have been processed before continuing */ static void flush_events(void) { @@ -492,19 +500,28 @@ static void test_buttons(void) button = GetDlgItem(hdlg, IDCANCEL); GetWindowRect(button, &rc); ok(rc.top == top, "Cancel button should have same top as OK button\n"); - ok(rc.left > prevRight, "Cancel button should be to the right of OK button\n"); + if (rtl) + ok(rc.left < prevRight, "Cancel button should be to the left of OK button\n"); + else + ok(rc.left > prevRight, "Cancel button should be to the right of OK button\n"); prevRight = rc.right; button = GetDlgItem(hdlg, IDC_APPLY_BUTTON); GetWindowRect(button, &rc); ok(rc.top == top, "Apply button should have same top as OK button\n"); - ok(rc.left > prevRight, "Apply button should be to the right of Cancel button\n"); + if (rtl) + ok(rc.left < prevRight, "Apply button should be to the left of Cancel button\n"); + else + ok(rc.left > prevRight, "Apply button should be to the right of Cancel button\n"); prevRight = rc.right; button = GetDlgItem(hdlg, IDHELP); GetWindowRect(button, &rc); ok(rc.top == top, "Help button should have same top as OK button\n"); - ok(rc.left > prevRight, "Help button should be to the right of Apply button\n"); + if (rtl) + ok(rc.left < prevRight, "Help button should be to the left of Apply button\n"); + else + ok(rc.left > prevRight, "Help button should be to the right of Apply button\n"); DestroyWindow(hdlg); } @@ -1120,6 +1137,15 @@ static void test_CreatePropertySheetPage(void) START_TEST(propsheet) { + detect_locale(); + if (rtl) + { + /* use locale-specific RTL resources when on an RTL locale */ + /* without this, propsheets on RTL locales use English LTR resources */ + trace("RTL locale detected\n"); + SetProcessDefaultLayout(LAYOUT_RTL); + } + test_title(); test_nopage(); test_disableowner(); diff --git a/modules/rostests/winetests/comctl32/rsrc.rc b/modules/rostests/winetests/comctl32/rsrc.rc index cd113491301..327aa225e17 100644 --- a/modules/rostests/winetests/comctl32/rsrc.rc +++ b/modules/rostests/winetests/comctl32/rsrc.rc @@ -18,7 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ -//#include "windef.h" +#include "windef.h" #include "winuser.h" #include "resources.h" @@ -101,3 +101,63 @@ FONT 8, "MS Shell Dlg" COMBOBOX IDC_PS_COMBO1, 16, 68, 140, 60, CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "Add", IDC_PS_PUSHBUTTON1, 164, 68, 40, 13 } + +MULTI_EDIT_DIALOG DIALOG 0, 0, 160, 75 +STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_CENTER +CAPTION "Multiple Edit Test" +FONT 8, "MS Shell Dlg" +{ + EDITTEXT 1000, 5, 5, 150, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_MULTILINE | ES_WANTRETURN + EDITTEXT 1001, 5, 25, 150, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + EDITTEXT 1002, 5, 45, 150, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP +} + +EDIT_DIALOG DIALOG 0, 0, 160, 80 +STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_CENTER +CAPTION "Edit Test" +FONT 8, "MS Shell Dlg" +{ + PUSHBUTTON "OK", IDOK, 20, 60, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + PUSHBUTTON "Cancel", IDCANCEL, 100, 60, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + EDITTEXT 1000, 5, 5, 150, 50, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_MULTILINE | WS_VSCROLL | ES_AUTOVSCROLL +} + +EDIT_READONLY_DIALOG DIALOG 0, 0, 160, 80 +STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_CENTER +CAPTION "Edit Readonly Test" +FONT 8, "MS Shell Dlg" +{ + PUSHBUTTON "OK", IDOK, 20, 60, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + PUSHBUTTON "Cancel", IDCANCEL, 100, 60, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + EDITTEXT 1000, 5, 5, 150, 50, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_MULTILINE | WS_VSCROLL | ES_AUTOVSCROLL | ES_READONLY +} + +EDIT_WANTRETURN_DIALOG DIALOG 0, 0, 160, 80 +STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_CENTER +CAPTION "Edit Test" +FONT 8, "MS Shell Dlg" +{ + PUSHBUTTON "OK", IDOK, 20, 60, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + PUSHBUTTON "Cancel", IDCANCEL, 100, 60, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + EDITTEXT 1000, 5, 5, 150, 50, WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_MULTILINE | WS_VSCROLL | ES_AUTOVSCROLL | ES_WANTRETURN +} + +EDIT_SINGLELINE_DIALOG DIALOG 0, 0, 160, 80 +STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_CENTER +CAPTION "Edit Test" +FONT 8, "MS Shell Dlg" +{ + PUSHBUTTON "OK", IDOK, 20, 60, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + PUSHBUTTON "Cancel", IDCANCEL, 100, 60, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + EDITTEXT 1000, 5, 5, 150, 50, WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | ES_AUTOVSCROLL +} + +EDIT_SINGLELINE_WANTRETURN_DIALOG DIALOG 0, 0, 160, 80 +STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_CENTER +CAPTION "Edit Test" +FONT 8, "MS Shell Dlg" +{ + PUSHBUTTON "OK", IDOK, 20, 60, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + PUSHBUTTON "Cancel", IDCANCEL, 100, 60, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP + EDITTEXT 1000, 5, 5, 150, 50, WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | ES_AUTOVSCROLL | ES_WANTRETURN +} diff --git a/modules/rostests/winetests/comctl32/static.c b/modules/rostests/winetests/comctl32/static.c new file mode 100644 index 00000000000..3bc25dc08d3 --- /dev/null +++ b/modules/rostests/winetests/comctl32/static.c @@ -0,0 +1,168 @@ +/* Unit test suite for static controls. + * + * Copyright 2007 Google (Mikolaj Zalewski) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "precomp.h" + +#define TODO_COUNT 1 + +#define CTRL_ID 1995 + +static HWND hMainWnd; +static int g_nReceivedColorStatic; + +/* try to make sure pending X events have been processed before continuing */ +static void flush_events(void) +{ + MSG msg; + int diff = 200; + int min_timeout = 100; + DWORD time = GetTickCount() + diff; + + while (diff > 0) + { + if (MsgWaitForMultipleObjects( 0, NULL, FALSE, min_timeout, QS_ALLINPUT ) == WAIT_TIMEOUT) break; + while (PeekMessageA( &msg, 0, 0, 0, PM_REMOVE )) DispatchMessageA( &msg ); + diff = time - GetTickCount(); + } +} + +static HWND create_static(DWORD style) +{ + return CreateWindowA("static", "Test", WS_VISIBLE|WS_CHILD|style, 5, 5, 100, 100, hMainWnd, (HMENU)CTRL_ID, NULL, 0); +} + +static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (msg) + { + case WM_CTLCOLORSTATIC: + { + HDC hdc = (HDC)wparam; + HRGN hrgn = CreateRectRgn(0, 0, 1, 1); + ok(GetClipRgn(hdc, hrgn) == 1, "Static controls during a WM_CTLCOLORSTATIC must have a clipping region\n"); + DeleteObject(hrgn); + g_nReceivedColorStatic++; + return (LRESULT) GetStockObject(BLACK_BRUSH); + } + break; + } + + return DefWindowProcA(hwnd, msg, wparam, lparam); +} + +static void test_updates(int style, int flags) +{ + HWND hStatic = create_static(style); + RECT r1 = {20, 20, 30, 30}; + int exp; + + flush_events(); + g_nReceivedColorStatic = 0; + /* during each update parent WndProc will test the WM_CTLCOLORSTATIC message */ + InvalidateRect(hMainWnd, NULL, FALSE); + UpdateWindow(hMainWnd); + InvalidateRect(hMainWnd, &r1, FALSE); + UpdateWindow(hMainWnd); + InvalidateRect(hStatic, &r1, FALSE); + UpdateWindow(hStatic); + InvalidateRect(hStatic, NULL, FALSE); + UpdateWindow(hStatic); + + if ((style & SS_TYPEMASK) == SS_BITMAP) + { + HDC hdc = GetDC(hStatic); + COLORREF colour = GetPixel(hdc, 10, 10); + todo_wine + ok(colour == 0, "Unexpected pixel color.\n"); + ReleaseDC(hStatic, hdc); + } + + if (style != SS_ETCHEDHORZ && style != SS_ETCHEDVERT) + exp = 4; + else + exp = 1; /* SS_ETCHED* seems to send WM_CTLCOLORSTATIC only sometimes */ + + if (flags & TODO_COUNT) + todo_wine + ok(g_nReceivedColorStatic == exp, "Unexpected WM_CTLCOLORSTATIC value %d\n", g_nReceivedColorStatic); + else if ((style & SS_TYPEMASK) == SS_ICON || (style & SS_TYPEMASK) == SS_BITMAP) + ok(g_nReceivedColorStatic == exp, "Unexpected %u got %u\n", exp, g_nReceivedColorStatic); + else + ok(g_nReceivedColorStatic == exp, "Unexpected WM_CTLCOLORSTATIC value %d\n", g_nReceivedColorStatic); + DestroyWindow(hStatic); +} + +static void test_set_text(void) +{ + HWND hStatic = create_static(SS_SIMPLE); + char buffA[10]; + + GetWindowTextA(hStatic, buffA, sizeof(buffA)); + ok(!strcmp(buffA, "Test"), "got wrong text %s\n", buffA); + + SetWindowTextA(hStatic, NULL); + GetWindowTextA(hStatic, buffA, sizeof(buffA)); + ok(buffA[0] == 0, "got wrong text %s\n", buffA); + + DestroyWindow(hStatic); +} + +START_TEST(static) +{ + static const char classname[] = "testclass"; + WNDCLASSEXA wndclass; + ULONG_PTR ctx_cookie; + HANDLE hCtx; + + if (!load_v6_module(&ctx_cookie, &hCtx)) + return; + + wndclass.cbSize = sizeof(wndclass); + wndclass.style = CS_HREDRAW | CS_VREDRAW; + wndclass.lpfnWndProc = WndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = GetModuleHandleA(NULL); + wndclass.hIcon = LoadIconA(NULL, (LPCSTR)IDI_APPLICATION); + wndclass.hIconSm = LoadIconA(NULL, (LPCSTR)IDI_APPLICATION); + wndclass.hCursor = LoadCursorA(NULL, (LPCSTR)IDC_ARROW); + wndclass.hbrBackground = GetStockObject(WHITE_BRUSH); + wndclass.lpszClassName = classname; + wndclass.lpszMenuName = NULL; + RegisterClassExA(&wndclass); + + hMainWnd = CreateWindowA(classname, "Test", WS_OVERLAPPEDWINDOW, 0, 0, 500, 500, NULL, NULL, + GetModuleHandleA(NULL), NULL); + ShowWindow(hMainWnd, SW_SHOW); + + test_updates(0, 0); + test_updates(SS_SIMPLE, 0); + test_updates(SS_ICON, 0); + test_updates(SS_BITMAP, 0); + test_updates(SS_BITMAP | SS_CENTERIMAGE, 0); + test_updates(SS_BLACKRECT, TODO_COUNT); + test_updates(SS_WHITERECT, TODO_COUNT); + test_updates(SS_ETCHEDHORZ, TODO_COUNT); + test_updates(SS_ETCHEDVERT, TODO_COUNT); + test_set_text(); + + DestroyWindow(hMainWnd); + + unload_v6_module(ctx_cookie, hCtx); +} diff --git a/modules/rostests/winetests/comctl32/syslink.c b/modules/rostests/winetests/comctl32/syslink.c index 028e7a6b96c..a585701c7ea 100644 --- a/modules/rostests/winetests/comctl32/syslink.c +++ b/modules/rostests/winetests/comctl32/syslink.c @@ -175,39 +175,81 @@ static HWND create_syslink(DWORD style, HWND parent) return hWndSysLink; } +static void test_create_syslink(void) +{ + HWND hWndSysLink; + LONG oldstyle; + + /* Create an invisible SysLink control */ + flush_sequences(sequences, NUM_MSG_SEQUENCE); + hWndSysLink = create_syslink(WS_CHILD | WS_TABSTOP, hWndParent); + ok(hWndSysLink != NULL, "Expected non NULL value (le %u)\n", GetLastError()); + flush_events(); + ok_sequence(sequences, SYSLINK_SEQ_INDEX, empty_wnd_seq, "create SysLink", FALSE); + ok_sequence(sequences, PARENT_SEQ_INDEX, parent_create_syslink_wnd_seq, "create SysLink (parent)", TRUE); + + /* Make the SysLink control visible */ + flush_sequences(sequences, NUM_MSG_SEQUENCE); + oldstyle = GetWindowLongA(hWndSysLink, GWL_STYLE); + SetWindowLongA(hWndSysLink, GWL_STYLE, oldstyle | WS_VISIBLE); + RedrawWindow(hWndSysLink, NULL, NULL, RDW_INVALIDATE); + flush_events(); + ok_sequence(sequences, SYSLINK_SEQ_INDEX, visible_syslink_wnd_seq, "visible SysLink", TRUE); + ok_sequence(sequences, PARENT_SEQ_INDEX, parent_visible_syslink_wnd_seq, "visible SysLink (parent)", TRUE); + + DestroyWindow(hWndSysLink); +} + +static void test_LM_GETIDEALHEIGHT(void) +{ + HWND hwnd; + LONG ret; + + hwnd = create_syslink(WS_CHILD | WS_TABSTOP | WS_VISIBLE, hWndParent); + ok(hwnd != NULL, "Failed to create SysLink window.\n"); + + ret = SendMessageA(hwnd, LM_GETIDEALHEIGHT, 0, 0); + ok(ret > 0, "Unexpected ideal height, %d.\n", ret); + + DestroyWindow(hwnd); +} + +static void test_LM_GETIDEALSIZE(void) +{ + HWND hwnd; + LONG ret; + SIZE sz; + + hwnd = create_syslink(WS_CHILD | WS_TABSTOP | WS_VISIBLE, hWndParent); + ok(hwnd != NULL, "Failed to create SysLink window.\n"); + + memset(&sz, 0, sizeof(sz)); + ret = SendMessageA(hwnd, LM_GETIDEALSIZE, 0, (LPARAM)&sz); + ok(ret > 0, "Unexpected return value, %d.\n", ret); + if (sz.cy == 0) + win_skip("LM_GETIDEALSIZE is not supported.\n"); + else + { + ok(sz.cx > 5, "Unexpected ideal width, %d.\n", sz.cx); + ok(sz.cy == ret, "Unexpected ideal height, %d.\n", sz.cy); + } + + DestroyWindow(hwnd); +} START_TEST(syslink) { ULONG_PTR ctx_cookie; - HANDLE hCtx; HMODULE hComctl32; - BOOL (WINAPI *pInitCommonControlsEx)(const INITCOMMONCONTROLSEX*); - INITCOMMONCONTROLSEX iccex; - BOOL rc; - HWND hWndSysLink; - LONG oldstyle; POINT orig_pos; + HANDLE hCtx; if (!load_v6_module(&ctx_cookie, &hCtx)) return; /* LoadLibrary is needed. This file has no reference to functions in comctl32 */ hComctl32 = LoadLibraryA("comctl32.dll"); - pInitCommonControlsEx = (void*)GetProcAddress(hComctl32, "InitCommonControlsEx"); - if (!pInitCommonControlsEx) - { - win_skip("InitCommonControlsEx() is missing. Skipping the tests\n"); - return; - } - iccex.dwSize = sizeof(iccex); - iccex.dwICC = ICC_LINK_CLASS; - rc = pInitCommonControlsEx(&iccex); - ok(rc, "InitCommonControlsEx failed (le %u)\n", GetLastError()); - if (!rc) - { - skip("Could not register ICC_LINK_CLASS\n"); - return; - } + ok(hComctl32 != NULL, "Failed to load comctl32.dll.\n"); /* Move the cursor off the parent window */ GetCursorPos(&orig_pos); @@ -218,36 +260,12 @@ START_TEST(syslink) /* Create parent window */ hWndParent = create_parent_window(); ok(hWndParent != NULL, "Failed to create parent Window!\n"); - if (!hWndParent) - { - skip("Parent window not present\n"); - return; - } - flush_events(); - - /* Create an invisible SysLink control */ - flush_sequences(sequences, NUM_MSG_SEQUENCE); - hWndSysLink = create_syslink(WS_CHILD | WS_TABSTOP, hWndParent); - ok(hWndSysLink != NULL, "Expected non NULL value (le %u)\n", GetLastError()); - if (!hWndSysLink) - { - skip("SysLink control not present?\n"); - return; - } flush_events(); - ok_sequence(sequences, SYSLINK_SEQ_INDEX, empty_wnd_seq, "create SysLink", FALSE); - ok_sequence(sequences, PARENT_SEQ_INDEX, parent_create_syslink_wnd_seq, "create SysLink (parent)", TRUE); - /* Make the SysLink control visible */ - flush_sequences(sequences, NUM_MSG_SEQUENCE); - oldstyle = GetWindowLongA(hWndSysLink, GWL_STYLE); - SetWindowLongA(hWndSysLink, GWL_STYLE, oldstyle | WS_VISIBLE); - RedrawWindow(hWndSysLink, NULL, NULL, RDW_INVALIDATE); - flush_events(); - ok_sequence(sequences, SYSLINK_SEQ_INDEX, visible_syslink_wnd_seq, "visible SysLink", TRUE); - ok_sequence(sequences, PARENT_SEQ_INDEX, parent_visible_syslink_wnd_seq, "visible SysLink (parent)", TRUE); + test_create_syslink(); + test_LM_GETIDEALHEIGHT(); + test_LM_GETIDEALSIZE(); - DestroyWindow(hWndSysLink); DestroyWindow(hWndParent); unload_v6_module(ctx_cookie, hCtx); SetCursorPos(orig_pos.x, orig_pos.y); diff --git a/modules/rostests/winetests/comctl32/taskdialog.c b/modules/rostests/winetests/comctl32/taskdialog.c index 40ca1637c20..da0b2faf637 100644 --- a/modules/rostests/winetests/comctl32/taskdialog.c +++ b/modules/rostests/winetests/comctl32/taskdialog.c @@ -19,10 +19,268 @@ #include "precomp.h" +#define WM_TD_CALLBACK (WM_APP) /* Custom dummy message to wrap callback notifications */ + +#define NUM_MSG_SEQUENCES 1 +#define TASKDIALOG_SEQ_INDEX 0 + +#define TEST_NUM_BUTTONS 10 /* Number of custom buttons to test with */ + +#define ID_START 20 /* Lower IDs might be used by the system */ +#define ID_START_BUTTON (ID_START + 0) + static HRESULT (WINAPI *pTaskDialogIndirect)(const TASKDIALOGCONFIG *, int *, int *, BOOL *); static HRESULT (WINAPI *pTaskDialog)(HWND, HINSTANCE, const WCHAR *, const WCHAR *, const WCHAR *, TASKDIALOG_COMMON_BUTTON_FLAGS, const WCHAR *, int *); +static struct msg_sequence *sequences[NUM_MSG_SEQUENCES]; + +struct message_info +{ + UINT message; + WPARAM wparam; + LPARAM lparam; + + HRESULT callback_retval; + const struct message_info *send; /* Message to send to trigger the next callback message */ +}; + +static const struct message_info *current_message_info; + +/* Messages to send */ +static const struct message_info msg_send_return[] = +{ + { WM_KEYDOWN, VK_RETURN, 0 }, + { 0 } +}; + +/* Messages to test against */ +static const struct message_info msg_return_press_ok[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_return }, + { TDN_BUTTON_CLICKED, IDOK, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_press_yes[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_return }, + { TDN_BUTTON_CLICKED, IDYES, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_press_no[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_return }, + { TDN_BUTTON_CLICKED, IDNO, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_press_cancel[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_return }, + { TDN_BUTTON_CLICKED, IDCANCEL, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_press_retry[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_return }, + { TDN_BUTTON_CLICKED, IDRETRY, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_press_custom1[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_return }, + { TDN_BUTTON_CLICKED, ID_START_BUTTON, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_press_custom4[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_return }, + { TDN_BUTTON_CLICKED, ID_START_BUTTON + 3, 0, S_OK, NULL }, + { 0 } +}; + +static const struct message_info msg_return_press_custom10[] = +{ + { TDN_CREATED, 0, 0, S_OK, msg_send_return }, + { TDN_BUTTON_CLICKED, -1, 0, S_OK, NULL }, + { 0 } +}; + +static void init_test_message(UINT message, WPARAM wParam, LPARAM lParam, struct message *msg) +{ + msg->message = WM_TD_CALLBACK; + msg->flags = sent|wparam|lparam|id; + msg->wParam = wParam; + msg->lParam = lParam; + msg->id = message; + msg->stage = 0; +} + +#define run_test(info, expect_button, seq, context) \ + run_test_(info, expect_button, seq, context, \ + sizeof(seq)/sizeof(seq[0]) - 1, __FILE__, __LINE__) + +static void run_test_(TASKDIALOGCONFIG *info, int expect_button, const struct message_info *test_messages, + const char *context, int test_messages_len, const char *file, int line) +{ + struct message *msg, *msg_start; + int ret_button = 0; + int ret_radio = 0; + HRESULT hr; + int i; + + /* Allocate messages to test against, plus 2 implicit and 1 empty */ + msg_start = msg = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*msg) * (test_messages_len + 3)); + + /* Always needed, thus made implicit */ + init_test_message(TDN_DIALOG_CONSTRUCTED, 0, 0, msg++); + for (i = 0; i < test_messages_len; i++) + init_test_message(test_messages[i].message, test_messages[i].wparam, test_messages[i].lparam, msg++); + /* Always needed, thus made implicit */ + init_test_message(TDN_DESTROYED, 0, 0, msg++); + + current_message_info = test_messages; + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + hr = pTaskDialogIndirect(info, &ret_button, &ret_radio, NULL); + ok_(file, line)(hr == S_OK, "TaskDialogIndirect() failed, got %#x.\n", hr); + + ok_sequence_(sequences, TASKDIALOG_SEQ_INDEX, msg_start, context, FALSE, file, line); + ok_(file, line)(ret_button == expect_button, + "Wrong button. Expected %d, got %d\n", expect_button, ret_button); + + HeapFree(GetProcessHeap(), 0, msg_start); +} + +static const LONG_PTR test_ref_data = 123456; + +static HRESULT CALLBACK taskdialog_callback_proc(HWND hwnd, UINT notification, + WPARAM wParam, LPARAM lParam, LONG_PTR ref_data) +{ + int msg_pos = sequences[TASKDIALOG_SEQ_INDEX]->count - 1; /* Skip implicit message */ + const struct message_info *msg_send; + struct message msg; + + ok(test_ref_data == ref_data, "Unexpected ref data %lu.\n", ref_data); + + init_test_message(notification, (short)wParam, lParam, &msg); + add_message(sequences, TASKDIALOG_SEQ_INDEX, &msg); + + if (notification == TDN_DIALOG_CONSTRUCTED || notification == TDN_DESTROYED) /* Skip implicit messages */ + return S_OK; + + msg_send = current_message_info[msg_pos].send; + for(; msg_send && msg_send->message; msg_send++) + PostMessageW(hwnd, msg_send->message, msg_send->wparam, msg_send->lparam); + + return current_message_info[msg_pos].callback_retval; +} + +static void test_invalid_parameters(void) +{ + TASKDIALOGCONFIG info = { 0 }; + HRESULT hr; + + hr = pTaskDialogIndirect(NULL, NULL, NULL, NULL); + ok(hr == E_INVALIDARG, "Unexpected return value %#x.\n", hr); + + info.cbSize = 0; + hr = pTaskDialogIndirect(&info, NULL, NULL, NULL); + ok(hr == E_INVALIDARG, "Unexpected return value %#x.\n", hr); + + info.cbSize = sizeof(TASKDIALOGCONFIG) - 1; + hr = pTaskDialogIndirect(&info, NULL, NULL, NULL); + ok(hr == E_INVALIDARG, "Unexpected return value %#x.\n", hr); + + info.cbSize = sizeof(TASKDIALOGCONFIG) + 1; + hr = pTaskDialogIndirect(&info, NULL, NULL, NULL); + ok(hr == E_INVALIDARG, "Unexpected return value %#x.\n", hr); +} + +static void test_callback(void) +{ + TASKDIALOGCONFIG info = {0}; + + info.cbSize = sizeof(TASKDIALOGCONFIG); + info.pfCallback = taskdialog_callback_proc; + info.lpCallbackData = test_ref_data; + + run_test(&info, IDOK, msg_return_press_ok, "Press VK_RETURN."); +} + +static void test_buttons(void) +{ + TASKDIALOGCONFIG info = {0}; + + TASKDIALOG_BUTTON custom_buttons[TEST_NUM_BUTTONS]; + const WCHAR button_format[] = {'%','0','2','d',0}; + WCHAR button_titles[TEST_NUM_BUTTONS * 3]; /* Each button has two digits as title, plus null-terminator */ + int i; + + info.cbSize = sizeof(TASKDIALOGCONFIG); + info.pfCallback = taskdialog_callback_proc; + info.lpCallbackData = test_ref_data; + + /* Init custom buttons */ + for (i = 0; i < TEST_NUM_BUTTONS; i++) + { + WCHAR *text = &button_titles[i * 3]; + wsprintfW(text, button_format, i); + + custom_buttons[i].pszButtonText = text; + custom_buttons[i].nButtonID = ID_START_BUTTON + i; + } + custom_buttons[TEST_NUM_BUTTONS - 1].nButtonID = -1; + + /* Test nDefaultButton */ + + /* Test common buttons with invalid default ID */ + info.nDefaultButton = 0; /* Should default to first created button */ + info.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_YES_BUTTON | TDCBF_NO_BUTTON + | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; + run_test(&info, IDOK, msg_return_press_ok, "default button: unset default"); + info.dwCommonButtons = TDCBF_YES_BUTTON | TDCBF_NO_BUTTON + | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; + run_test(&info, IDYES, msg_return_press_yes, "default button: unset default"); + info.dwCommonButtons = TDCBF_NO_BUTTON | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; + run_test(&info, IDNO, msg_return_press_no, "default button: unset default"); + info.dwCommonButtons = TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; + run_test(&info, IDRETRY, msg_return_press_retry, "default button: unset default"); + info.dwCommonButtons = TDCBF_CANCEL_BUTTON | TDCBF_CLOSE_BUTTON; + run_test(&info, IDCANCEL, msg_return_press_cancel, "default button: unset default"); + + /* Test with all common and custom buttons and invalid default ID */ + info.nDefaultButton = 0xff; /* Random ID, should also default to first created button */ + info.cButtons = TEST_NUM_BUTTONS; + info.pButtons = custom_buttons; + run_test(&info, ID_START_BUTTON, msg_return_press_custom1, "default button: invalid default, with common buttons - 1"); + + info.nDefaultButton = -1; /* Should work despite button ID -1 */ + run_test(&info, -1, msg_return_press_custom10, "default button: invalid default, with common buttons - 2"); + + info.nDefaultButton = -2; /* Should also default to first created button */ + run_test(&info, ID_START_BUTTON, msg_return_press_custom1, "default button: invalid default, with common buttons - 3"); + + /* Test with only custom buttons and invalid default ID */ + info.dwCommonButtons = 0; + run_test(&info, ID_START_BUTTON, msg_return_press_custom1, "default button: invalid default, no common buttons"); + + /* Test with common and custom buttons and valid default ID */ + info.dwCommonButtons = TDCBF_OK_BUTTON | TDCBF_YES_BUTTON | TDCBF_NO_BUTTON + | TDCBF_CANCEL_BUTTON | TDCBF_RETRY_BUTTON | TDCBF_CLOSE_BUTTON; + info.nDefaultButton = IDRETRY; + run_test(&info, IDRETRY, msg_return_press_retry, "default button: valid default - 1"); + + /* Test with common and custom buttons and valid default ID */ + info.nDefaultButton = ID_START_BUTTON + 3; + run_test(&info, ID_START_BUTTON + 3, msg_return_press_custom4, "default button: valid default - 2"); +} + START_TEST(taskdialog) { ULONG_PTR ctx_cookie; @@ -54,5 +312,11 @@ START_TEST(taskdialog) ok(pTaskDialogIndirect == ptr_ordinal, "got wrong pointer for ordinal 345, %p expected %p\n", ptr_ordinal, pTaskDialogIndirect); + init_msg_sequences(sequences, NUM_MSG_SEQUENCES); + + test_invalid_parameters(); + test_callback(); + test_buttons(); + unload_v6_module(ctx_cookie, hCtx); } diff --git a/modules/rostests/winetests/comctl32/testlist.c b/modules/rostests/winetests/comctl32/testlist.c index 2abb83ad7f1..e05eb6af53a 100644 --- a/modules/rostests/winetests/comctl32/testlist.c +++ b/modules/rostests/winetests/comctl32/testlist.c @@ -5,12 +5,14 @@ extern void func_animate(void); extern void func_button(void); -extern void func_comboex(void); +extern void func_combo(void); extern void func_datetime(void); extern void func_dpa(void); +extern void func_edit(void); extern void func_header(void); extern void func_imagelist(void); extern void func_ipaddress(void); +extern void func_listbox(void); extern void func_listview(void); extern void func_misc(void); extern void func_monthcal(void); @@ -19,6 +21,7 @@ extern void func_pager(void); extern void func_progress(void); extern void func_propsheet(void); extern void func_rebar(void); +extern void func_static(void); extern void func_status(void); extern void func_subclass(void); extern void func_syslink(void); @@ -34,12 +37,14 @@ const struct test winetest_testlist[] = { { "animate", func_animate }, { "button", func_button }, - { "comboex", func_comboex }, + { "combo", func_combo }, { "datetime", func_datetime }, { "dpa", func_dpa }, + { "edit", func_edit }, { "header", func_header }, { "imagelist", func_imagelist }, { "ipaddress", func_ipaddress }, + { "listbox", func_listbox }, { "listview", func_listview }, { "misc", func_misc }, { "monthcal", func_monthcal }, @@ -48,6 +53,7 @@ const struct test winetest_testlist[] = { "progress", func_progress }, { "propsheet", func_propsheet }, { "rebar", func_rebar }, + { "static", func_static }, { "status", func_status }, { "subclass", func_subclass }, { "syslink", func_syslink }, diff --git a/modules/rostests/winetests/comctl32/tooltips.c b/modules/rostests/winetests/comctl32/tooltips.c index 2cc02103128..7564a11e4bc 100644 --- a/modules/rostests/winetests/comctl32/tooltips.c +++ b/modules/rostests/winetests/comctl32/tooltips.c @@ -146,8 +146,8 @@ static void test_customdraw(void) { DWORD iterationNumber; WNDCLASSA wc; - LRESULT lResult; POINT orig_pos; + LRESULT ret; /* Create a class to use the custom draw wndproc */ wc.style = CS_HREDRAW | CS_VREDRAW; @@ -211,8 +211,8 @@ static void test_customdraw(void) { toolInfo.lpszText = (LPSTR)"This is a test tooltip"; toolInfo.lParam = 0xdeadbeef; GetClientRect (parent, &toolInfo.rect); - lResult = SendMessageA(hwndTip, TTM_ADDTOOLA, 0, (LPARAM)&toolInfo); - ok(lResult, "Adding the tool to the tooltip failed\n"); + ret = SendMessageA(hwndTip, TTM_ADDTOOLA, 0, (LPARAM)&toolInfo); + ok(ret, "Failed to add the tool.\n"); /* Make tooltip appear quickly */ SendMessageA(hwndTip, TTM_SETDELAYTIME, TTDT_INITIAL, MAKELPARAM(1,0)); @@ -231,6 +231,20 @@ static void test_customdraw(void) { expectedResults[iterationNumber].ExpectedCalls); } + ret = SendMessageA(hwndTip, TTM_GETCURRENTTOOLA, 0, 0); + ok(ret, "Failed to get current tool %#lx.\n", ret); + + memset(&toolInfo, 0xcc, sizeof(toolInfo)); + toolInfo.cbSize = sizeof(toolInfo); + toolInfo.lpszText = NULL; + toolInfo.lpReserved = (void *)0xdeadbeef; + SendMessageA(hwndTip, TTM_GETCURRENTTOOLA, 0, (LPARAM)&toolInfo); + ok(toolInfo.hwnd == parent, "Unexpected hwnd %p.\n", toolInfo.hwnd); + ok(toolInfo.hinst == GetModuleHandleA(NULL), "Unexpected hinst %p.\n", toolInfo.hinst); + ok(toolInfo.uId == 0x1234abcd, "Unexpected uId %lx.\n", toolInfo.uId); + ok(toolInfo.lParam == 0, "Unexpected lParam %lx.\n", toolInfo.lParam); + ok(toolInfo.lpReserved == (void *)0xdeadbeef, "Unexpected lpReserved %p.\n", toolInfo.lpReserved); + /* Clean up */ DestroyWindow(hwndTip); DestroyWindow(parent); @@ -436,33 +450,6 @@ todo_wine r = SendMessageW(hwnd, TTM_ADDTOOLW, 0, (LPARAM)&toolinfoW); ok(!r, "Adding the tool to the tooltip succeeded!\n"); - /* lpszText with an invalid address */ - toolinfoW.cbSize = sizeof(TTTOOLINFOW); - toolinfoW.hwnd = notify; - toolinfoW.hinst = GetModuleHandleA(NULL); - toolinfoW.uFlags = 0; - toolinfoW.uId = 0; - toolinfoW.lpszText = (LPWSTR)0xdeadbeef; - toolinfoW.lParam = 0; - GetClientRect(hwnd, &toolinfoW.rect); - r = SendMessageA(hwnd, TTM_ADDTOOLW, 0, (LPARAM)&toolinfoW); - ok(!r, "Adding the tool to the tooltip succeeded!\n"); - - /* lpszText with an invalid address. Crashes using TTTOOLINFOA message */ - if(0) - { - toolinfoA.cbSize = sizeof(TTTOOLINFOA); - toolinfoA.hwnd = notify; - toolinfoA.hinst = GetModuleHandleA(NULL); - toolinfoA.uFlags = 0; - toolinfoA.uId = 0; - toolinfoA.lpszText = (LPSTR)0xdeadbeef; - toolinfoA.lParam = 0; - GetClientRect(hwnd, &toolinfoA.rect); - r = SendMessageA(hwnd, TTM_ADDTOOLA, 0, (LPARAM)&toolinfoA); - ok(!r, "Adding the tool to the tooltip succeeded!\n"); - } - if (0) /* crashes on NT4 */ { toolinfoW.hwnd = NULL; @@ -513,9 +500,8 @@ static void test_ttm_gettoolinfo(void) HWND hwnd; DWORD r; - hwnd = CreateWindowExA(0, TOOLTIPS_CLASSA, NULL, 0, - 10, 10, 300, 100, - NULL, NULL, NULL, 0); + hwnd = CreateWindowExA(0, TOOLTIPS_CLASSA, NULL, 0, 10, 10, 300, 100, NULL, NULL, NULL, 0); + ok(hwnd != NULL, "Failed to create tooltip control.\n"); ti.cbSize = TTTOOLINFOA_V2_SIZE; ti.hwnd = NULL; @@ -633,11 +619,7 @@ static void test_ttm_gettoolinfo(void) hwnd = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, 0, 10, 10, 300, 100, NULL, NULL, NULL, 0); - if(!hwnd) - { - win_skip("CreateWindowExW() not supported. Skipping.\n"); - return; - } + ok(hwnd != NULL, "Failed to create tooltip window.\n"); tiW.cbSize = TTTOOLINFOW_V1_SIZE - 1; tiW.hwnd = NULL; @@ -779,11 +761,7 @@ static void test_longtextW(void) hwnd = CreateWindowExW(0, TOOLTIPS_CLASSW, NULL, 0, 10, 10, 300, 100, NULL, NULL, NULL, 0); - if(!hwnd) - { - win_skip("CreateWindowExW() not supported. Skipping.\n"); - return; - } + ok(hwnd != NULL, "Failed to create tooltip window.\n"); toolinfoW.cbSize = TTTOOLINFOW_V2_SIZE; toolinfoW.hwnd = NULL; diff --git a/modules/rostests/winetests/comctl32/treeview.c b/modules/rostests/winetests/comctl32/treeview.c index e14c33690a1..d22d1958566 100644 --- a/modules/rostests/winetests/comctl32/treeview.c +++ b/modules/rostests/winetests/comctl32/treeview.c @@ -207,6 +207,9 @@ static const struct message test_right_click_seq[] = { { WM_RBUTTONDOWN, sent|wparam, MK_RBUTTON }, { WM_CAPTURECHANGED, sent|defwinproc }, { TVM_GETNEXTITEM, sent|wparam|lparam|defwinproc, TVGN_CARET, 0 }, + { WM_NCHITTEST, sent|optional }, + { WM_SETCURSOR, sent|optional }, + { WM_MOUSEMOVE, sent|optional }, { 0 } }; @@ -354,6 +357,8 @@ static const struct message parent_vk_return_seq[] = { static const struct message parent_right_click_seq[] = { { WM_NOTIFY, sent|id, 0, 0, NM_RCLICK }, { WM_CONTEXTMENU, sent }, + { WM_NOTIFY, sent|optional }, + { WM_SETCURSOR, sent|optional }, { 0 } }; diff --git a/modules/rostests/winetests/comctl32/updown.c b/modules/rostests/winetests/comctl32/updown.c index e3dde91ebc9..ec0937a5a64 100644 --- a/modules/rostests/winetests/comctl32/updown.c +++ b/modules/rostests/winetests/comctl32/updown.c @@ -288,11 +288,16 @@ static HWND create_updown_control(DWORD style, HWND buddy) RECT rect; GetClientRect(parent_wnd, &rect); - updown = CreateUpDownControl(WS_CHILD | WS_BORDER | WS_VISIBLE | style, - 0, 0, rect.right, rect.bottom, parent_wnd, 1, GetModuleHandleA(NULL), buddy, - 100, 0, 50); + updown = CreateWindowExA(0, UPDOWN_CLASSA, NULL, WS_CHILD | WS_BORDER | WS_VISIBLE | style, + 0, 0, rect.right, rect.bottom, + parent_wnd, (HMENU)1, GetModuleHandleA(NULL), NULL); + ok(updown != NULL, "Failed to create UpDown control.\n"); if (!updown) return NULL; + SendMessageA(updown, UDM_SETBUDDY, (WPARAM)buddy, 0); + SendMessageA(updown, UDM_SETRANGE, 0, MAKELONG(100, 0)); + SendMessageA(updown, UDM_SETPOS, 0, MAKELONG(50, 0)); + oldproc = (WNDPROC)SetWindowLongPtrA(updown, GWLP_WNDPROC, (LONG_PTR)updown_subclass_proc); SetWindowLongPtrA(updown, GWLP_USERDATA, (LONG_PTR)oldproc); @@ -512,6 +517,7 @@ static void test_updown_pos32(void) static void test_updown_buddy(void) { HWND updown, buddyReturn, buddy; + RECT rect, rect2; WNDPROC proc; DWORD style; @@ -558,8 +564,82 @@ static void test_updown_buddy(void) } DestroyWindow(updown); - DestroyWindow(buddy); + + /* Create with buddy and UDS_HORZ, reset buddy. */ + updown = create_updown_control(UDS_HORZ, g_edit); + + buddyReturn = (HWND)SendMessageA(updown, UDM_GETBUDDY, 0, 0); + ok(buddyReturn == g_edit, "Unexpected buddy window.\n"); + + GetClientRect(updown, &rect); + + buddyReturn = (HWND)SendMessageA(updown, UDM_SETBUDDY, 0, 0); + ok(buddyReturn == g_edit, "Unexpected buddy window.\n"); + + GetClientRect(updown, &rect2); + ok(EqualRect(&rect, &rect2), "Unexpected window rect.\n"); + + /* Remove UDS_HORZ, reset buddy again. */ + style = GetWindowLongA(updown, GWL_STYLE); + SetWindowLongA(updown, GWL_STYLE, style & ~UDS_HORZ); + style = GetWindowLongA(updown, GWL_STYLE); + ok(!(style & UDS_HORZ), "Unexpected style.\n"); + + buddyReturn = (HWND)SendMessageA(updown, UDM_SETBUDDY, 0, 0); + ok(buddyReturn == NULL, "Unexpected buddy window.\n"); + + GetClientRect(updown, &rect2); + ok(EqualRect(&rect, &rect2), "Unexpected window rect.\n"); + + DestroyWindow(updown); + + /* Without UDS_HORZ. */ + updown = create_updown_control(0, g_edit); + + buddyReturn = (HWND)SendMessageA(updown, UDM_GETBUDDY, 0, 0); + ok(buddyReturn == g_edit, "Unexpected buddy window.\n"); + + GetClientRect(updown, &rect); + + buddyReturn = (HWND)SendMessageA(updown, UDM_SETBUDDY, 0, 0); + ok(buddyReturn == g_edit, "Unexpected buddy window.\n"); + + GetClientRect(updown, &rect2); + ok(EqualRect(&rect, &rect2), "Unexpected window rect.\n"); + + DestroyWindow(updown); + + /* Create without buddy. */ + GetClientRect(parent_wnd, &rect); + updown = CreateWindowExA(0, UPDOWN_CLASSA, NULL, WS_CHILD | WS_BORDER | WS_VISIBLE | UDS_HORZ, + 0, 0, rect.right, rect.bottom, parent_wnd, (HMENU)1, GetModuleHandleA(NULL), NULL); + ok(updown != NULL, "Failed to create UpDown control.\n"); + + GetClientRect(updown, &rect); + buddyReturn = (HWND)SendMessageA(updown, UDM_SETBUDDY, 0, 0); + ok(buddyReturn == NULL, "Unexpected buddy window.\n"); + GetClientRect(updown, &rect2); + + ok(EqualRect(&rect, &rect2), "Unexpected window rect.\n"); + + style = GetWindowLongA(updown, GWL_STYLE); + SetWindowLongA(updown, GWL_STYLE, style & ~UDS_HORZ); + + GetClientRect(updown, &rect2); + ok(EqualRect(&rect, &rect2), "Unexpected window rect.\n"); + + buddyReturn = (HWND)SendMessageA(updown, UDM_SETBUDDY, (WPARAM)g_edit, 0); + ok(buddyReturn == NULL, "Unexpected buddy window.\n"); + GetClientRect(updown, &rect); + + buddyReturn = (HWND)SendMessageA(updown, UDM_SETBUDDY, 0, 0); + ok(buddyReturn == g_edit, "Unexpected buddy window.\n"); + GetClientRect(updown, &rect2); +todo_wine + ok(EqualRect(&rect, &rect2), "Unexpected window rect.\n"); + + DestroyWindow(updown); } static void test_updown_base(void) @@ -790,6 +870,29 @@ static void test_UDS_SETBUDDYINT(void) DestroyWindow(updown); } +static void test_CreateUpDownControl(void) +{ + HWND updown, buddy; + DWORD range, pos; + RECT rect; + + GetClientRect(parent_wnd, &rect); + updown = CreateUpDownControl(WS_CHILD | WS_BORDER | WS_VISIBLE, + 0, 0, rect.right, rect.bottom, parent_wnd, 1, GetModuleHandleA(NULL), g_edit, 100, 10, 50); + ok(updown != NULL, "Failed to create control.\n"); + + buddy = (HWND)SendMessageA(updown, UDM_GETBUDDY, 0, 0); + ok(buddy == g_edit, "Unexpected buddy window.\n"); + + range = SendMessageA(updown, UDM_GETRANGE, 0, 0); + ok(range == MAKELONG(100, 10), "Unexpected range.\n"); + + pos = SendMessageA(updown, UDM_GETPOS, 0, 0); + ok(pos == MAKELONG(50, 1), "Unexpected position.\n"); + + DestroyWindow(updown); +} + START_TEST(updown) { HMODULE mod = GetModuleHandleA("comctl32.dll"); @@ -811,6 +914,7 @@ START_TEST(updown) test_updown_base(); test_updown_unicode(); test_UDS_SETBUDDYINT(); + test_CreateUpDownControl(); DestroyWindow(g_edit); DestroyWindow(parent_wnd);