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