1 /* 2 * PROJECT: ReactOS api tests 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Test for IAutoComplete objects 5 * COPYRIGHT: Copyright 2021 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 */ 7 #define _UNICODE 8 #define UNICODE 9 #include <apitest.h> 10 #include <shlobj.h> 11 #include <atlbase.h> 12 #include <tchar.h> 13 #include <atlcom.h> 14 #include <atlwin.h> 15 #include <atlstr.h> 16 #include <atlsimpcoll.h> 17 #include <shlwapi.h> 18 #include <strsafe.h> 19 #include <shellutils.h> 20 21 //#define MANUAL_DEBUGGING 22 23 // compare wide strings 24 #define ok_wstri(x, y) \ 25 ok(lstrcmpiW(x, y) == 0, "Wrong string. Expected '%S', got '%S'\n", y, x) 26 27 // create an EDIT control 28 static HWND MyCreateEditCtrl(INT x, INT y, INT cx, INT cy) 29 { 30 DWORD style = WS_POPUPWINDOW | WS_BORDER; 31 DWORD exstyle = WS_EX_CLIENTEDGE; 32 return CreateWindowExW(exstyle, L"EDIT", NULL, style, x, y, cx, cy, 33 NULL, NULL, GetModuleHandleW(NULL), NULL); 34 } 35 36 static BOOL s_bReset = FALSE; 37 static BOOL s_bExpand = FALSE; 38 static CStringW s_strExpand; 39 40 // CEnumString class for auto-completion test 41 class CEnumString : public IEnumString, public IACList2 42 { 43 public: 44 CEnumString() : m_cRefs(0), m_nIndex(0), m_nCount(0), m_pList(NULL) 45 { 46 trace("CEnumString::CEnumString(%p)\n", this); 47 } 48 49 virtual ~CEnumString() 50 { 51 trace("CEnumString::~CEnumString(%p)\n", this); 52 for (UINT i = 0; i < m_nCount; ++i) 53 { 54 CoTaskMemFree(m_pList[i]); 55 m_pList[i] = NULL; 56 } 57 CoTaskMemFree(m_pList); 58 } 59 60 VOID SetList(UINT nCount, LPWSTR *pList) 61 { 62 m_nCount = nCount; 63 m_pList = pList; 64 } 65 66 STDMETHODIMP QueryInterface(REFIID iid, VOID** ppv) override 67 { 68 if (iid == IID_IUnknown || iid == IID_IEnumString) 69 { 70 trace("IID_IEnumString\n"); 71 AddRef(); 72 *ppv = static_cast<IEnumString *>(this); 73 return S_OK; 74 } 75 if (iid == IID_IACList || iid == IID_IACList2) 76 { 77 trace("IID_IACList2\n"); 78 AddRef(); 79 *ppv = static_cast<IACList2 *>(this); 80 return S_OK; 81 } 82 return E_NOINTERFACE; 83 } 84 STDMETHODIMP_(ULONG) AddRef() override 85 { 86 //trace("CEnumString::AddRef\n"); 87 ++m_cRefs; 88 return m_cRefs; 89 } 90 STDMETHODIMP_(ULONG) Release() override 91 { 92 //trace("CEnumString::Release\n"); 93 --m_cRefs; 94 if (m_cRefs == 0) 95 { 96 delete this; 97 return 0; 98 } 99 return m_cRefs; 100 } 101 102 STDMETHODIMP Next(ULONG celt, LPWSTR* rgelt, ULONG* pceltFetched) override 103 { 104 if (rgelt) 105 *rgelt = NULL; 106 if (pceltFetched) 107 *pceltFetched = 0; 108 if (celt != 1 || !rgelt || !pceltFetched) 109 return E_INVALIDARG; 110 if (m_nIndex >= m_nCount) 111 return S_FALSE; 112 113 SHStrDupW(m_pList[m_nIndex], rgelt); 114 ++m_nIndex; 115 if (!*rgelt) 116 return E_OUTOFMEMORY; 117 *pceltFetched = 1; 118 return S_OK; 119 } 120 STDMETHODIMP Skip(ULONG celt) override 121 { 122 trace("CEnumString::Skip(%lu)\n", celt); 123 return E_NOTIMPL; 124 } 125 STDMETHODIMP Reset() override 126 { 127 trace("CEnumString::Reset\n"); 128 m_nIndex = 0; 129 s_bReset = TRUE; 130 return S_OK; 131 } 132 STDMETHODIMP Clone(IEnumString** ppenum) override 133 { 134 trace("CEnumString::Clone()\n"); 135 return E_NOTIMPL; 136 } 137 138 STDMETHODIMP Expand(PCWSTR pszExpand) override 139 { 140 trace("CEnumString::Expand(%S)\n", pszExpand); 141 s_bExpand = TRUE; 142 s_strExpand = pszExpand; 143 return S_OK; 144 } 145 STDMETHODIMP GetOptions(DWORD *pdwFlag) override 146 { 147 trace("CEnumString::GetOption(%p)\n", pdwFlag); 148 return S_OK; 149 } 150 STDMETHODIMP SetOptions(DWORD dwFlag) override 151 { 152 trace("CEnumString::SetOption(0x%lX)\n", dwFlag); 153 return S_OK; 154 } 155 156 protected: 157 ULONG m_cRefs; 158 UINT m_nIndex, m_nCount; 159 LPWSTR *m_pList; 160 }; 161 162 // range of WCHAR (inclusive) 163 struct RANGE 164 { 165 WCHAR from, to; 166 }; 167 168 //#define OUTPUT_TABLE // generate the table to analyze 169 170 #ifndef OUTPUT_TABLE 171 // comparison of two ranges 172 static int __cdecl RangeCompare(const void *x, const void *y) 173 { 174 const RANGE *a = (const RANGE *)x; 175 const RANGE *b = (const RANGE *)y; 176 if (a->to < b->from) 177 return -1; 178 if (b->to < a->from) 179 return 1; 180 return 0; 181 } 182 183 // is the WCHAR a word break? 184 static __inline BOOL IsWordBreak(WCHAR ch) 185 { 186 static const RANGE s_ranges[] = 187 { 188 { 0x0009, 0x0009 }, { 0x0020, 0x002f }, { 0x003a, 0x0040 }, { 0x005b, 0x0060 }, 189 { 0x007b, 0x007e }, { 0x00ab, 0x00ab }, { 0x00ad, 0x00ad }, { 0x00bb, 0x00bb }, 190 { 0x02c7, 0x02c7 }, { 0x02c9, 0x02c9 }, { 0x055d, 0x055d }, { 0x060c, 0x060c }, 191 { 0x2002, 0x200b }, { 0x2013, 0x2014 }, { 0x2016, 0x2016 }, { 0x2018, 0x2018 }, 192 { 0x201c, 0x201d }, { 0x2022, 0x2022 }, { 0x2025, 0x2027 }, { 0x2039, 0x203a }, 193 { 0x2045, 0x2046 }, { 0x207d, 0x207e }, { 0x208d, 0x208e }, { 0x226a, 0x226b }, 194 { 0x2574, 0x2574 }, { 0x3001, 0x3003 }, { 0x3005, 0x3005 }, { 0x3008, 0x3011 }, 195 { 0x3014, 0x301b }, { 0x301d, 0x301e }, { 0x3041, 0x3041 }, { 0x3043, 0x3043 }, 196 { 0x3045, 0x3045 }, { 0x3047, 0x3047 }, { 0x3049, 0x3049 }, { 0x3063, 0x3063 }, 197 { 0x3083, 0x3083 }, { 0x3085, 0x3085 }, { 0x3087, 0x3087 }, { 0x308e, 0x308e }, 198 { 0x309b, 0x309e }, { 0x30a1, 0x30a1 }, { 0x30a3, 0x30a3 }, { 0x30a5, 0x30a5 }, 199 { 0x30a7, 0x30a7 }, { 0x30a9, 0x30a9 }, { 0x30c3, 0x30c3 }, { 0x30e3, 0x30e3 }, 200 { 0x30e5, 0x30e5 }, { 0x30e7, 0x30e7 }, { 0x30ee, 0x30ee }, { 0x30f5, 0x30f6 }, 201 { 0x30fc, 0x30fe }, { 0xfd3e, 0xfd3f }, { 0xfe30, 0xfe31 }, { 0xfe33, 0xfe44 }, 202 { 0xfe4f, 0xfe51 }, { 0xfe59, 0xfe5e }, { 0xff08, 0xff09 }, { 0xff0c, 0xff0c }, 203 { 0xff0e, 0xff0e }, { 0xff1c, 0xff1c }, { 0xff1e, 0xff1e }, { 0xff3b, 0xff3b }, 204 { 0xff3d, 0xff3d }, { 0xff40, 0xff40 }, { 0xff5b, 0xff5e }, { 0xff61, 0xff64 }, 205 { 0xff67, 0xff70 }, { 0xff9e, 0xff9f }, { 0xffe9, 0xffe9 }, { 0xffeb, 0xffeb }, 206 }; 207 #ifndef NDEBUG 208 // check the table if first time 209 static BOOL s_bFirstTime = TRUE; 210 if (s_bFirstTime) 211 { 212 s_bFirstTime = FALSE; 213 for (UINT i = 0; i < _countof(s_ranges); ++i) 214 { 215 ATLASSERT(s_ranges[i].from <= s_ranges[i].to); 216 } 217 for (UINT i = 0; i + 1 < _countof(s_ranges); ++i) 218 { 219 ATLASSERT(s_ranges[i].to < s_ranges[i + 1].from); 220 } 221 } 222 #endif 223 RANGE range = { ch, ch }; 224 return !!bsearch(&range, s_ranges, _countof(s_ranges), sizeof(RANGE), RangeCompare); 225 } 226 #endif 227 228 static VOID DoWordBreakProc(EDITWORDBREAKPROC fn) 229 { 230 #ifdef OUTPUT_TABLE 231 // generate the table text 232 WORD wType1, wType2, wType3; 233 for (DWORD i = 0; i <= 0xFFFF; ++i) 234 { 235 WCHAR ch = (WCHAR)i; 236 GetStringTypeW(CT_CTYPE1, &ch, 1, &wType1); 237 GetStringTypeW(CT_CTYPE2, &ch, 1, &wType2); 238 GetStringTypeW(CT_CTYPE3, &ch, 1, &wType3); 239 BOOL b = fn(&ch, 0, 1, WB_ISDELIMITER); 240 trace("%u\t0x%04x\t0x%04x\t0x%04x\t0x%04x\n", b, wType1, wType2, wType3, ch); 241 } 242 #else 243 // check the word break procedure 244 for (DWORD i = 0; i <= 0xFFFF; ++i) 245 { 246 WCHAR ch = (WCHAR)i; 247 BOOL b1 = fn(&ch, 0, 1, WB_ISDELIMITER); 248 BOOL b2 = IsWordBreak(ch); 249 ok(b1 == b2, "ch:0x%04x, b1:%d, b2:%d\n", ch, b1, b2); 250 } 251 #endif 252 } 253 254 // the testcase A 255 static VOID 256 DoTestCaseA(INT x, INT y, INT cx, INT cy, LPCWSTR pszInput, 257 LPWSTR *pList, UINT nCount, BOOL bDowner, BOOL bLong) 258 { 259 MSG msg; 260 s_bExpand = s_bReset = FALSE; 261 262 // create EDIT control 263 HWND hwndEdit = MyCreateEditCtrl(x, y, cx, cy); 264 ok(hwndEdit != NULL, "hwndEdit was NULL\n"); 265 ShowWindowAsync(hwndEdit, SW_SHOWNORMAL); 266 267 // get word break procedure 268 EDITWORDBREAKPROC fn1 = 269 (EDITWORDBREAKPROC)SendMessageW(hwndEdit, EM_GETWORDBREAKPROC, 0, 0); 270 ok(fn1 == NULL, "fn1 was %p\n", fn1); 271 272 // set the list data 273 CComPtr<CEnumString> pEnum(new CEnumString()); 274 pEnum->SetList(nCount, pList); 275 276 // create auto-completion object 277 CComPtr<IAutoComplete2> pAC; 278 HRESULT hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, 279 IID_IAutoComplete2, (VOID **)&pAC); 280 ok_hr(hr, S_OK); 281 282 // enable auto-suggest 283 hr = pAC->SetOptions(ACO_AUTOSUGGEST); 284 ok_hr(hr, S_OK); 285 286 // initialize 287 IUnknown *punk = static_cast<IEnumString *>(pEnum); 288 hr = pAC->Init(hwndEdit, punk, NULL, NULL); // IAutoComplete::Init 289 ok_hr(hr, S_OK); 290 291 #ifdef MANUAL_DEBUGGING 292 trace("enter MANUAL_DEBUGGING...\n"); 293 trace("NOTE: You can quit EDIT control by Alt+F4.\n"); 294 while (GetMessageW(&msg, NULL, 0, 0)) 295 { 296 TranslateMessage(&msg); 297 DispatchMessageW(&msg); 298 if (!IsWindow(hwndEdit)) 299 break; 300 } 301 trace("leave MANUAL_DEBUGGING...\n"); 302 return; 303 #endif 304 305 // check expansion 306 ok_int(s_bExpand, FALSE); 307 // check reset 308 ok_int(s_bReset, FALSE); 309 310 // input 311 SetFocus(hwndEdit); 312 WCHAR chLast = 0; 313 for (UINT i = 0; pszInput[i]; ++i) 314 { 315 PostMessageW(hwndEdit, WM_CHAR, pszInput[i], 0); 316 chLast = pszInput[i]; 317 } 318 319 // wait for hwndDropDown 320 DWORD style, exstyle; 321 HWND hwndDropDown; 322 LONG_PTR id; 323 for (INT i = 0; i < 100; ++i) 324 { 325 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) 326 { 327 TranslateMessage(&msg); 328 DispatchMessageW(&msg); 329 } 330 hwndDropDown = FindWindowW(L"Auto-Suggest Dropdown", L""); 331 if (IsWindowVisible(hwndDropDown)) 332 break; 333 Sleep(100); 334 } 335 ok(hwndDropDown != NULL, "hwndDropDown was NULL\n"); 336 ok_int(IsWindowVisible(hwndDropDown), TRUE); 337 338 // check word break procedure 339 static BOOL s_bFirstTime = TRUE; 340 if (s_bFirstTime) 341 { 342 s_bFirstTime = FALSE; 343 EDITWORDBREAKPROC fn2 = 344 (EDITWORDBREAKPROC)SendMessageW(hwndEdit, EM_GETWORDBREAKPROC, 0, 0); 345 ok(fn1 != fn2, "fn1 == fn2\n"); 346 ok(fn2 != NULL, "fn2 was NULL\n"); 347 if (fn2) 348 DoWordBreakProc(fn2); 349 else 350 skip("fn2 == NULL\n"); 351 } 352 353 // take care of the message queue 354 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) 355 { 356 TranslateMessage(&msg); 357 DispatchMessageW(&msg); 358 } 359 360 // check reset 361 ok_int(s_bReset, TRUE); 362 363 // get sizes and positions 364 RECT rcEdit, rcDropDown; 365 GetWindowRect(hwndEdit, &rcEdit); 366 GetWindowRect(hwndDropDown, &rcDropDown); 367 trace("rcEdit: (%ld, %ld, %ld, %ld)\n", rcEdit.left, rcEdit.top, rcEdit.right, rcEdit.bottom); 368 trace("rcDropDown: (%ld, %ld, %ld, %ld)\n", rcDropDown.left, rcDropDown.top, rcDropDown.right, rcDropDown.bottom); 369 370 // is it "downer"? 371 ok_int(bDowner, rcEdit.top < rcDropDown.top); 372 373 // check window style and id 374 style = (LONG)GetWindowLongPtrW(hwndDropDown, GWL_STYLE); 375 exstyle = (LONG)GetWindowLongPtrW(hwndDropDown, GWL_EXSTYLE); 376 id = GetWindowLongPtrW(hwndDropDown, GWLP_ID); 377 #define DROPDOWN_STYLE (WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | \ 378 WS_CLIPCHILDREN | WS_BORDER) // 0x96800000 379 ok(style == DROPDOWN_STYLE, "style was 0x%08lx\n", style); 380 #define DROPDOWN_EX_STYLE (WS_EX_TOOLWINDOW | WS_EX_TOPMOST | \ 381 WS_EX_NOPARENTNOTIFY) // 0x8c 382 ok_long(exstyle, DROPDOWN_EX_STYLE); 383 ok_long((LONG)id, 0); 384 385 // check class style 386 style = (LONG)GetClassLongPtrW(hwndDropDown, GCL_STYLE); 387 #define DROPDOWN_CLASS_STYLE_1 (CS_DROPSHADOW | CS_SAVEBITS) 388 #define DROPDOWN_CLASS_STYLE_2 0 389 ok(style == DROPDOWN_CLASS_STYLE_1 /* Win10 */ || 390 style == DROPDOWN_CLASS_STYLE_2 /* WinXP/Win2k3 */, 391 "style was 0x%08lx\n", style); 392 393 // get client rectangle 394 RECT rcClient; 395 GetClientRect(hwndDropDown, &rcClient); 396 trace("rcClient: (%ld, %ld, %ld, %ld)\n", 397 rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); 398 399 HWND hwndScrollBar, hwndSizeBox, hwndList, hwndNone; 400 WCHAR szClass[64]; 401 402 // scroll bar 403 hwndScrollBar = GetTopWindow(hwndDropDown); 404 ok(hwndScrollBar != NULL, "hwndScrollBar was NULL\n"); 405 GetClassNameW(hwndScrollBar, szClass, _countof(szClass)); 406 ok_wstri(szClass, L"ScrollBar"); 407 style = (LONG)GetWindowLongPtrW(hwndScrollBar, GWL_STYLE); 408 exstyle = (LONG)GetWindowLongPtrW(hwndScrollBar, GWL_EXSTYLE); 409 id = GetWindowLongPtrW(hwndScrollBar, GWLP_ID); 410 #define SCROLLBAR_STYLE_1 (WS_CHILD | WS_VISIBLE | SBS_BOTTOMALIGN | SBS_VERT) // 0x50000005 411 #define SCROLLBAR_STYLE_2 (WS_CHILD | SBS_BOTTOMALIGN | SBS_VERT) // 0x40000005 412 if (bLong) 413 ok(style == SCROLLBAR_STYLE_1, "style was 0x%08lx\n", style); 414 else 415 ok(style == SCROLLBAR_STYLE_2, "style was 0x%08lx\n", style); 416 ok_long(exstyle, 0); 417 ok_long((LONG)id, 0); 418 419 // size-box 420 hwndSizeBox = GetNextWindow(hwndScrollBar, GW_HWNDNEXT); 421 ok(hwndSizeBox != NULL, "hwndSizeBox was NULL\n"); 422 GetClassNameW(hwndSizeBox, szClass, _countof(szClass)); 423 ok_wstri(szClass, L"ScrollBar"); 424 style = (LONG)GetWindowLongPtrW(hwndSizeBox, GWL_STYLE); 425 exstyle = (LONG)GetWindowLongPtrW(hwndSizeBox, GWL_EXSTYLE); 426 id = GetWindowLongPtrW(hwndSizeBox, GWLP_ID); 427 #define SIZEBOX_STYLE_1 \ 428 (WS_CHILD | WS_VISIBLE | SBS_SIZEBOX | SBS_SIZEBOXBOTTOMRIGHTALIGN) // 0x5000000c 429 #define SIZEBOX_STYLE_2 \ 430 (WS_CHILD | WS_VISIBLE | SBS_SIZEBOX) // 0x50000008 431 ok(style == SIZEBOX_STYLE_1 /* Win10 */ || 432 style == SIZEBOX_STYLE_2 /* Win2k3/WinXP */, "style was 0x%08lx\n", style); 433 ok_long(exstyle, 0); 434 ok_long((LONG)id, 0); 435 436 // the list 437 hwndList = GetNextWindow(hwndSizeBox, GW_HWNDNEXT); 438 ok(hwndList != NULL, "hwndList was NULL\n"); 439 GetClassNameW(hwndList, szClass, _countof(szClass)); 440 ok_wstri(szClass, WC_LISTVIEWW); // L"SysListView32" 441 style = (LONG)GetWindowLongPtrW(hwndList, GWL_STYLE); 442 exstyle = (LONG)GetWindowLongPtrW(hwndList, GWL_EXSTYLE); 443 id = GetWindowLongPtrW(hwndList, GWLP_ID); 444 #define LIST_STYLE_1 \ 445 (WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_CLIPSIBLINGS | \ 446 LVS_NOCOLUMNHEADER | LVS_OWNERDATA | LVS_OWNERDRAWFIXED | \ 447 LVS_SINGLESEL | LVS_REPORT) // 0x54205405 448 #define LIST_STYLE_2 \ 449 (WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | LVS_NOCOLUMNHEADER | \ 450 LVS_OWNERDATA | LVS_OWNERDRAWFIXED | LVS_SINGLESEL | LVS_REPORT) // 0x54005405 451 if (bLong) 452 (void)0; // ok(style == LIST_STYLE_1, "style was 0x%08lx\n", style); broken on Windows 453 else 454 ok(style == LIST_STYLE_2, "style was 0x%08lx\n", style); 455 ok_long(exstyle, 0); 456 ok_long((LONG)id, 0); 457 #define LIST_EXTENDED_LV_STYLE_1 \ 458 (LVS_EX_DOUBLEBUFFER | LVS_EX_ONECLICKACTIVATE | \ 459 LVS_EX_FULLROWSELECT | LVS_EX_TRACKSELECT) // 0x10068 460 #define LIST_EXTENDED_LV_STYLE_2 \ 461 (LVS_EX_ONECLICKACTIVATE | LVS_EX_FULLROWSELECT | \ 462 LVS_EX_TRACKSELECT) // 0x68 463 exstyle = ListView_GetExtendedListViewStyle(hwndList); 464 ok(exstyle == LIST_EXTENDED_LV_STYLE_1 /* Win10 */ || 465 exstyle == LIST_EXTENDED_LV_STYLE_2 /* WinXP/Win2k3 */, 466 "exstyle was 0x%08lx\n", exstyle); 467 468 // no more controls 469 hwndNone = GetNextWindow(hwndList, GW_HWNDNEXT); 470 ok(hwndNone == NULL, "hwndNone was %p\n", hwndNone); 471 472 // get rectangles of controls 473 RECT rcScrollBar, rcSizeBox, rcList; 474 GetWindowRect(hwndScrollBar, &rcScrollBar); 475 MapWindowPoints(NULL, hwndDropDown, (LPPOINT)&rcScrollBar, 2); 476 GetWindowRect(hwndSizeBox, &rcSizeBox); 477 MapWindowPoints(NULL, hwndDropDown, (LPPOINT)&rcSizeBox, 2); 478 GetWindowRect(hwndList, &rcList); 479 MapWindowPoints(NULL, hwndDropDown, (LPPOINT)&rcList, 2); 480 trace("rcScrollBar: (%ld, %ld, %ld, %ld)\n", rcScrollBar.left, rcScrollBar.top, 481 rcScrollBar.right, rcScrollBar.bottom); 482 trace("rcSizeBox: (%ld, %ld, %ld, %ld)\n", rcSizeBox.left, rcSizeBox.top, 483 rcSizeBox.right, rcSizeBox.bottom); 484 trace("rcList: (%ld, %ld, %ld, %ld)\n", rcList.left, rcList.top, 485 rcList.right, rcList.bottom); 486 487 // are they visible? 488 ok_int(IsWindowVisible(hwndDropDown), TRUE); 489 ok_int(IsWindowVisible(hwndEdit), TRUE); 490 ok_int(IsWindowVisible(hwndSizeBox), TRUE); 491 ok_int(IsWindowVisible(hwndList), TRUE); 492 493 // check item count 494 INT nListCount = ListView_GetItemCount(hwndList); 495 if (nListCount < 1000) 496 (void)0; // ok_int(nListCount, nCount); broken on Windows 497 else 498 ok_int(nListCount, 1000); 499 500 // check the positions 501 if (bDowner) // downer 502 { 503 ok_int(rcDropDown.left, rcEdit.left); 504 ok_int(rcDropDown.top, rcEdit.bottom); 505 ok_int(rcDropDown.right, rcEdit.right); 506 //ok_int(rcDropDown.bottom, ???); 507 ok_int(rcSizeBox.left, rcClient.right - GetSystemMetrics(SM_CXVSCROLL)); 508 ok_int(rcSizeBox.top, rcClient.bottom - GetSystemMetrics(SM_CYHSCROLL)); 509 ok_int(rcSizeBox.right, rcClient.right); 510 ok_int(rcSizeBox.bottom, rcClient.bottom); 511 ok_int(rcScrollBar.left, rcClient.right - GetSystemMetrics(SM_CXVSCROLL)); 512 ok_int(rcScrollBar.top, 0); 513 ok_int(rcScrollBar.right, rcClient.right); 514 ok_int(rcScrollBar.bottom, rcClient.bottom - GetSystemMetrics(SM_CYHSCROLL)); 515 ok_int(rcList.left, 0); 516 ok_int(rcList.top, 0); 517 //ok_int(rcList.right, 30160 or 30170???); 518 ok_int(rcList.bottom, rcClient.bottom); 519 } 520 else // upper 521 { 522 ok_int(rcDropDown.left, rcEdit.left); 523 //ok_int(rcDropDown.top, ???); 524 ok_int(rcDropDown.right, rcEdit.right); 525 ok_int(rcDropDown.bottom, rcEdit.top); 526 ok_int(rcSizeBox.left, rcClient.right - GetSystemMetrics(SM_CXVSCROLL)); 527 ok_int(rcSizeBox.top, 0); 528 ok_int(rcSizeBox.right, rcClient.right); 529 ok_int(rcSizeBox.bottom, rcClient.top + GetSystemMetrics(SM_CYHSCROLL)); 530 ok_int(rcScrollBar.left, rcClient.right - GetSystemMetrics(SM_CXVSCROLL)); 531 ok_int(rcScrollBar.top, GetSystemMetrics(SM_CYHSCROLL)); 532 ok_int(rcScrollBar.right, rcClient.right); 533 ok_int(rcScrollBar.bottom, rcClient.bottom); 534 ok_int(rcList.left, 0); 535 ok_int(rcList.top, 0); 536 //ok_int(rcList.right, 30160 or 30170???); 537 ok_int(rcList.bottom, rcClient.bottom); 538 } 539 540 // append WM_QUIT message into message queue 541 PostQuitMessage(0); 542 543 // do the messages 544 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) 545 { 546 TranslateMessage(&msg); 547 DispatchMessageW(&msg); 548 Sleep(30); // another thread is working... 549 } 550 551 // destroy the EDIT control and drop-down window 552 DestroyWindow(hwndEdit); 553 DestroyWindow(hwndDropDown); 554 555 // do the messages 556 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) 557 { 558 TranslateMessage(&msg); 559 DispatchMessageW(&msg); 560 } 561 562 // check the expansion 563 ok_int(s_bExpand, chLast == L'\\'); 564 } 565 566 struct TEST_B_ENTRY 567 { 568 INT m_line; 569 UINT m_uMsg; 570 WPARAM m_wParam; 571 LPARAM m_lParam; 572 LPCWSTR m_text; 573 BOOL m_bVisible; 574 INT m_ich0, m_ich1; 575 INT m_iItem; 576 BOOL m_bReset, m_bExpand; 577 LPCWSTR m_expand; 578 }; 579 580 static BOOL 581 DoesMatch(LPWSTR *pList, UINT nCount, LPCWSTR psz) 582 { 583 INT cch = lstrlenW(psz); 584 if (cch == 0) 585 return FALSE; 586 for (UINT i = 0; i < nCount; ++i) 587 { 588 if (::StrCmpNIW(pList[i], psz, cch) == 0) 589 return TRUE; 590 } 591 return FALSE; 592 } 593 594 // the testcase B 595 static VOID 596 DoTestCaseB(INT x, INT y, INT cx, INT cy, LPWSTR *pList, UINT nCount, 597 const TEST_B_ENTRY *pEntries, UINT cEntries) 598 { 599 MSG msg; 600 s_bExpand = s_bReset = FALSE; 601 602 // create EDIT control 603 HWND hwndEdit = MyCreateEditCtrl(x, y, cx, cy); 604 ok(hwndEdit != NULL, "hwndEdit was NULL\n"); 605 ShowWindowAsync(hwndEdit, SW_SHOWNORMAL); 606 607 // do messages 608 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) 609 { 610 TranslateMessage(&msg); 611 DispatchMessageW(&msg); 612 } 613 ok_int(IsWindowVisible(hwndEdit), TRUE); 614 615 // set the list data 616 CComPtr<CEnumString> pEnum(new CEnumString()); 617 pEnum->SetList(nCount, pList); 618 619 // create auto-completion object 620 CComPtr<IAutoComplete2> pAC; 621 HRESULT hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, 622 IID_IAutoComplete2, (VOID **)&pAC); 623 ok_hr(hr, S_OK); 624 625 // enable auto-suggest 626 hr = pAC->SetOptions(ACO_AUTOSUGGEST); 627 ok_hr(hr, S_OK); 628 629 // initialize 630 IUnknown *punk = static_cast<IEnumString *>(pEnum); 631 hr = pAC->Init(hwndEdit, punk, NULL, NULL); // IAutoComplete::Init 632 ok_hr(hr, S_OK); 633 634 HWND hwndDropDown = NULL, hwndScrollBar = NULL, hwndSizeBox = NULL, hwndList = NULL; 635 // input 636 SetFocus(hwndEdit); 637 Sleep(100); 638 639 for (UINT i = 0; i < cEntries; ++i) 640 { 641 const TEST_B_ENTRY& entry = pEntries[i]; 642 s_bReset = s_bExpand = FALSE; 643 s_strExpand.Empty(); 644 645 UINT uMsg = entry.m_uMsg; 646 WPARAM wParam = entry.m_wParam; 647 LPARAM lParam = entry.m_lParam; 648 PostMessageW(hwndEdit, uMsg, wParam, lParam); 649 650 while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) 651 { 652 TranslateMessage(&msg); 653 DispatchMessageW(&msg); 654 655 if (msg.message == WM_CHAR || msg.message == WM_KEYDOWN) 656 Sleep(100); // another thread is working 657 } 658 659 if (!IsWindow(hwndDropDown)) 660 { 661 hwndDropDown = FindWindowW(L"Auto-Suggest Dropdown", L""); 662 hwndScrollBar = hwndSizeBox = hwndList = NULL; 663 } 664 if (IsWindowVisible(hwndDropDown)) 665 { 666 hwndScrollBar = GetTopWindow(hwndDropDown); 667 hwndSizeBox = GetNextWindow(hwndScrollBar, GW_HWNDNEXT); 668 hwndList = GetNextWindow(hwndSizeBox, GW_HWNDNEXT); 669 } 670 671 WCHAR szText[64]; 672 GetWindowTextW(hwndEdit, szText, _countof(szText)); 673 CStringW strText = szText; 674 INT ich0, ich1; 675 SendMessageW(hwndEdit, EM_GETSEL, (WPARAM)&ich0, (LPARAM)&ich1); 676 677 BOOL bVisible = IsWindowVisible(hwndDropDown); 678 ok(bVisible == entry.m_bVisible, "Line %d: bVisible was %d\n", entry.m_line, bVisible); 679 INT iItem = -1; 680 if (IsWindowVisible(hwndList)) 681 { 682 iItem = ListView_GetNextItem(hwndList, -1, LVNI_ALL | LVNI_SELECTED); 683 } 684 BOOL bDidMatch = DoesMatch(pList, nCount, strText); 685 ok(bVisible == bDidMatch, "Line %d: bVisible != bDidMatch\n", entry.m_line); 686 687 if (entry.m_text) 688 ok(strText == entry.m_text, "Line %d: szText was %S\n", entry.m_line, (LPCWSTR)strText); 689 else 690 ok(strText == L"", "Line %d: szText was %S\n", entry.m_line, (LPCWSTR)strText); 691 ok(ich0 == entry.m_ich0, "Line %d: ich0 was %d\n", entry.m_line, ich0); 692 ok(ich1 == entry.m_ich1, "Line %d: ich1 was %d\n", entry.m_line, ich1); 693 ok(iItem == entry.m_iItem, "Line %d: iItem was %d\n", entry.m_line, iItem); 694 ok(s_bReset == entry.m_bReset, "Line %d: s_bReset was %d\n", entry.m_line, s_bReset); 695 ok(s_bExpand == entry.m_bExpand, "Line %d: s_bExpand was %d\n", entry.m_line, s_bExpand); 696 if (entry.m_expand) 697 ok(s_strExpand == entry.m_expand, "Line %d: s_strExpand was %S\n", entry.m_line, (LPCWSTR)s_strExpand); 698 else 699 ok(s_strExpand == L"", "Line %d: s_strExpand was %S\n", entry.m_line, (LPCWSTR)s_strExpand); 700 } 701 702 DestroyWindow(hwndEdit); 703 } 704 705 START_TEST(IAutoComplete) 706 { 707 // initialize COM 708 CCoInit init; 709 ok_hr(init.hr, S_OK); 710 if (!SUCCEEDED(init.hr)) 711 { 712 skip("CoInitialize failed\n"); 713 return; 714 } 715 716 // get screen size 717 HMONITOR hMon = MonitorFromWindow(GetDesktopWindow(), MONITOR_DEFAULTTOPRIMARY); 718 MONITORINFO mi = { sizeof(mi) }; 719 GetMonitorInfoW(hMon, &mi); 720 const RECT& rcWork = mi.rcWork; 721 trace("rcWork: (%ld, %ld, %ld, %ld)\n", 722 rcWork.left, rcWork.top, rcWork.right, rcWork.bottom); 723 trace("SM_CXVSCROLL: %d, SM_CYHSCROLL: %d\n", 724 GetSystemMetrics(SM_CXVSCROLL), GetSystemMetrics(SM_CYHSCROLL)); 725 726 UINT nCount; 727 LPWSTR *pList; 728 WCHAR szText[64]; 729 730 // Test case #1 (A) 731 trace("Testcase #1 (downer, short) ------------------------------\n"); 732 nCount = 3; 733 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR)); 734 SHStrDupW(L"test\\AA", &pList[0]); 735 SHStrDupW(L"test\\BBB", &pList[1]); 736 SHStrDupW(L"test\\CCC", &pList[2]); 737 DoTestCaseA(0, 0, 100, 30, L"test\\", pList, nCount, TRUE, FALSE); 738 739 // Test case #2 (A) 740 trace("Testcase #2 (downer, long) ------------------------------\n"); 741 nCount = 300; 742 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR)); 743 for (UINT i = 0; i < nCount; ++i) 744 { 745 StringCbPrintfW(szText, sizeof(szText), L"test\\%u", i); 746 SHStrDupW(szText, &pList[i]); 747 } 748 DoTestCaseA(100, 20, 100, 30, L"test\\", pList, nCount, TRUE, TRUE); 749 750 // Test case #3 (A) 751 trace("Testcase #3 (upper, short) ------------------------------\n"); 752 nCount = 2; 753 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR)); 754 SHStrDupW(L"test/AA", &pList[0]); 755 SHStrDupW(L"test/BBB", &pList[0]); 756 SHStrDupW(L"test/CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", &pList[1]); 757 DoTestCaseA(rcWork.right - 100, rcWork.bottom - 30, 80, 40, L"test/", 758 pList, nCount, FALSE, FALSE); 759 760 // Test case #4 (A) 761 trace("Testcase #4 (upper, short) ------------------------------\n"); 762 nCount = 2; 763 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR)); 764 SHStrDupW(L"testtest\\AA", &pList[0]); 765 SHStrDupW(L"testtest\\BBB", &pList[0]); 766 SHStrDupW(L"testtest\\CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", &pList[1]); 767 DoTestCaseA(rcWork.right - 100, rcWork.bottom - 30, 80, 40, L"testtest\\", 768 pList, nCount, FALSE, FALSE); 769 770 #ifdef MANUAL_DEBUGGING 771 return; 772 #endif 773 774 // Test case #5 (A) 775 trace("Testcase #5 (upper, long) ------------------------------\n"); 776 nCount = 300; 777 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR)); 778 for (UINT i = 0; i < nCount; ++i) 779 { 780 StringCbPrintfW(szText, sizeof(szText), L"testtest/%u", i); 781 SHStrDupW(szText, &pList[i]); 782 } 783 DoTestCaseA(0, rcWork.bottom - 30, 80, 30, L"testtest/", pList, nCount, FALSE, TRUE); 784 785 // Test case #6 (A) 786 trace("Testcase #6 (upper, long) ------------------------------\n"); 787 nCount = 2000; 788 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR)); 789 for (UINT i = 0; i < nCount; ++i) 790 { 791 StringCbPrintfW(szText, sizeof(szText), L"testtest\\item-%u", i); 792 SHStrDupW(szText, &pList[i]); 793 } 794 DoTestCaseA(0, rcWork.bottom - 30, 80, 40, L"testtest\\", pList, nCount, FALSE, TRUE); 795 796 // Test case #7 (B) 797 trace("Testcase #7 ------------------------------\n"); 798 nCount = 16; 799 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR)); 800 for (UINT i = 0; i < nCount; ++i) 801 { 802 StringCbPrintfW(szText, sizeof(szText), L"test\\item-%u", i); 803 SHStrDupW(szText, &pList[i]); 804 } 805 static const TEST_B_ENTRY testcase7_entries[] = 806 { 807 { __LINE__, WM_CHAR, L't', 0, L"t", TRUE, 1, 1, -1, TRUE }, 808 { __LINE__, WM_CHAR, L'e', 0, L"te", TRUE, 2, 2, -1 }, 809 { __LINE__, WM_CHAR, L's', 0, L"tes", TRUE, 3, 3, -1 }, 810 { __LINE__, WM_CHAR, L'T', 0, L"tesT", TRUE, 4, 4, -1 }, 811 { __LINE__, WM_CHAR, L'\\', 0, L"tesT\\", TRUE, 5, 5, -1, TRUE, TRUE, L"tesT\\" }, 812 { __LINE__, WM_CHAR, L't', 0, L"tesT\\t", FALSE, 6, 6, -1 }, 813 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"tesT\\", TRUE, 5, 5, -1 }, 814 { __LINE__, WM_CHAR, L'i', 0, L"tesT\\i", TRUE, 6, 6, -1 }, 815 { __LINE__, WM_KEYDOWN, VK_DOWN, 0, L"test\\item-0", TRUE, 11, 11, 0 }, 816 { __LINE__, WM_KEYDOWN, VK_DOWN, 0, L"test\\item-1", TRUE, 11, 11, 1 }, 817 { __LINE__, WM_KEYDOWN, VK_UP, 0, L"test\\item-0", TRUE, 11, 11, 0 }, 818 { __LINE__, WM_KEYDOWN, VK_UP, 0, L"tesT\\i", TRUE, 6, 6, -1 }, 819 { __LINE__, WM_KEYDOWN, VK_UP, 0, L"test\\item-9", TRUE, 11, 11, 15 }, 820 { __LINE__, WM_KEYDOWN, VK_DOWN, 0, L"tesT\\i", TRUE, 6, 6, -1 }, 821 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"tesT\\", TRUE, 5, 5, -1 }, 822 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"tesT", TRUE, 4, 4, -1, TRUE }, 823 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"tes", TRUE, 3, 3, -1 }, 824 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"te", TRUE, 2, 2, -1 }, 825 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"t", TRUE, 1, 1, -1 }, 826 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"", FALSE, 0, 0, -1 }, 827 { __LINE__, WM_CHAR, L't', 0, L"t", TRUE, 1, 1, -1, TRUE }, 828 { __LINE__, WM_KEYDOWN, VK_LEFT, 0, L"t", TRUE, 0, 0, -1 }, 829 { __LINE__, WM_KEYDOWN, VK_RIGHT, 0, L"t", TRUE, 1, 1, -1 }, 830 }; 831 DoTestCaseB(0, 0, 100, 30, pList, nCount, testcase7_entries, _countof(testcase7_entries)); 832 833 // Test case #8 (B) 834 trace("Testcase #8 ------------------------------\n"); 835 nCount = 10; 836 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR)); 837 for (UINT i = 0; i < nCount; ++i) 838 { 839 StringCbPrintfW(szText, sizeof(szText), L"test\\item-%u", i); 840 SHStrDupW(szText, &pList[i]); 841 } 842 static const TEST_B_ENTRY testcase8_entries[] = 843 { 844 { __LINE__, WM_CHAR, L'a', 0, L"a", FALSE, 1, 1, -1, TRUE }, 845 { __LINE__, WM_CHAR, L'b', 0, L"ab", FALSE, 2, 2, -1 }, 846 { __LINE__, WM_KEYDOWN, VK_LEFT, 0, L"ab", FALSE, 1, 1, -1 }, 847 { __LINE__, WM_KEYDOWN, VK_LEFT, 0, L"ab", FALSE, 0, 0, -1 }, 848 { __LINE__, WM_KEYDOWN, VK_DELETE, 0, L"b", FALSE, 0, 0, -1, TRUE }, 849 { __LINE__, WM_CHAR, L't', 0, L"tb", FALSE, 1, 1, -1, TRUE }, 850 { __LINE__, WM_CHAR, L'e', 0, L"teb", FALSE, 2, 2, -1 }, 851 { __LINE__, WM_CHAR, L's', 0, L"tesb", FALSE, 3, 3, -1 }, 852 { __LINE__, WM_CHAR, L't', 0, L"testb", FALSE, 4, 4, -1 }, 853 { __LINE__, WM_KEYDOWN, VK_DELETE, 0, L"test", TRUE, 4, 4, -1 }, 854 { __LINE__, WM_CHAR, L'\\', 0, L"test\\", TRUE, 5, 5, -1, TRUE, TRUE, L"test\\" }, 855 { __LINE__, WM_CHAR, L'i', 0, L"test\\i", TRUE, 6, 6, -1 }, 856 { __LINE__, WM_KEYDOWN, VK_DELETE, 0, L"test\\i", TRUE, 6, 6, -1 }, 857 { __LINE__, WM_CHAR, L'z', 0, L"test\\iz", FALSE, 7, 7, -1 }, 858 { __LINE__, WM_KEYDOWN, VK_LEFT, 0, L"test\\iz", FALSE, 6, 6, -1 }, 859 { __LINE__, WM_KEYDOWN, VK_LEFT, 0, L"test\\iz", FALSE, 5, 5, -1 }, 860 { __LINE__, WM_KEYDOWN, VK_DELETE, 0, L"test\\z", FALSE, 5, 5, -1 }, 861 { __LINE__, WM_KEYDOWN, VK_DELETE, 0, L"test\\", TRUE, 5, 5, -1 }, 862 { __LINE__, WM_KEYDOWN, VK_LEFT, 0, L"test\\", TRUE, 4, 4, -1 }, 863 { __LINE__, WM_KEYDOWN, VK_LEFT, 0, L"test\\", TRUE, 3, 3, -1 }, 864 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"tet\\", FALSE, 2, 2, -1, TRUE, TRUE, L"tet\\" }, 865 { __LINE__, WM_KEYDOWN, VK_DELETE, 0, L"te\\", FALSE, 2, 2, -1, TRUE, TRUE, L"te\\" }, 866 { __LINE__, WM_KEYDOWN, VK_DELETE, 0, L"te", TRUE, 2, 2, -1, TRUE }, 867 }; 868 DoTestCaseB(rcWork.right - 100, rcWork.bottom - 30, 80, 40, pList, nCount, testcase8_entries, _countof(testcase8_entries)); 869 870 // Test case #9 (B) 871 trace("Testcase #9 ------------------------------\n"); 872 nCount = 32; 873 pList = (LPWSTR *)CoTaskMemAlloc(nCount * sizeof(LPWSTR)); 874 for (UINT i = 0; i < nCount; ++i) 875 { 876 StringCbPrintfW(szText, sizeof(szText), L"a%u\\b%u\\c%u", i % 2, (i / 2) % 2, i % 4); 877 SHStrDupW(szText, &pList[i]); 878 } 879 static const TEST_B_ENTRY testcase9_entries[] = 880 { 881 { __LINE__, WM_CHAR, L'a', 0, L"a", TRUE, 1, 1, -1, TRUE }, 882 { __LINE__, WM_CHAR, L'0', 0, L"a0", TRUE, 2, 2, -1 }, 883 { __LINE__, WM_CHAR, L'\\', 0, L"a0\\", TRUE, 3, 3, -1, TRUE, TRUE, L"a0\\" }, 884 { __LINE__, WM_CHAR, L'b', 0, L"a0\\b", TRUE, 4, 4, -1 }, 885 { __LINE__, WM_CHAR, L'1', 0, L"a0\\b1", TRUE, 5, 5, -1 }, 886 { __LINE__, WM_CHAR, L'\\', 0, L"a0\\b1\\", TRUE, 6, 6, -1, TRUE, TRUE, L"a0\\b1\\" }, 887 { __LINE__, WM_CHAR, L'c', 0, L"a0\\b1\\c", TRUE, 7, 7, -1 }, 888 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"a0\\b1\\", TRUE, 6, 6, -1 }, 889 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"a0\\b1", TRUE, 5, 5, -1, TRUE, TRUE, L"a0\\" }, 890 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"a0\\b", TRUE, 4, 4, -1 }, 891 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"a0\\", TRUE, 3, 3, -1 }, 892 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"a0", TRUE, 2, 2, -1, TRUE }, 893 { __LINE__, WM_KEYDOWN, VK_BACK, 0, L"a", TRUE, 1, 1, -1 }, 894 { __LINE__, WM_KEYDOWN, VK_DOWN, 0, L"a0\\b0\\c0", TRUE, 8, 8, 0 }, 895 { __LINE__, WM_KEYDOWN, VK_UP, 0, L"a", TRUE, 1, 1, -1 }, 896 }; 897 DoTestCaseB(0, 0, 100, 30, pList, nCount, testcase9_entries, _countof(testcase9_entries)); 898 } 899