1 /*
2  *    AutoComplete interfaces implementation.
3  *
4  *    Copyright 2004    Maxime Belleng� <maxime.bellenge@laposte.net>
5  *    Copyright 2009  Andrew Hill
6  *    Copyright 2020  Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 /*
24   Implemented:
25   - ACO_AUTOAPPEND style
26   - ACO_AUTOSUGGEST style
27   - ACO_UPDOWNKEYDROPSLIST style
28 
29   - Handle pwzsRegKeyPath and pwszQuickComplete in Init
30 
31   TODO:
32   - implement ACO_SEARCH style
33   - implement ACO_FILTERPREFIXES style
34   - implement ACO_USETAB style
35   - implement ACO_RTLREADING style
36 
37  */
38 
39 #include "precomp.h"
40 
41 static const WCHAR autocomplete_propertyW[] = {'W','i','n','e',' ','A','u','t','o',
42                                                'c','o','m','p','l','e','t','e',' ',
43                                                'c','o','n','t','r','o','l',0};
44 
45 /**************************************************************************
46  *  IAutoComplete_Constructor
47  */
48 CAutoComplete::CAutoComplete()
49 {
50     enabled = TRUE;
51     initialized = FALSE;
52     options = ACO_AUTOAPPEND;
53     wpOrigEditProc = NULL;
54     hwndListBox = NULL;
55     txtbackup = NULL;
56     quickComplete = NULL;
57     hwndEdit = NULL;
58     wpOrigLBoxProc = NULL;
59 }
60 
61 /**************************************************************************
62  *  IAutoComplete_Destructor
63  */
64 CAutoComplete::~CAutoComplete()
65 {
66     TRACE(" destroying IAutoComplete(%p)\n", this);
67     HeapFree(GetProcessHeap(), 0, quickComplete);
68     HeapFree(GetProcessHeap(), 0, txtbackup);
69     if (wpOrigEditProc)
70     {
71         SetWindowLongPtrW(hwndEdit, GWLP_WNDPROC, (LONG_PTR)wpOrigEditProc);
72         RemovePropW(hwndEdit, autocomplete_propertyW);
73     }
74     if (hwndListBox)
75         DestroyWindow(hwndListBox);
76 }
77 
78 /******************************************************************************
79  * IAutoComplete_fnEnable
80  */
81 HRESULT WINAPI CAutoComplete::Enable(BOOL fEnable)
82 {
83     HRESULT hr = S_OK;
84 
85     TRACE("(%p)->(%s)\n", this, (fEnable) ? "true" : "false");
86 
87     enabled = fEnable;
88 
89     return hr;
90 }
91 
92 /******************************************************************************
93  * create_listbox
94  */
95 void CAutoComplete::CreateListbox()
96 {
97     HWND hwndParent = GetParent(hwndEdit);
98 
99     /* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */
100     hwndListBox = CreateWindowExW(0, WC_LISTBOXW, NULL,
101                                   WS_BORDER | WS_CHILD | WS_VSCROLL | LBS_HASSTRINGS | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT,
102                                   CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
103                                   hwndParent, NULL,
104                                   (HINSTANCE)GetWindowLongPtrW(hwndParent, GWLP_HINSTANCE), NULL);
105 
106     if (hwndListBox)
107     {
108         wpOrigLBoxProc = (WNDPROC)SetWindowLongPtrW(hwndListBox, GWLP_WNDPROC, (LONG_PTR)ACLBoxSubclassProc);
109         SetWindowLongPtrW(hwndListBox, GWLP_USERDATA, (LONG_PTR)this);
110     }
111 }
112 
113 
114 /******************************************************************************
115  * IAutoComplete_fnInit
116  */
117 HRESULT WINAPI CAutoComplete::Init(HWND hwndEdit, IUnknown *punkACL, LPCOLESTR pwzsRegKeyPath, LPCOLESTR pwszQuickComplete)
118 {
119     TRACE("(%p)->(0x%08lx, %p, %s, %s)\n",
120       this, hwndEdit, punkACL, debugstr_w(pwzsRegKeyPath), debugstr_w(pwszQuickComplete));
121 
122     if (options & ACO_AUTOSUGGEST)
123         TRACE(" ACO_AUTOSUGGEST\n");
124     if (options & ACO_AUTOAPPEND)
125         TRACE(" ACO_AUTOAPPEND\n");
126     if (options & ACO_SEARCH)
127         FIXME(" ACO_SEARCH not supported\n");
128     if (options & ACO_FILTERPREFIXES)
129         FIXME(" ACO_FILTERPREFIXES not supported\n");
130     if (options & ACO_USETAB)
131         FIXME(" ACO_USETAB not supported\n");
132     if (options & ACO_UPDOWNKEYDROPSLIST)
133         TRACE(" ACO_UPDOWNKEYDROPSLIST\n");
134     if (options & ACO_RTLREADING)
135         FIXME(" ACO_RTLREADING not supported\n");
136 
137     if (!hwndEdit || !punkACL)
138         return E_INVALIDARG;
139 
140     if (this->initialized)
141     {
142         WARN("Autocompletion object is already initialized\n");
143         /* This->hwndEdit is set to NULL when the edit window is destroyed. */
144         return this->hwndEdit ? E_FAIL : E_UNEXPECTED;
145     }
146 
147     if (!SUCCEEDED(punkACL->QueryInterface(IID_PPV_ARG(IEnumString,&enumstr))))
148     {
149         TRACE("No IEnumString interface\n");
150         return  E_NOINTERFACE;
151     }
152 
153     this->hwndEdit = hwndEdit;
154     this->initialized = TRUE;
155 
156     /* Keep at least one reference to the object until the edit window is destroyed. */
157     this->AddRef();
158 
159     /* If another AutoComplete object was previously assigned to this edit control,
160        release it but keep the same callback on the control, to avoid an infinite
161        recursive loop in ACEditSubclassProc while the property is set to this object */
162     CAutoComplete *prev = static_cast<CAutoComplete *>(GetPropW(hwndEdit, autocomplete_propertyW));
163 
164     if (prev && prev->initialized)
165     {
166         this->wpOrigEditProc = prev->wpOrigEditProc;
167         SetPropW(hwndEdit, autocomplete_propertyW, this);
168         prev->wpOrigEditProc = NULL;
169         prev->Release();
170     }
171     else
172     {
173         SetPropW( this->hwndEdit, autocomplete_propertyW, (HANDLE)this );
174         this->wpOrigEditProc = (WNDPROC)SetWindowLongPtrW(hwndEdit, GWLP_WNDPROC, (LONG_PTR)ACEditSubclassProc);
175     }
176 
177     if (options & ACO_AUTOSUGGEST)
178     {
179         this->CreateListbox();
180     }
181 
182     if (pwzsRegKeyPath)
183     {
184         WCHAR *key;
185         WCHAR result[MAX_PATH];
186         WCHAR *value;
187         HKEY hKey = 0;
188         LONG res;
189         LONG len;
190 
191         /* pwszRegKeyPath contains the key as well as the value, so we split */
192         key = (WCHAR *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (wcslen(pwzsRegKeyPath) + 1) * sizeof(WCHAR));
193 
194         if (key)
195         {
196             wcscpy(key, pwzsRegKeyPath);
197             value = const_cast<WCHAR *>(wcsrchr(key, '\\'));
198 
199             if (value)
200             {
201                 *value = 0;
202                 value++;
203                 /* Now value contains the value and buffer the key */
204                 res = RegOpenKeyExW(HKEY_CURRENT_USER, key, 0, KEY_READ, &hKey);
205 
206                 if (res != ERROR_SUCCESS)
207                 {
208                     /* if the key is not found, MSDN states we must seek in HKEY_LOCAL_MACHINE */
209                     res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hKey);
210                 }
211 
212                 if (res == ERROR_SUCCESS)
213                 {
214                     len = sizeof(result);
215                     res = RegQueryValueW(hKey, value, result, &len);
216                     if (res == ERROR_SUCCESS)
217                     {
218                         quickComplete = (WCHAR *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
219                         wcscpy(quickComplete, result);
220                     }
221                     RegCloseKey(hKey);
222                 }
223             }
224 
225             HeapFree(GetProcessHeap(), 0, key);
226         }
227         else
228         {
229             TRACE("HeapAlloc Failed when trying to alloca %d bytes\n", (wcslen(pwzsRegKeyPath) + 1) * sizeof(WCHAR));
230             return S_FALSE;
231         }
232     }
233 
234     if ((pwszQuickComplete) && (!quickComplete))
235     {
236         quickComplete = (WCHAR *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (wcslen(pwszQuickComplete) + 1) * sizeof(WCHAR));
237 
238         if (quickComplete)
239         {
240             wcscpy(quickComplete, pwszQuickComplete);
241         }
242         else
243         {
244             TRACE("HeapAlloc Failed when trying to alloca %d bytes\n", (wcslen(pwszQuickComplete) + 1) * sizeof(WCHAR));
245             return S_FALSE;
246         }
247     }
248 
249     return S_OK;
250 }
251 
252 /**************************************************************************
253  *  IAutoComplete_fnGetOptions
254  */
255 HRESULT WINAPI CAutoComplete::GetOptions(DWORD *pdwFlag)
256 {
257     HRESULT hr = S_OK;
258 
259     TRACE("(%p) -> (%p)\n", this, pdwFlag);
260 
261     *pdwFlag = options;
262 
263     return hr;
264 }
265 
266 /**************************************************************************
267  *  IAutoComplete_fnSetOptions
268  */
269 HRESULT WINAPI CAutoComplete::SetOptions(DWORD dwFlag)
270 {
271     HRESULT hr = S_OK;
272 
273     TRACE("(%p) -> (0x%x)\n", this, dwFlag);
274 
275     options = (AUTOCOMPLETEOPTIONS)dwFlag;
276 
277     if ((options & ACO_AUTOSUGGEST) && hwndEdit && !hwndListBox)
278         CreateListbox();
279 
280     return hr;
281 }
282 
283 /* Edit_BackWord --- Delete previous word in text box */
284 static void Edit_BackWord(HWND hwndEdit)
285 {
286     INT iStart, iEnd;
287     iStart = iEnd = 0;
288     SendMessageW(hwndEdit, EM_GETSEL, (WPARAM)&iStart, (LPARAM)&iEnd);
289 
290     if (iStart != iEnd || iStart < 0)
291         return;
292 
293     size_t cchText = GetWindowTextLengthW(hwndEdit);
294     if (cchText < (size_t)iStart || (INT)cchText <= 0)
295         return;
296 
297     CComHeapPtr<WCHAR> pszText;
298     if (!pszText.Allocate(cchText + 1))
299         return;
300 
301     if (GetWindowTextW(hwndEdit, pszText, cchText + 1) <= 0)
302         return;
303 
304     WORD types[2];
305     for (--iStart; 0 < iStart; --iStart)
306     {
307         GetStringTypeW(CT_CTYPE1, &pszText[iStart - 1], 2, types);
308         if (((types[0] & C1_PUNCT) && !(types[1] & C1_SPACE)) ||
309             ((types[0] & C1_SPACE) && (types[1] & (C1_ALPHA | C1_DIGIT))))
310         {
311             SendMessageW(hwndEdit, EM_SETSEL, iStart, iEnd);
312             SendMessageW(hwndEdit, EM_REPLACESEL, TRUE, (LPARAM)L"");
313             return;
314         }
315     }
316 
317     if (iStart == 0)
318     {
319         SendMessageW(hwndEdit, EM_SETSEL, iStart, iEnd);
320         SendMessageW(hwndEdit, EM_REPLACESEL, TRUE, (LPARAM)L"");
321     }
322 }
323 
324 /*
325   Window procedure for autocompletion
326  */
327 LRESULT APIENTRY CAutoComplete::ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
328 {
329     CAutoComplete *pThis = static_cast<CAutoComplete *>(GetPropW(hwnd, autocomplete_propertyW));
330     HRESULT hr;
331     WCHAR hwndText[255];
332     WCHAR *hwndQCText;
333     RECT r;
334     BOOL control, filled, displayall = FALSE;
335     int cpt, height, sel;
336     ULONG fetched;
337 
338     if (!pThis->enabled)
339     {
340         return CallWindowProcW(pThis->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
341     }
342 
343     switch (uMsg)
344     {
345         case CB_SHOWDROPDOWN:
346         {
347             ShowWindow(pThis->hwndListBox, SW_HIDE);
348         }; break;
349 
350         case WM_KILLFOCUS:
351         {
352             if ((pThis->options & ACO_AUTOSUGGEST) && ((HWND)wParam != pThis->hwndListBox))
353             {
354                 ShowWindow(pThis->hwndListBox, SW_HIDE);
355             }
356             return CallWindowProcW(pThis->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
357         }; break;
358 
359         case WM_KEYUP:
360         {
361             GetWindowTextW(hwnd, (LPWSTR)hwndText, 255);
362 
363             switch(wParam)
364             {
365                 case VK_RETURN:
366                 {
367                     /* If quickComplete is set and control is pressed, replace the string */
368                     control = GetKeyState(VK_CONTROL) & 0x8000;
369                     if (control && pThis->quickComplete)
370                     {
371                         hwndQCText = (WCHAR *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
372                                            (wcslen(pThis->quickComplete)+wcslen(hwndText))*sizeof(WCHAR));
373                         sel = swprintf(hwndQCText, pThis->quickComplete, hwndText);
374                         SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)hwndQCText);
375                         SendMessageW(hwnd, EM_SETSEL, 0, sel);
376                         HeapFree(GetProcessHeap(), 0, hwndQCText);
377                     }
378 
379                     ShowWindow(pThis->hwndListBox, SW_HIDE);
380                     return 0;
381                 }; break;
382 
383                 case VK_LEFT:
384                 case VK_RIGHT:
385                 {
386                     return 0;
387                 }; break;
388 
389                 case VK_UP:
390                 case VK_DOWN:
391                 {
392                     /* Two cases here :
393                        - if the listbox is not visible, displays it
394                        with all the entries if the style ACO_UPDOWNKEYDROPSLIST
395                        is present but does not select anything.
396                        - if the listbox is visible, change the selection
397                     */
398                     if ( (pThis->options & (ACO_AUTOSUGGEST | ACO_UPDOWNKEYDROPSLIST))
399                      && (!IsWindowVisible(pThis->hwndListBox) && (! *hwndText)) )
400                     {
401                         /* We must display all the entries */
402                         displayall = TRUE;
403                     }
404                     else
405                     {
406                         if (IsWindowVisible(pThis->hwndListBox))
407                         {
408                             int count;
409 
410                             count = SendMessageW(pThis->hwndListBox, LB_GETCOUNT, 0, 0);
411                             /* Change the selection */
412                             sel = SendMessageW(pThis->hwndListBox, LB_GETCURSEL, 0, 0);
413                             if (wParam == VK_UP)
414                                 sel = ((sel-1) < 0) ? count-1 : sel-1;
415                             else
416                                 sel = ((sel+1) >= count) ? -1 : sel+1;
417 
418                             SendMessageW(pThis->hwndListBox, LB_SETCURSEL, sel, 0);
419 
420                             if (sel != -1)
421                             {
422                                 WCHAR *msg;
423                                 int len;
424 
425                                 len = SendMessageW(pThis->hwndListBox, LB_GETTEXTLEN, sel, (LPARAM)NULL);
426                                 msg = (WCHAR *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (len + 1) * sizeof(WCHAR));
427 
428                                 if (msg)
429                                 {
430                                     SendMessageW(pThis->hwndListBox, LB_GETTEXT, sel, (LPARAM)msg);
431                                     SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)msg);
432                                     SendMessageW(hwnd, EM_SETSEL, wcslen(msg), wcslen(msg));
433 
434                                     HeapFree(GetProcessHeap(), 0, msg);
435                                 }
436                                 else
437                                 {
438                                     TRACE("HeapAlloc failed to allocate %d bytes\n", (len + 1) * sizeof(WCHAR));
439                                 }
440                             }
441                             else
442                             {
443                                 SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)pThis->txtbackup);
444                                 SendMessageW(hwnd, EM_SETSEL, wcslen(pThis->txtbackup), wcslen(pThis->txtbackup));
445                             }
446                         }
447                         return 0;
448                     }
449                 }; break;
450 
451                 case VK_BACK:
452                 {
453                     if (GetKeyState(VK_CONTROL) < 0) // Ctrl+Backspace
454                     {
455                         Edit_BackWord(hwnd);
456                         return 0;
457                     }
458                 }
459                 // FALL THROUGH
460                 case VK_DELETE:
461                 {
462                     if ((! *hwndText) && (pThis->options & ACO_AUTOSUGGEST))
463                     {
464                         ShowWindow(pThis->hwndListBox, SW_HIDE);
465                         return CallWindowProcW(pThis->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
466                     }
467 
468                     if (pThis->options & ACO_AUTOAPPEND)
469                     {
470                         DWORD b;
471                         SendMessageW(hwnd, EM_GETSEL, (WPARAM)&b, (LPARAM)NULL);
472                         if (b>1)
473                         {
474                             hwndText[b-1] = '\0';
475                         }
476                         else
477                         {
478                             hwndText[0] = '\0';
479                             SetWindowTextW(hwnd, hwndText);
480                         }
481                     }
482                 }; break;
483 
484                 default:
485                     ;
486             }
487 
488             SendMessageW(pThis->hwndListBox, LB_RESETCONTENT, 0, 0);
489 
490             HeapFree(GetProcessHeap(), 0, pThis->txtbackup);
491 
492             pThis->txtbackup = (WCHAR *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (wcslen(hwndText)+1)*sizeof(WCHAR));
493 
494             if (pThis->txtbackup)
495             {
496                 wcscpy(pThis->txtbackup, hwndText);
497             }
498             else
499             {
500                 TRACE("HeapAlloc failed to allocate %d bytes\n", (wcslen(hwndText)+1)*sizeof(WCHAR));
501             }
502 
503             /* Returns if there is no text to search and we doesn't want to display all the entries */
504             if ((!displayall) && (! *hwndText) )
505                 break;
506 
507             pThis->enumstr->Reset();
508             filled = FALSE;
509             size_t curlen = wcslen(hwndText);
510 
511             for(cpt = 0;;)
512             {
513                 CComHeapPtr<OLECHAR> strs;
514                 hr = pThis->enumstr->Next(1, &strs, &fetched);
515                 if (hr != S_OK)
516                     break;
517 
518                 if (!_wcsnicmp(hwndText, strs, curlen))
519                 {
520 
521                     if (pThis->options & ACO_AUTOAPPEND && *hwndText)
522                     {
523                         CComBSTR str((PCWSTR)strs);
524                         memcpy(str.m_str, hwndText, curlen * sizeof(WCHAR));
525                         SetWindowTextW(hwnd, str);
526                         SendMessageW(hwnd, EM_SETSEL, curlen, str.Length());
527                         if (!(pThis->options & ACO_AUTOSUGGEST))
528                             break;
529                     }
530 
531                     if (pThis->options & ACO_AUTOSUGGEST)
532                     {
533                         SendMessageW(pThis->hwndListBox, LB_ADDSTRING, 0, (LPARAM)(LPOLESTR)strs);
534                         filled = TRUE;
535                         cpt++;
536                     }
537                 }
538             }
539 
540             if (pThis->options & ACO_AUTOSUGGEST)
541             {
542                 if (filled)
543                 {
544                     height = SendMessageW(pThis->hwndListBox, LB_GETITEMHEIGHT, 0, 0);
545                     SendMessageW(pThis->hwndListBox, LB_CARETOFF, 0, 0);
546                     GetWindowRect(hwnd, &r);
547                     SetParent(pThis->hwndListBox, HWND_DESKTOP);
548                     /* It seems that Windows XP displays 7 lines at most
549                        and otherwise displays a vertical scroll bar */
550                     SetWindowPos(pThis->hwndListBox, HWND_TOP,
551                          r.left, r.bottom + 1, r.right - r.left, min(height * 7, height * (cpt + 1)),
552                          SWP_SHOWWINDOW );
553                 }
554                 else
555                 {
556                     ShowWindow(pThis->hwndListBox, SW_HIDE);
557                 }
558             }
559 
560         }; break;
561 
562         case WM_DESTROY:
563         {
564             /* Release our reference that we had since ->Init() */
565             pThis->Release();
566             return 0;
567         }
568 
569 
570         default:
571         {
572             return CallWindowProcW(pThis->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
573         }
574 
575     }
576 
577     return 0;
578 }
579 
580 LRESULT APIENTRY CAutoComplete::ACLBoxSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
581 {
582     CAutoComplete *pThis = reinterpret_cast<CAutoComplete *>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
583     WCHAR *msg;
584     int sel, len;
585 
586     switch (uMsg)
587     {
588         case WM_MOUSEMOVE:
589         {
590             sel = SendMessageW(hwnd, LB_ITEMFROMPOINT, 0, lParam);
591             SendMessageW(hwnd, LB_SETCURSEL, (WPARAM)sel, (LPARAM)0);
592         }; break;
593 
594         case WM_LBUTTONDOWN:
595         {
596             sel = SendMessageW(hwnd, LB_GETCURSEL, 0, 0);
597 
598             if (sel < 0)
599                 break;
600 
601             len = SendMessageW(pThis->hwndListBox, LB_GETTEXTLEN, sel, 0);
602             msg = (WCHAR *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (len + 1) * sizeof(WCHAR));
603 
604             if (msg)
605             {
606                 SendMessageW(hwnd, LB_GETTEXT, sel, (LPARAM)msg);
607                 SendMessageW(pThis->hwndEdit, WM_SETTEXT, 0, (LPARAM)msg);
608                 SendMessageW(pThis->hwndEdit, EM_SETSEL, 0, wcslen(msg));
609                 ShowWindow(hwnd, SW_HIDE);
610 
611                 HeapFree(GetProcessHeap(), 0, msg);
612             }
613             else
614             {
615                 TRACE("HeapAlloc failed to allocate %d bytes\n", (len + 1) * sizeof(WCHAR));
616             }
617 
618         }; break;
619 
620         default:
621             return CallWindowProcW(pThis->wpOrigLBoxProc, hwnd, uMsg, wParam, lParam);
622     }
623     return 0;
624 }
625 
626 /**************************************************************************
627  *  IAutoCompleteDropDown
628  */
629 HRESULT STDMETHODCALLTYPE CAutoComplete::GetDropDownStatus(DWORD *pdwFlags, LPWSTR *ppwszString)
630 {
631     BOOL dropped = IsWindowVisible(hwndListBox);
632 
633     if (pdwFlags)
634         *pdwFlags = (dropped ? ACDD_VISIBLE : 0);
635 
636     if (ppwszString)
637     {
638         *ppwszString = NULL;
639 
640         if (dropped)
641         {
642             int sel = SendMessageW(hwndListBox, LB_GETCURSEL, 0, 0);
643             if (sel >= 0)
644             {
645                 DWORD len = SendMessageW(hwndListBox, LB_GETTEXTLEN, sel, 0);
646                 *ppwszString = (LPWSTR)CoTaskMemAlloc((len+1)*sizeof(WCHAR));
647                 SendMessageW(hwndListBox, LB_GETTEXT, sel, (LPARAM)*ppwszString);
648             }
649         }
650     }
651 
652     return S_OK;
653 }
654 
655 HRESULT STDMETHODCALLTYPE CAutoComplete::ResetEnumerator()
656 {
657     FIXME("(%p): stub\n", this);
658     return E_NOTIMPL;
659 }
660 
661 /**************************************************************************
662  *  IEnumString
663  */
664 HRESULT STDMETHODCALLTYPE CAutoComplete::Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched)
665 {
666     FIXME("(%p, %d, %p, %p): stub\n", this, celt, rgelt, pceltFetched);
667     *pceltFetched = 0;
668     return E_NOTIMPL;
669 }
670 
671 HRESULT STDMETHODCALLTYPE CAutoComplete::Skip(ULONG celt)
672 {
673     FIXME("(%p, %d): stub\n", this, celt);
674     return E_NOTIMPL;
675 }
676 
677 HRESULT STDMETHODCALLTYPE CAutoComplete::Reset()
678 {
679     FIXME("(%p): stub\n", this);
680     return E_NOTIMPL;
681 }
682 
683 HRESULT STDMETHODCALLTYPE CAutoComplete::Clone(IEnumString **ppOut)
684 {
685     FIXME("(%p, %p): stub\n", this, ppOut);
686     *ppOut = NULL;
687     return E_NOTIMPL;
688 }
689