shell32/autocomplete: Implement PageDown and PageUp for the auto-suggest listbox.
[wine.git] / dlls / shell32 / autocomplete.c
blob5cf63fed2a054f4e29e2162b4961b880e4919b42
1 /*
2 * AutoComplete interfaces implementation.
4 * Copyright 2004 Maxime Bellengé <maxime.bellenge@laposte.net>
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.
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.
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
22 Implemented:
23 - ACO_AUTOAPPEND style
24 - ACO_AUTOSUGGEST style
25 - ACO_UPDOWNKEYDROPSLIST style
27 - Handle pwzsRegKeyPath and pwszQuickComplete in Init
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
38 #include "config.h"
40 #include <stdarg.h>
41 #include <stdlib.h>
42 #include <string.h>
44 #define COBJMACROS
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"
55 #include "pidl.h"
56 #include "shlobj.h"
57 #include "shldisp.h"
58 #include "debughlp.h"
59 #include "shell32_main.h"
61 #include "wine/unicode.h"
63 WINE_DEFAULT_DEBUG_CHANNEL(shell);
65 typedef struct
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;
82 enum autoappend_flag
84 autoappend_flag_yes,
85 autoappend_flag_no,
86 autoappend_flag_displayempty
89 static const WCHAR autocomplete_propertyW[] = {'W','i','n','e',' ','A','u','t','o',
90 'c','o','m','p','l','e','t','e',' ',
91 'c','o','n','t','r','o','l',0};
93 static inline IAutoCompleteImpl *impl_from_IAutoComplete2(IAutoComplete2 *iface)
95 return CONTAINING_RECORD(iface, IAutoCompleteImpl, IAutoComplete2_iface);
98 static inline IAutoCompleteImpl *impl_from_IAutoCompleteDropDown(IAutoCompleteDropDown *iface)
100 return CONTAINING_RECORD(iface, IAutoCompleteImpl, IAutoCompleteDropDown_iface);
103 static void set_text_and_selection(IAutoCompleteImpl *ac, HWND hwnd, WCHAR *text, WPARAM start, LPARAM end)
105 /* Send it directly to the edit control to match Windows behavior */
106 WNDPROC proc = ac->wpOrigEditProc;
107 if (CallWindowProcW(proc, hwnd, WM_SETTEXT, 0, (LPARAM)text))
108 CallWindowProcW(proc, hwnd, EM_SETSEL, start, end);
111 static size_t format_quick_complete(WCHAR *dst, const WCHAR *qc, const WCHAR *str, size_t str_len)
113 /* Replace the first %s directly without using snprintf, to avoid
114 exploits since the format string can be retrieved from the registry */
115 WCHAR *base = dst;
116 UINT args = 0;
117 while (*qc != '\0')
119 if (qc[0] == '%')
121 if (args < 1 && qc[1] == 's')
123 memcpy(dst, str, str_len * sizeof(WCHAR));
124 dst += str_len;
125 qc += 2;
126 args++;
127 continue;
129 qc += (qc[1] == '%');
131 *dst++ = *qc++;
133 *dst = '\0';
134 return dst - base;
137 static LRESULT change_selection(IAutoCompleteImpl *ac, HWND hwnd, UINT key)
139 INT count = SendMessageW(ac->hwndListBox, LB_GETCOUNT, 0, 0);
140 INT sel = SendMessageW(ac->hwndListBox, LB_GETCURSEL, 0, 0);
141 if (key == VK_PRIOR || key == VK_NEXT)
143 if (sel < 0)
144 sel = (key == VK_PRIOR) ? count - 1 : 0;
145 else
147 INT base = SendMessageW(ac->hwndListBox, LB_GETTOPINDEX, 0, 0);
148 INT pgsz = SendMessageW(ac->hwndListBox, LB_GETLISTBOXINFO, 0, 0);
149 pgsz = max(pgsz - 1, 1);
150 if (key == VK_PRIOR)
152 if (sel == 0)
153 sel = -1;
154 else
156 if (sel == base) base -= min(base, pgsz);
157 sel = base;
160 else
162 if (sel == count - 1)
163 sel = -1;
164 else
166 base += pgsz;
167 if (sel >= base) base += pgsz;
168 sel = min(base, count - 1);
173 else if (key == VK_UP)
174 sel = ((sel - 1) < -1) ? count - 1 : sel - 1;
175 else
176 sel = ((sel + 1) >= count) ? -1 : sel + 1;
178 SendMessageW(ac->hwndListBox, LB_SETCURSEL, sel, 0);
179 if (sel >= 0)
181 WCHAR *msg;
182 UINT len = SendMessageW(ac->hwndListBox, LB_GETTEXTLEN, sel, 0);
183 if (!(msg = heap_alloc((len + 1) * sizeof(WCHAR))))
184 return 0;
185 len = SendMessageW(ac->hwndListBox, LB_GETTEXT, sel, (LPARAM)msg);
186 set_text_and_selection(ac, hwnd, msg, len, len);
187 heap_free(msg);
189 else
191 UINT len = strlenW(ac->txtbackup);
192 set_text_and_selection(ac, hwnd, ac->txtbackup, len, len);
194 return 0;
197 static void autoappend_str(IAutoCompleteImpl *ac, WCHAR *text, UINT len, WCHAR *str, HWND hwnd)
199 DWORD sel_start;
200 WCHAR *tmp;
201 size_t size;
203 /* Don't auto-append unless the caret is at the end */
204 SendMessageW(hwnd, EM_GETSEL, (WPARAM)&sel_start, 0);
205 if (sel_start != len)
206 return;
208 /* The character capitalization can be different,
209 so merge text and str into a new string */
210 size = len + strlenW(&str[len]) + 1;
212 if ((tmp = heap_alloc(size * sizeof(*tmp))))
214 memcpy(tmp, text, len * sizeof(*tmp));
215 memcpy(&tmp[len], &str[len], (size - len) * sizeof(*tmp));
217 else tmp = str;
219 set_text_and_selection(ac, hwnd, tmp, len, size - 1);
220 if (tmp != str)
221 heap_free(tmp);
224 static void autocomplete_text(IAutoCompleteImpl *ac, HWND hwnd, enum autoappend_flag flag)
226 HRESULT hr;
227 WCHAR *text;
228 UINT cpt, size, len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0);
230 if (flag != autoappend_flag_displayempty && len == 0)
232 if (ac->options & ACO_AUTOSUGGEST)
233 ShowWindow(ac->hwndListBox, SW_HIDE);
234 return;
237 size = len + 1;
238 if (!(text = heap_alloc(size * sizeof(WCHAR))))
239 return;
240 len = SendMessageW(hwnd, WM_GETTEXT, size, (LPARAM)text);
241 if (len + 1 != size)
242 text = heap_realloc(text, (len + 1) * sizeof(WCHAR));
244 /* Set txtbackup to point to text itself (which must not be released) */
245 heap_free(ac->txtbackup);
246 ac->txtbackup = text;
248 if (ac->options & ACO_AUTOSUGGEST)
250 SendMessageW(ac->hwndListBox, WM_SETREDRAW, FALSE, 0);
251 SendMessageW(ac->hwndListBox, LB_RESETCONTENT, 0, 0);
253 IEnumString_Reset(ac->enumstr);
254 for (cpt = 0;;)
256 LPOLESTR strs = NULL;
257 ULONG fetched;
259 hr = IEnumString_Next(ac->enumstr, 1, &strs, &fetched);
260 if (hr != S_OK)
261 break;
263 if (!strncmpiW(text, strs, len))
265 if (cpt == 0 && flag == autoappend_flag_yes)
267 autoappend_str(ac, text, len, strs, hwnd);
268 if (!(ac->options & ACO_AUTOSUGGEST))
270 CoTaskMemFree(strs);
271 break;
275 if (ac->options & ACO_AUTOSUGGEST)
276 SendMessageW(ac->hwndListBox, LB_ADDSTRING, 0, (LPARAM)strs);
278 cpt++;
281 CoTaskMemFree(strs);
284 if (ac->options & ACO_AUTOSUGGEST)
286 if (cpt)
288 RECT r;
289 UINT height = SendMessageW(ac->hwndListBox, LB_GETITEMHEIGHT, 0, 0);
290 SendMessageW(ac->hwndListBox, LB_CARETOFF, 0, 0);
291 GetWindowRect(hwnd, &r);
292 /* It seems that Windows XP displays 7 lines at most
293 and otherwise displays a vertical scroll bar */
294 SetWindowPos(ac->hwndListBox, HWND_TOP,
295 r.left, r.bottom + 1, r.right - r.left, height * min(cpt + 1, 7),
296 SWP_SHOWWINDOW );
297 SendMessageW(ac->hwndListBox, WM_SETREDRAW, TRUE, 0);
299 else
300 ShowWindow(ac->hwndListBox, SW_HIDE);
304 static void destroy_autocomplete_object(IAutoCompleteImpl *ac)
306 ac->hwndEdit = NULL;
307 if (ac->hwndListBox)
308 DestroyWindow(ac->hwndListBox);
309 IAutoComplete2_Release(&ac->IAutoComplete2_iface);
313 Helper for ACEditSubclassProc
315 static LRESULT ACEditSubclassProc_KeyDown(IAutoCompleteImpl *ac, HWND hwnd, UINT uMsg,
316 WPARAM wParam, LPARAM lParam)
318 switch (wParam)
320 case VK_RETURN:
321 /* If quickComplete is set and control is pressed, replace the string */
322 if (ac->quickComplete && (GetKeyState(VK_CONTROL) & 0x8000))
324 WCHAR *text, *buf;
325 size_t sz;
326 UINT len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0);
327 if (!(text = heap_alloc((len + 1) * sizeof(WCHAR))))
328 return 0;
329 len = SendMessageW(hwnd, WM_GETTEXT, len + 1, (LPARAM)text);
330 sz = strlenW(ac->quickComplete) + 1 + len;
332 if ((buf = heap_alloc(sz * sizeof(WCHAR))))
334 len = format_quick_complete(buf, ac->quickComplete, text, len);
335 set_text_and_selection(ac, hwnd, buf, 0, len);
336 heap_free(buf);
339 if (ac->options & ACO_AUTOSUGGEST)
340 ShowWindow(ac->hwndListBox, SW_HIDE);
341 heap_free(text);
342 return 0;
345 if (ac->options & ACO_AUTOSUGGEST)
346 ShowWindow(ac->hwndListBox, SW_HIDE);
347 break;
348 case VK_UP:
349 case VK_DOWN:
350 case VK_PRIOR:
351 case VK_NEXT:
352 /* Two cases here:
353 - if the listbox is not visible and ACO_UPDOWNKEYDROPSLIST is
354 set, display it with all the entries, without selecting any
355 - if the listbox is visible, change the selection
357 if (!(ac->options & ACO_AUTOSUGGEST))
358 break;
360 if (!IsWindowVisible(ac->hwndListBox))
362 if (ac->options & ACO_UPDOWNKEYDROPSLIST)
364 autocomplete_text(ac, hwnd, autoappend_flag_displayempty);
365 return 0;
368 else
369 return change_selection(ac, hwnd, wParam);
370 break;
371 case VK_DELETE:
373 LRESULT ret = CallWindowProcW(ac->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
374 autocomplete_text(ac, hwnd, autoappend_flag_no);
375 return ret;
378 return CallWindowProcW(ac->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
382 Window procedure for autocompletion
384 static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
386 IAutoCompleteImpl *This = GetPropW(hwnd, autocomplete_propertyW);
387 LRESULT ret;
389 if (!This->enabled) return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
391 switch (uMsg)
393 case CB_SHOWDROPDOWN:
394 if (This->options & ACO_AUTOSUGGEST)
395 ShowWindow(This->hwndListBox, SW_HIDE);
396 return 0;
397 case WM_KILLFOCUS:
398 if ((This->options & ACO_AUTOSUGGEST) && ((HWND)wParam != This->hwndListBox))
400 ShowWindow(This->hwndListBox, SW_HIDE);
402 break;
403 case WM_KEYDOWN:
404 return ACEditSubclassProc_KeyDown(This, hwnd, uMsg, wParam, lParam);
405 case WM_CHAR:
406 case WM_UNICHAR:
407 /* Don't autocomplete at all on most control characters */
408 if (iscntrlW(wParam) && !(wParam >= '\b' && wParam <= '\r'))
409 break;
411 ret = CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
412 autocomplete_text(This, hwnd, (This->options & ACO_AUTOAPPEND) && wParam >= ' '
413 ? autoappend_flag_yes : autoappend_flag_no);
414 return ret;
415 case WM_SETTEXT:
416 case WM_CUT:
417 case WM_CLEAR:
418 case WM_UNDO:
419 ret = CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
420 autocomplete_text(This, hwnd, autoappend_flag_no);
421 return ret;
422 case WM_PASTE:
423 ret = CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
424 autocomplete_text(This, hwnd, autoappend_flag_yes);
425 return ret;
426 case WM_SETFONT:
427 if (This->hwndListBox)
428 SendMessageW(This->hwndListBox, WM_SETFONT, wParam, lParam);
429 break;
430 case WM_DESTROY:
432 WNDPROC proc = This->wpOrigEditProc;
434 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)proc);
435 RemovePropW(hwnd, autocomplete_propertyW);
436 destroy_autocomplete_object(This);
437 return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
440 return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
443 static LRESULT APIENTRY ACLBoxSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
445 IAutoCompleteImpl *This = (IAutoCompleteImpl *)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
446 WCHAR *msg;
447 int sel, len;
449 switch (uMsg) {
450 case WM_MOUSEMOVE:
451 sel = SendMessageW(hwnd, LB_ITEMFROMPOINT, 0, lParam);
452 SendMessageW(hwnd, LB_SETCURSEL, sel, 0);
453 break;
454 case WM_LBUTTONDOWN:
455 sel = SendMessageW(hwnd, LB_GETCURSEL, 0, 0);
456 if (sel < 0)
457 break;
458 len = SendMessageW(hwnd, LB_GETTEXTLEN, sel, 0);
459 if (!(msg = heap_alloc((len + 1) * sizeof(WCHAR))))
460 break;
461 len = SendMessageW(hwnd, LB_GETTEXT, sel, (LPARAM)msg);
462 set_text_and_selection(This, This->hwndEdit, msg, 0, len);
463 ShowWindow(hwnd, SW_HIDE);
464 heap_free(msg);
465 break;
466 default:
467 return CallWindowProcW(This->wpOrigLBoxProc, hwnd, uMsg, wParam, lParam);
469 return 0;
472 static void create_listbox(IAutoCompleteImpl *This)
474 /* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */
475 This->hwndListBox = CreateWindowExW(0, WC_LISTBOXW, NULL,
476 WS_BORDER | WS_CHILD | WS_VSCROLL | LBS_HASSTRINGS | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT,
477 0, 0, 0, 0, GetParent(This->hwndEdit), NULL, shell32_hInstance, NULL);
479 if (This->hwndListBox) {
480 HFONT edit_font;
482 This->wpOrigLBoxProc = (WNDPROC) SetWindowLongPtrW( This->hwndListBox, GWLP_WNDPROC, (LONG_PTR) ACLBoxSubclassProc);
483 SetWindowLongPtrW( This->hwndListBox, GWLP_USERDATA, (LONG_PTR)This);
484 SetParent(This->hwndListBox, HWND_DESKTOP);
486 /* Use the same font as the edit control, as it gets destroyed before it anyway */
487 edit_font = (HFONT)SendMessageW(This->hwndEdit, WM_GETFONT, 0, 0);
488 if (edit_font)
489 SendMessageW(This->hwndListBox, WM_SETFONT, (WPARAM)edit_font, FALSE);
491 else
492 This->options &= ~ACO_AUTOSUGGEST;
495 /**************************************************************************
496 * AutoComplete_QueryInterface
498 static HRESULT WINAPI IAutoComplete2_fnQueryInterface(
499 IAutoComplete2 * iface,
500 REFIID riid,
501 LPVOID *ppvObj)
503 IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
505 TRACE("(%p)->(IID:%s,%p)\n", This, shdebugstr_guid(riid), ppvObj);
506 *ppvObj = NULL;
508 if (IsEqualIID(riid, &IID_IUnknown) ||
509 IsEqualIID(riid, &IID_IAutoComplete) ||
510 IsEqualIID(riid, &IID_IAutoComplete2))
512 *ppvObj = &This->IAutoComplete2_iface;
514 else if (IsEqualIID(riid, &IID_IAutoCompleteDropDown))
516 *ppvObj = &This->IAutoCompleteDropDown_iface;
519 if (*ppvObj)
521 IUnknown_AddRef((IUnknown*)*ppvObj);
522 TRACE("-- Interface: (%p)->(%p)\n", ppvObj, *ppvObj);
523 return S_OK;
525 WARN("unsupported interface: %s\n", debugstr_guid(riid));
526 return E_NOINTERFACE;
529 /******************************************************************************
530 * IAutoComplete2_fnAddRef
532 static ULONG WINAPI IAutoComplete2_fnAddRef(
533 IAutoComplete2 * iface)
535 IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
536 ULONG refCount = InterlockedIncrement(&This->ref);
538 TRACE("(%p)->(%u)\n", This, refCount - 1);
540 return refCount;
543 /******************************************************************************
544 * IAutoComplete2_fnRelease
546 static ULONG WINAPI IAutoComplete2_fnRelease(
547 IAutoComplete2 * iface)
549 IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
550 ULONG refCount = InterlockedDecrement(&This->ref);
552 TRACE("(%p)->(%u)\n", This, refCount + 1);
554 if (!refCount) {
555 TRACE("destroying IAutoComplete(%p)\n", This);
556 heap_free(This->quickComplete);
557 heap_free(This->txtbackup);
558 if (This->enumstr)
559 IEnumString_Release(This->enumstr);
560 heap_free(This);
562 return refCount;
565 /******************************************************************************
566 * IAutoComplete2_fnEnable
568 static HRESULT WINAPI IAutoComplete2_fnEnable(
569 IAutoComplete2 * iface,
570 BOOL fEnable)
572 IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
573 HRESULT hr = S_OK;
575 TRACE("(%p)->(%s)\n", This, (fEnable)?"true":"false");
577 This->enabled = fEnable;
579 return hr;
582 /******************************************************************************
583 * IAutoComplete2_fnInit
585 static HRESULT WINAPI IAutoComplete2_fnInit(
586 IAutoComplete2 * iface,
587 HWND hwndEdit,
588 IUnknown *punkACL,
589 LPCOLESTR pwzsRegKeyPath,
590 LPCOLESTR pwszQuickComplete)
592 IAutoCompleteImpl *prev, *This = impl_from_IAutoComplete2(iface);
594 TRACE("(%p)->(%p, %p, %s, %s)\n",
595 This, hwndEdit, punkACL, debugstr_w(pwzsRegKeyPath), debugstr_w(pwszQuickComplete));
597 if (This->options & ACO_SEARCH) FIXME(" ACO_SEARCH not supported\n");
598 if (This->options & ACO_FILTERPREFIXES) FIXME(" ACO_FILTERPREFIXES not supported\n");
599 if (This->options & ACO_USETAB) FIXME(" ACO_USETAB not supported\n");
600 if (This->options & ACO_RTLREADING) FIXME(" ACO_RTLREADING not supported\n");
602 if (!hwndEdit || !punkACL)
603 return E_INVALIDARG;
605 if (This->initialized)
607 WARN("Autocompletion object is already initialized\n");
608 /* This->hwndEdit is set to NULL when the edit window is destroyed. */
609 return This->hwndEdit ? E_FAIL : E_UNEXPECTED;
612 if (FAILED (IUnknown_QueryInterface (punkACL, &IID_IEnumString, (LPVOID*)&This->enumstr))) {
613 WARN("No IEnumString interface\n");
614 return E_NOINTERFACE;
617 This->initialized = TRUE;
618 This->hwndEdit = hwndEdit;
620 /* If another AutoComplete object was previously assigned to this edit control,
621 release it but keep the same callback on the control, to avoid an infinite
622 recursive loop in ACEditSubclassProc while the property is set to this object */
623 prev = GetPropW(hwndEdit, autocomplete_propertyW);
624 SetPropW(hwndEdit, autocomplete_propertyW, This);
626 if (prev && prev->initialized) {
627 This->wpOrigEditProc = prev->wpOrigEditProc;
628 destroy_autocomplete_object(prev);
630 else
631 This->wpOrigEditProc = (WNDPROC) SetWindowLongPtrW(hwndEdit, GWLP_WNDPROC, (LONG_PTR) ACEditSubclassProc);
633 /* Keep at least one reference to the object until the edit window is destroyed */
634 IAutoComplete2_AddRef(&This->IAutoComplete2_iface);
636 if (This->options & ACO_AUTOSUGGEST)
637 create_listbox(This);
639 if (pwzsRegKeyPath)
641 static const HKEY roots[] = { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE };
642 WCHAR *key, *value;
643 DWORD type, sz;
644 BYTE *qc;
645 HKEY hKey;
646 LSTATUS res;
647 size_t len;
648 UINT i;
650 /* pwszRegKeyPath contains the key as well as the value, so split it */
651 value = strrchrW(pwzsRegKeyPath, '\\');
652 len = value - pwzsRegKeyPath;
654 if (value && (key = heap_alloc((len+1) * sizeof(*key))) != NULL)
656 memcpy(key, pwzsRegKeyPath, len * sizeof(*key));
657 key[len] = '\0';
658 value++;
660 for (i = 0; i < ARRAY_SIZE(roots); i++)
662 if (RegOpenKeyExW(roots[i], key, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
663 continue;
664 sz = MAX_PATH * sizeof(WCHAR);
666 while ((qc = heap_alloc(sz)) != NULL)
668 res = RegQueryValueExW(hKey, value, NULL, &type, qc, &sz);
669 if (res == ERROR_SUCCESS && type == REG_SZ)
671 This->quickComplete = heap_realloc(qc, sz);
672 i = ARRAY_SIZE(roots);
673 break;
675 heap_free(qc);
676 if (res != ERROR_MORE_DATA || type != REG_SZ)
677 break;
679 RegCloseKey(hKey);
681 heap_free(key);
685 if (!This->quickComplete && pwszQuickComplete)
687 size_t len = strlenW(pwszQuickComplete)+1;
688 if ((This->quickComplete = heap_alloc(len * sizeof(WCHAR))) != NULL)
689 memcpy(This->quickComplete, pwszQuickComplete, len * sizeof(WCHAR));
692 return S_OK;
695 /**************************************************************************
696 * IAutoComplete2_fnGetOptions
698 static HRESULT WINAPI IAutoComplete2_fnGetOptions(
699 IAutoComplete2 * iface,
700 DWORD *pdwFlag)
702 IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
703 HRESULT hr = S_OK;
705 TRACE("(%p) -> (%p)\n", This, pdwFlag);
707 *pdwFlag = This->options;
709 return hr;
712 /**************************************************************************
713 * IAutoComplete2_fnSetOptions
715 static HRESULT WINAPI IAutoComplete2_fnSetOptions(
716 IAutoComplete2 * iface,
717 DWORD dwFlag)
719 IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
720 HRESULT hr = S_OK;
722 TRACE("(%p) -> (0x%x)\n", This, dwFlag);
724 This->options = dwFlag;
726 if ((This->options & ACO_AUTOSUGGEST) && This->hwndEdit && !This->hwndListBox)
727 create_listbox(This);
729 return hr;
732 /**************************************************************************
733 * IAutoComplete2 VTable
735 static const IAutoComplete2Vtbl acvt =
737 IAutoComplete2_fnQueryInterface,
738 IAutoComplete2_fnAddRef,
739 IAutoComplete2_fnRelease,
740 IAutoComplete2_fnInit,
741 IAutoComplete2_fnEnable,
742 /* IAutoComplete2 */
743 IAutoComplete2_fnSetOptions,
744 IAutoComplete2_fnGetOptions,
748 static HRESULT WINAPI IAutoCompleteDropDown_fnQueryInterface(IAutoCompleteDropDown *iface,
749 REFIID riid, LPVOID *ppvObj)
751 IAutoCompleteImpl *This = impl_from_IAutoCompleteDropDown(iface);
752 return IAutoComplete2_QueryInterface(&This->IAutoComplete2_iface, riid, ppvObj);
755 static ULONG WINAPI IAutoCompleteDropDown_fnAddRef(IAutoCompleteDropDown *iface)
757 IAutoCompleteImpl *This = impl_from_IAutoCompleteDropDown(iface);
758 return IAutoComplete2_AddRef(&This->IAutoComplete2_iface);
761 static ULONG WINAPI IAutoCompleteDropDown_fnRelease(IAutoCompleteDropDown *iface)
763 IAutoCompleteImpl *This = impl_from_IAutoCompleteDropDown(iface);
764 return IAutoComplete2_Release(&This->IAutoComplete2_iface);
767 /**************************************************************************
768 * IAutoCompleteDropDown_fnGetDropDownStatus
770 static HRESULT WINAPI IAutoCompleteDropDown_fnGetDropDownStatus(
771 IAutoCompleteDropDown *iface,
772 DWORD *pdwFlags,
773 LPWSTR *ppwszString)
775 IAutoCompleteImpl *This = impl_from_IAutoCompleteDropDown(iface);
776 BOOL dropped;
778 TRACE("(%p) -> (%p, %p)\n", This, pdwFlags, ppwszString);
780 dropped = IsWindowVisible(This->hwndListBox);
782 if (pdwFlags)
783 *pdwFlags = (dropped ? ACDD_VISIBLE : 0);
785 if (ppwszString) {
786 if (dropped) {
787 int sel;
789 sel = SendMessageW(This->hwndListBox, LB_GETCURSEL, 0, 0);
790 if (sel >= 0)
792 DWORD len;
794 len = SendMessageW(This->hwndListBox, LB_GETTEXTLEN, sel, 0);
795 *ppwszString = CoTaskMemAlloc((len+1)*sizeof(WCHAR));
796 SendMessageW(This->hwndListBox, LB_GETTEXT, sel, (LPARAM)*ppwszString);
798 else
799 *ppwszString = NULL;
801 else
802 *ppwszString = NULL;
805 return S_OK;
808 /**************************************************************************
809 * IAutoCompleteDropDown_fnResetEnumarator
811 static HRESULT WINAPI IAutoCompleteDropDown_fnResetEnumerator(
812 IAutoCompleteDropDown *iface)
814 IAutoCompleteImpl *This = impl_from_IAutoCompleteDropDown(iface);
816 FIXME("(%p): stub\n", This);
818 return E_NOTIMPL;
821 /**************************************************************************
822 * IAutoCompleteDropDown VTable
824 static const IAutoCompleteDropDownVtbl acdropdownvt =
826 IAutoCompleteDropDown_fnQueryInterface,
827 IAutoCompleteDropDown_fnAddRef,
828 IAutoCompleteDropDown_fnRelease,
829 IAutoCompleteDropDown_fnGetDropDownStatus,
830 IAutoCompleteDropDown_fnResetEnumerator,
833 /**************************************************************************
834 * IAutoComplete_Constructor
836 HRESULT WINAPI IAutoComplete_Constructor(IUnknown * pUnkOuter, REFIID riid, LPVOID * ppv)
838 IAutoCompleteImpl *lpac;
839 HRESULT hr;
841 if (pUnkOuter && !IsEqualIID (riid, &IID_IUnknown))
842 return CLASS_E_NOAGGREGATION;
844 lpac = heap_alloc_zero(sizeof(*lpac));
845 if (!lpac)
846 return E_OUTOFMEMORY;
848 lpac->ref = 1;
849 lpac->IAutoComplete2_iface.lpVtbl = &acvt;
850 lpac->IAutoCompleteDropDown_iface.lpVtbl = &acdropdownvt;
851 lpac->enabled = TRUE;
852 lpac->options = ACO_AUTOAPPEND;
854 hr = IAutoComplete2_QueryInterface(&lpac->IAutoComplete2_iface, riid, ppv);
855 IAutoComplete2_Release(&lpac->IAutoComplete2_iface);
857 TRACE("-- (%p)->\n",lpac);
859 return hr;