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