2 * AutoComplete interfaces implementation.
4 * Copyright 2004 Maxime Bellengé <maxime.bellenge@laposte.net>
5 * Copyright 2018 Gabriel Ivăncescu <gabrielopcode@gmail.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 - implement ACO_SEARCH style
25 - implement ACO_FILTERPREFIXES style
26 - implement ACO_RTLREADING style
27 - implement ResetEnumerator
38 #include "wine/debug.h"
42 #include "undocshell.h"
51 #include "shell32_main.h"
53 #include "wine/unicode.h"
55 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
59 IAutoComplete2 IAutoComplete2_iface
;
60 IAutoCompleteDropDown IAutoCompleteDropDown_iface
;
68 WNDPROC wpOrigEditProc
;
69 WNDPROC wpOrigLBoxProc
;
74 AUTOCOMPLETEOPTIONS options
;
82 autoappend_flag_displayempty
85 static const WCHAR autocomplete_propertyW
[] = {'W','i','n','e',' ','A','u','t','o',
86 'c','o','m','p','l','e','t','e',' ',
87 'c','o','n','t','r','o','l',0};
89 static inline IAutoCompleteImpl
*impl_from_IAutoComplete2(IAutoComplete2
*iface
)
91 return CONTAINING_RECORD(iface
, IAutoCompleteImpl
, IAutoComplete2_iface
);
94 static inline IAutoCompleteImpl
*impl_from_IAutoCompleteDropDown(IAutoCompleteDropDown
*iface
)
96 return CONTAINING_RECORD(iface
, IAutoCompleteImpl
, IAutoCompleteDropDown_iface
);
99 static void set_text_and_selection(IAutoCompleteImpl
*ac
, HWND hwnd
, WCHAR
*text
, WPARAM start
, LPARAM end
)
101 /* Send it directly to the edit control to match Windows behavior */
102 WNDPROC proc
= ac
->wpOrigEditProc
;
103 if (CallWindowProcW(proc
, hwnd
, WM_SETTEXT
, 0, (LPARAM
)text
))
104 CallWindowProcW(proc
, hwnd
, EM_SETSEL
, start
, end
);
107 static int enumerate_strings_cmpfn(const void *a
, const void *b
)
109 return strcmpiW(*(WCHAR
* const*)a
, *(WCHAR
* const*)b
);
113 Enumerate all of the strings and sort them in the internal list.
115 We don't free the enumerated strings (except on error) to avoid needless
116 copies, until the next reset (or the object itself is destroyed)
118 static void enumerate_strings(IAutoCompleteImpl
*ac
)
120 UINT cur
= 0, array_size
= 1024;
121 LPOLESTR
*strs
= NULL
, *tmp
;
126 if ((tmp
= heap_realloc(strs
, array_size
* sizeof(*strs
))) == NULL
)
132 if (FAILED(IEnumString_Next(ac
->enumstr
, array_size
- cur
, &strs
[cur
], &read
)))
134 } while (read
!= 0 && (cur
+= read
) < array_size
);
139 /* Allocate even if there were zero strings enumerated, to mark it non-NULL */
140 if ((tmp
= heap_realloc(strs
, cur
* sizeof(*strs
))))
144 qsort(strs
, cur
, sizeof(*strs
), enumerate_strings_cmpfn
);
146 ac
->enum_strs
= strs
;
147 ac
->enum_strs_num
= cur
;
153 CoTaskMemFree(strs
[cur
]);
157 static UINT
find_matching_enum_str(IAutoCompleteImpl
*ac
, UINT start
, WCHAR
*text
,
158 UINT len
, int direction
)
160 WCHAR
**strs
= ac
->enum_strs
;
161 UINT index
= ~0, a
= start
, b
= ac
->enum_strs_num
;
164 UINT i
= (a
+ b
- 1) / 2;
165 int cmp
= strncmpiW(text
, strs
[i
], len
);
177 static void free_enum_strs(IAutoCompleteImpl
*ac
)
179 WCHAR
**strs
= ac
->enum_strs
;
182 UINT i
= ac
->enum_strs_num
;
183 ac
->enum_strs
= NULL
;
185 CoTaskMemFree(strs
[i
]);
190 static void hide_listbox(IAutoCompleteImpl
*ac
, HWND hwnd
, BOOL reset
)
192 ShowWindow(hwnd
, SW_HIDE
);
193 SendMessageW(hwnd
, LB_RESETCONTENT
, 0, 0);
194 if (reset
) free_enum_strs(ac
);
197 static void show_listbox(IAutoCompleteImpl
*ac
, UINT cnt
)
202 GetWindowRect(ac
->hwndEdit
, &r
);
203 SendMessageW(ac
->hwndListBox
, LB_CARETOFF
, 0, 0);
205 /* Windows XP displays 7 lines at most, then it uses a scroll bar */
206 height
= SendMessageW(ac
->hwndListBox
, LB_GETITEMHEIGHT
, 0, 0) * min(cnt
+ 1, 7);
207 width
= r
.right
- r
.left
;
209 SetWindowPos(ac
->hwndListBox
, HWND_TOP
, r
.left
, r
.bottom
+ 1, width
, height
, SWP_SHOWWINDOW
);
212 static size_t format_quick_complete(WCHAR
*dst
, const WCHAR
*qc
, const WCHAR
*str
, size_t str_len
)
214 /* Replace the first %s directly without using snprintf, to avoid
215 exploits since the format string can be retrieved from the registry */
222 if (args
< 1 && qc
[1] == 's')
224 memcpy(dst
, str
, str_len
* sizeof(WCHAR
));
230 qc
+= (qc
[1] == '%');
238 static BOOL
select_item_with_return_key(IAutoCompleteImpl
*ac
, HWND hwnd
)
241 HWND hwndListBox
= ac
->hwndListBox
;
242 if (!(ac
->options
& ACO_AUTOSUGGEST
))
245 if (IsWindowVisible(hwndListBox
))
247 INT sel
= SendMessageW(hwndListBox
, LB_GETCURSEL
, 0, 0);
250 UINT len
= SendMessageW(hwndListBox
, LB_GETTEXTLEN
, sel
, 0);
251 if ((text
= heap_alloc((len
+ 1) * sizeof(WCHAR
))))
253 len
= SendMessageW(hwndListBox
, LB_GETTEXT
, sel
, (LPARAM
)text
);
254 set_text_and_selection(ac
, hwnd
, text
, 0, len
);
255 hide_listbox(ac
, hwndListBox
, TRUE
);
256 ac
->no_fwd_char
= '\r'; /* RETURN char */
262 hide_listbox(ac
, hwndListBox
, TRUE
);
266 static LRESULT
change_selection(IAutoCompleteImpl
*ac
, HWND hwnd
, UINT key
)
268 INT count
= SendMessageW(ac
->hwndListBox
, LB_GETCOUNT
, 0, 0);
269 INT sel
= SendMessageW(ac
->hwndListBox
, LB_GETCURSEL
, 0, 0);
270 if (key
== VK_PRIOR
|| key
== VK_NEXT
)
273 sel
= (key
== VK_PRIOR
) ? count
- 1 : 0;
276 INT base
= SendMessageW(ac
->hwndListBox
, LB_GETTOPINDEX
, 0, 0);
277 INT pgsz
= SendMessageW(ac
->hwndListBox
, LB_GETLISTBOXINFO
, 0, 0);
278 pgsz
= max(pgsz
- 1, 1);
285 if (sel
== base
) base
-= min(base
, pgsz
);
291 if (sel
== count
- 1)
296 if (sel
>= base
) base
+= pgsz
;
297 sel
= min(base
, count
- 1);
302 else if (key
== VK_UP
|| (key
== VK_TAB
&& (GetKeyState(VK_SHIFT
) & 0x8000)))
303 sel
= ((sel
- 1) < -1) ? count
- 1 : sel
- 1;
305 sel
= ((sel
+ 1) >= count
) ? -1 : sel
+ 1;
307 SendMessageW(ac
->hwndListBox
, LB_SETCURSEL
, sel
, 0);
311 UINT len
= SendMessageW(ac
->hwndListBox
, LB_GETTEXTLEN
, sel
, 0);
312 if (!(msg
= heap_alloc((len
+ 1) * sizeof(WCHAR
))))
314 len
= SendMessageW(ac
->hwndListBox
, LB_GETTEXT
, sel
, (LPARAM
)msg
);
315 set_text_and_selection(ac
, hwnd
, msg
, len
, len
);
320 UINT len
= strlenW(ac
->txtbackup
);
321 set_text_and_selection(ac
, hwnd
, ac
->txtbackup
, len
, len
);
326 static BOOL
do_aclist_expand(IAutoCompleteImpl
*ac
, WCHAR
*txt
, WCHAR
*last_delim
)
328 WCHAR c
= last_delim
[1];
331 IEnumString_Reset(ac
->enumstr
); /* call before expand */
333 last_delim
[1] = '\0';
334 IACList_Expand(ac
->aclist
, txt
);
339 static BOOL
aclist_expand(IAutoCompleteImpl
*ac
, WCHAR
*txt
)
341 /* call IACList::Expand only when needed, if the
342 new txt and old_txt require different expansions */
343 WCHAR c
, *p
, *last_delim
, *old_txt
= ac
->txtbackup
;
346 /* '/' is allowed as a delim for unix paths */
347 static const WCHAR delims
[] = { '\\', '/', 0 };
349 /* skip the shared prefix */
350 while ((c
= tolowerW(txt
[i
])) == tolowerW(old_txt
[i
]))
352 if (c
== '\0') return FALSE
;
356 /* they differ at this point, check for a delim further in txt */
357 for (last_delim
= NULL
, p
= &txt
[i
]; (p
= strpbrkW(p
, delims
)) != NULL
; p
++)
359 if (last_delim
) return do_aclist_expand(ac
, txt
, last_delim
);
361 /* txt has no delim after i, check for a delim further in old_txt */
362 if (strpbrkW(&old_txt
[i
], delims
))
364 /* scan backwards to find the first delim before txt[i] (if any) */
366 if (strchrW(delims
, txt
[i
]))
367 return do_aclist_expand(ac
, txt
, &txt
[i
]);
369 /* Windows doesn't expand without a delim, but it does reset */
376 static void autoappend_str(IAutoCompleteImpl
*ac
, WCHAR
*text
, UINT len
, WCHAR
*str
, HWND hwnd
)
382 /* Don't auto-append unless the caret is at the end */
383 SendMessageW(hwnd
, EM_GETSEL
, (WPARAM
)&sel_start
, 0);
384 if (sel_start
!= len
)
387 /* The character capitalization can be different,
388 so merge text and str into a new string */
389 size
= len
+ strlenW(&str
[len
]) + 1;
391 if ((tmp
= heap_alloc(size
* sizeof(*tmp
))))
393 memcpy(tmp
, text
, len
* sizeof(*tmp
));
394 memcpy(&tmp
[len
], &str
[len
], (size
- len
) * sizeof(*tmp
));
398 set_text_and_selection(ac
, hwnd
, tmp
, len
, size
- 1);
403 static BOOL
display_matching_strs(IAutoCompleteImpl
*ac
, WCHAR
*text
, UINT len
,
404 HWND hwnd
, enum autoappend_flag flag
)
406 /* Return FALSE if we need to hide the listbox */
407 WCHAR
**str
= ac
->enum_strs
;
408 UINT cnt
, start
, end
;
409 if (!str
) return (ac
->options
& ACO_AUTOSUGGEST
) ? FALSE
: TRUE
;
413 start
= find_matching_enum_str(ac
, 0, text
, len
, -1);
415 return (ac
->options
& ACO_AUTOSUGGEST
) ? FALSE
: TRUE
;
417 if (flag
== autoappend_flag_yes
)
418 autoappend_str(ac
, text
, len
, str
[start
], hwnd
);
419 if (!(ac
->options
& ACO_AUTOSUGGEST
))
422 /* Find the index beyond the last string that matches */
423 end
= find_matching_enum_str(ac
, start
+ 1, text
, len
, 1);
424 end
= (end
== ~0 ? start
: end
) + 1;
428 if (!(ac
->options
& ACO_AUTOSUGGEST
))
431 end
= ac
->enum_strs_num
;
437 SendMessageW(ac
->hwndListBox
, WM_SETREDRAW
, FALSE
, 0);
438 SendMessageW(ac
->hwndListBox
, LB_RESETCONTENT
, 0, 0);
439 SendMessageW(ac
->hwndListBox
, LB_INITSTORAGE
, cnt
, 0);
440 for (; start
< end
; start
++)
441 SendMessageW(ac
->hwndListBox
, LB_INSERTSTRING
, -1, (LPARAM
)str
[start
]);
443 show_listbox(ac
, cnt
);
444 SendMessageW(ac
->hwndListBox
, WM_SETREDRAW
, TRUE
, 0);
448 static void autocomplete_text(IAutoCompleteImpl
*ac
, HWND hwnd
, enum autoappend_flag flag
)
451 BOOL expanded
= FALSE
;
452 UINT size
, len
= SendMessageW(hwnd
, WM_GETTEXTLENGTH
, 0, 0);
454 if (flag
!= autoappend_flag_displayempty
&& len
== 0)
456 if (ac
->options
& ACO_AUTOSUGGEST
)
457 hide_listbox(ac
, ac
->hwndListBox
, FALSE
);
463 if (!(text
= heap_alloc(size
* sizeof(WCHAR
))))
465 len
= SendMessageW(hwnd
, WM_GETTEXT
, size
, (LPARAM
)text
);
467 text
= heap_realloc(text
, (len
+ 1) * sizeof(WCHAR
));
471 if (text
[len
- 1] == '\\' || text
[len
- 1] == '/')
472 flag
= autoappend_flag_no
;
473 expanded
= aclist_expand(ac
, text
);
475 if (expanded
|| !ac
->enum_strs
)
477 if (!expanded
) IEnumString_Reset(ac
->enumstr
);
478 enumerate_strings(ac
);
481 /* Set txtbackup to point to text itself (which must not be released),
482 and it must be done here since aclist_expand uses it to track changes */
483 heap_free(ac
->txtbackup
);
484 ac
->txtbackup
= text
;
486 if (!display_matching_strs(ac
, text
, len
, hwnd
, flag
))
487 hide_listbox(ac
, ac
->hwndListBox
, FALSE
);
490 static void destroy_autocomplete_object(IAutoCompleteImpl
*ac
)
495 DestroyWindow(ac
->hwndListBox
);
496 IAutoComplete2_Release(&ac
->IAutoComplete2_iface
);
500 Helper for ACEditSubclassProc
502 static LRESULT
ACEditSubclassProc_KeyDown(IAutoCompleteImpl
*ac
, HWND hwnd
, UINT uMsg
,
503 WPARAM wParam
, LPARAM lParam
)
508 /* When pressing ESC, Windows hides the auto-suggest listbox, if visible */
509 if ((ac
->options
& ACO_AUTOSUGGEST
) && IsWindowVisible(ac
->hwndListBox
))
511 hide_listbox(ac
, ac
->hwndListBox
, FALSE
);
512 ac
->no_fwd_char
= 0x1B; /* ESC char */
517 /* If quickComplete is set and control is pressed, replace the string */
518 if (ac
->quickComplete
&& (GetKeyState(VK_CONTROL
) & 0x8000))
522 UINT len
= SendMessageW(hwnd
, WM_GETTEXTLENGTH
, 0, 0);
523 ac
->no_fwd_char
= '\n'; /* CTRL+RETURN char */
525 if (!(text
= heap_alloc((len
+ 1) * sizeof(WCHAR
))))
527 len
= SendMessageW(hwnd
, WM_GETTEXT
, len
+ 1, (LPARAM
)text
);
528 sz
= strlenW(ac
->quickComplete
) + 1 + len
;
530 if ((buf
= heap_alloc(sz
* sizeof(WCHAR
))))
532 len
= format_quick_complete(buf
, ac
->quickComplete
, text
, len
);
533 set_text_and_selection(ac
, hwnd
, buf
, 0, len
);
537 if (ac
->options
& ACO_AUTOSUGGEST
)
538 hide_listbox(ac
, ac
->hwndListBox
, TRUE
);
543 if (select_item_with_return_key(ac
, hwnd
))
547 if ((ac
->options
& (ACO_AUTOSUGGEST
| ACO_USETAB
)) == (ACO_AUTOSUGGEST
| ACO_USETAB
)
548 && IsWindowVisible(ac
->hwndListBox
) && !(GetKeyState(VK_CONTROL
) & 0x8000))
550 ac
->no_fwd_char
= '\t';
551 return change_selection(ac
, hwnd
, wParam
);
559 - if the listbox is not visible and ACO_UPDOWNKEYDROPSLIST is
560 set, display it with all the entries, without selecting any
561 - if the listbox is visible, change the selection
563 if (!(ac
->options
& ACO_AUTOSUGGEST
))
566 if (!IsWindowVisible(ac
->hwndListBox
))
568 if (ac
->options
& ACO_UPDOWNKEYDROPSLIST
)
570 autocomplete_text(ac
, hwnd
, autoappend_flag_displayempty
);
575 return change_selection(ac
, hwnd
, wParam
);
579 LRESULT ret
= CallWindowProcW(ac
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
580 autocomplete_text(ac
, hwnd
, autoappend_flag_no
);
584 ac
->no_fwd_char
= '\0';
585 return CallWindowProcW(ac
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
589 Window procedure for autocompletion
591 static LRESULT APIENTRY
ACEditSubclassProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
593 IAutoCompleteImpl
*This
= GetPropW(hwnd
, autocomplete_propertyW
);
596 if (!This
->enabled
) return CallWindowProcW(This
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
600 case CB_SHOWDROPDOWN
:
601 if (This
->options
& ACO_AUTOSUGGEST
)
602 hide_listbox(This
, This
->hwndListBox
, TRUE
);
605 if (This
->options
& ACO_AUTOSUGGEST
)
607 if ((HWND
)wParam
== This
->hwndListBox
) break;
608 hide_listbox(This
, This
->hwndListBox
, FALSE
);
611 /* Reset the enumerator if it's not visible anymore */
612 if (!IsWindowVisible(hwnd
)) free_enum_strs(This
);
615 return ACEditSubclassProc_KeyDown(This
, hwnd
, uMsg
, wParam
, lParam
);
618 if (wParam
== This
->no_fwd_char
) return 0;
619 This
->no_fwd_char
= '\0';
621 /* Don't autocomplete at all on most control characters */
622 if (iscntrlW(wParam
) && !(wParam
>= '\b' && wParam
<= '\r'))
625 ret
= CallWindowProcW(This
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
626 autocomplete_text(This
, hwnd
, (This
->options
& ACO_AUTOAPPEND
) && wParam
>= ' '
627 ? autoappend_flag_yes
: autoappend_flag_no
);
633 ret
= CallWindowProcW(This
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
634 autocomplete_text(This
, hwnd
, autoappend_flag_no
);
637 ret
= CallWindowProcW(This
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
638 autocomplete_text(This
, hwnd
, autoappend_flag_yes
);
641 if (This
->hwndListBox
)
642 SendMessageW(This
->hwndListBox
, WM_SETFONT
, wParam
, lParam
);
646 WNDPROC proc
= This
->wpOrigEditProc
;
648 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (LONG_PTR
)proc
);
649 RemovePropW(hwnd
, autocomplete_propertyW
);
650 destroy_autocomplete_object(This
);
651 return CallWindowProcW(proc
, hwnd
, uMsg
, wParam
, lParam
);
654 return CallWindowProcW(This
->wpOrigEditProc
, hwnd
, uMsg
, wParam
, lParam
);
657 static LRESULT APIENTRY
ACLBoxSubclassProc(HWND hwnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
659 IAutoCompleteImpl
*This
= (IAutoCompleteImpl
*)GetWindowLongPtrW(hwnd
, GWLP_USERDATA
);
665 sel
= SendMessageW(hwnd
, LB_ITEMFROMPOINT
, 0, lParam
);
666 SendMessageW(hwnd
, LB_SETCURSEL
, sel
, 0);
669 sel
= SendMessageW(hwnd
, LB_GETCURSEL
, 0, 0);
672 len
= SendMessageW(hwnd
, LB_GETTEXTLEN
, sel
, 0);
673 if (!(msg
= heap_alloc((len
+ 1) * sizeof(WCHAR
))))
675 len
= SendMessageW(hwnd
, LB_GETTEXT
, sel
, (LPARAM
)msg
);
676 set_text_and_selection(This
, This
->hwndEdit
, msg
, 0, len
);
677 hide_listbox(This
, hwnd
, TRUE
);
681 return CallWindowProcW(This
->wpOrigLBoxProc
, hwnd
, uMsg
, wParam
, lParam
);
686 static void create_listbox(IAutoCompleteImpl
*This
)
688 /* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */
689 This
->hwndListBox
= CreateWindowExW(0, WC_LISTBOXW
, NULL
,
690 WS_BORDER
| WS_CHILD
| WS_VSCROLL
| LBS_HASSTRINGS
| LBS_NOTIFY
| LBS_NOINTEGRALHEIGHT
,
691 0, 0, 0, 0, GetParent(This
->hwndEdit
), NULL
, shell32_hInstance
, NULL
);
693 if (This
->hwndListBox
) {
696 This
->wpOrigLBoxProc
= (WNDPROC
) SetWindowLongPtrW( This
->hwndListBox
, GWLP_WNDPROC
, (LONG_PTR
) ACLBoxSubclassProc
);
697 SetWindowLongPtrW( This
->hwndListBox
, GWLP_USERDATA
, (LONG_PTR
)This
);
698 SetParent(This
->hwndListBox
, HWND_DESKTOP
);
700 /* Use the same font as the edit control, as it gets destroyed before it anyway */
701 edit_font
= (HFONT
)SendMessageW(This
->hwndEdit
, WM_GETFONT
, 0, 0);
703 SendMessageW(This
->hwndListBox
, WM_SETFONT
, (WPARAM
)edit_font
, FALSE
);
706 This
->options
&= ~ACO_AUTOSUGGEST
;
709 /**************************************************************************
710 * AutoComplete_QueryInterface
712 static HRESULT WINAPI
IAutoComplete2_fnQueryInterface(
713 IAutoComplete2
* iface
,
717 IAutoCompleteImpl
*This
= impl_from_IAutoComplete2(iface
);
719 TRACE("(%p)->(IID:%s,%p)\n", This
, shdebugstr_guid(riid
), ppvObj
);
722 if (IsEqualIID(riid
, &IID_IUnknown
) ||
723 IsEqualIID(riid
, &IID_IAutoComplete
) ||
724 IsEqualIID(riid
, &IID_IAutoComplete2
))
726 *ppvObj
= &This
->IAutoComplete2_iface
;
728 else if (IsEqualIID(riid
, &IID_IAutoCompleteDropDown
))
730 *ppvObj
= &This
->IAutoCompleteDropDown_iface
;
735 IUnknown_AddRef((IUnknown
*)*ppvObj
);
736 TRACE("-- Interface: (%p)->(%p)\n", ppvObj
, *ppvObj
);
739 WARN("unsupported interface: %s\n", debugstr_guid(riid
));
740 return E_NOINTERFACE
;
743 /******************************************************************************
744 * IAutoComplete2_fnAddRef
746 static ULONG WINAPI
IAutoComplete2_fnAddRef(
747 IAutoComplete2
* iface
)
749 IAutoCompleteImpl
*This
= impl_from_IAutoComplete2(iface
);
750 ULONG refCount
= InterlockedIncrement(&This
->ref
);
752 TRACE("(%p)->(%u)\n", This
, refCount
- 1);
757 /******************************************************************************
758 * IAutoComplete2_fnRelease
760 static ULONG WINAPI
IAutoComplete2_fnRelease(
761 IAutoComplete2
* iface
)
763 IAutoCompleteImpl
*This
= impl_from_IAutoComplete2(iface
);
764 ULONG refCount
= InterlockedDecrement(&This
->ref
);
766 TRACE("(%p)->(%u)\n", This
, refCount
+ 1);
769 TRACE("destroying IAutoComplete(%p)\n", This
);
770 heap_free(This
->quickComplete
);
771 heap_free(This
->txtbackup
);
773 IEnumString_Release(This
->enumstr
);
775 IACList_Release(This
->aclist
);
781 /******************************************************************************
782 * IAutoComplete2_fnEnable
784 static HRESULT WINAPI
IAutoComplete2_fnEnable(
785 IAutoComplete2
* iface
,
788 IAutoCompleteImpl
*This
= impl_from_IAutoComplete2(iface
);
791 TRACE("(%p)->(%s)\n", This
, (fEnable
)?"true":"false");
793 This
->enabled
= fEnable
;
798 /******************************************************************************
799 * IAutoComplete2_fnInit
801 static HRESULT WINAPI
IAutoComplete2_fnInit(
802 IAutoComplete2
* iface
,
805 LPCOLESTR pwzsRegKeyPath
,
806 LPCOLESTR pwszQuickComplete
)
808 IAutoCompleteImpl
*prev
, *This
= impl_from_IAutoComplete2(iface
);
810 TRACE("(%p)->(%p, %p, %s, %s)\n",
811 This
, hwndEdit
, punkACL
, debugstr_w(pwzsRegKeyPath
), debugstr_w(pwszQuickComplete
));
813 if (This
->options
& ACO_SEARCH
) FIXME(" ACO_SEARCH not supported\n");
814 if (This
->options
& ACO_FILTERPREFIXES
) FIXME(" ACO_FILTERPREFIXES not supported\n");
815 if (This
->options
& ACO_RTLREADING
) FIXME(" ACO_RTLREADING not supported\n");
817 if (!hwndEdit
|| !punkACL
)
820 if (This
->initialized
)
822 WARN("Autocompletion object is already initialized\n");
823 /* This->hwndEdit is set to NULL when the edit window is destroyed. */
824 return This
->hwndEdit
? E_FAIL
: E_UNEXPECTED
;
827 if (FAILED (IUnknown_QueryInterface (punkACL
, &IID_IEnumString
, (LPVOID
*)&This
->enumstr
))) {
828 WARN("No IEnumString interface\n");
829 return E_NOINTERFACE
;
832 /* Prevent txtbackup from ever being NULL to simplify aclist_expand */
833 if ((This
->txtbackup
= heap_alloc_zero(sizeof(WCHAR
))) == NULL
)
835 IEnumString_Release(This
->enumstr
);
836 This
->enumstr
= NULL
;
837 return E_OUTOFMEMORY
;
840 if (FAILED (IUnknown_QueryInterface (punkACL
, &IID_IACList
, (LPVOID
*)&This
->aclist
)))
843 This
->initialized
= TRUE
;
844 This
->hwndEdit
= hwndEdit
;
846 /* If another AutoComplete object was previously assigned to this edit control,
847 release it but keep the same callback on the control, to avoid an infinite
848 recursive loop in ACEditSubclassProc while the property is set to this object */
849 prev
= GetPropW(hwndEdit
, autocomplete_propertyW
);
850 SetPropW(hwndEdit
, autocomplete_propertyW
, This
);
852 if (prev
&& prev
->initialized
) {
853 This
->wpOrigEditProc
= prev
->wpOrigEditProc
;
854 destroy_autocomplete_object(prev
);
857 This
->wpOrigEditProc
= (WNDPROC
) SetWindowLongPtrW(hwndEdit
, GWLP_WNDPROC
, (LONG_PTR
) ACEditSubclassProc
);
859 /* Keep at least one reference to the object until the edit window is destroyed */
860 IAutoComplete2_AddRef(&This
->IAutoComplete2_iface
);
862 if (This
->options
& ACO_AUTOSUGGEST
)
863 create_listbox(This
);
867 static const HKEY roots
[] = { HKEY_CURRENT_USER
, HKEY_LOCAL_MACHINE
};
876 /* pwszRegKeyPath contains the key as well as the value, so split it */
877 value
= strrchrW(pwzsRegKeyPath
, '\\');
878 len
= value
- pwzsRegKeyPath
;
880 if (value
&& (key
= heap_alloc((len
+1) * sizeof(*key
))) != NULL
)
882 memcpy(key
, pwzsRegKeyPath
, len
* sizeof(*key
));
886 for (i
= 0; i
< ARRAY_SIZE(roots
); i
++)
888 if (RegOpenKeyExW(roots
[i
], key
, 0, KEY_READ
, &hKey
) != ERROR_SUCCESS
)
890 sz
= MAX_PATH
* sizeof(WCHAR
);
892 while ((qc
= heap_alloc(sz
)) != NULL
)
894 res
= RegQueryValueExW(hKey
, value
, NULL
, &type
, qc
, &sz
);
895 if (res
== ERROR_SUCCESS
&& type
== REG_SZ
)
897 This
->quickComplete
= heap_realloc(qc
, sz
);
898 i
= ARRAY_SIZE(roots
);
902 if (res
!= ERROR_MORE_DATA
|| type
!= REG_SZ
)
911 if (!This
->quickComplete
&& pwszQuickComplete
)
913 size_t len
= strlenW(pwszQuickComplete
)+1;
914 if ((This
->quickComplete
= heap_alloc(len
* sizeof(WCHAR
))) != NULL
)
915 memcpy(This
->quickComplete
, pwszQuickComplete
, len
* sizeof(WCHAR
));
921 /**************************************************************************
922 * IAutoComplete2_fnGetOptions
924 static HRESULT WINAPI
IAutoComplete2_fnGetOptions(
925 IAutoComplete2
* iface
,
928 IAutoCompleteImpl
*This
= impl_from_IAutoComplete2(iface
);
931 TRACE("(%p) -> (%p)\n", This
, pdwFlag
);
933 *pdwFlag
= This
->options
;
938 /**************************************************************************
939 * IAutoComplete2_fnSetOptions
941 static HRESULT WINAPI
IAutoComplete2_fnSetOptions(
942 IAutoComplete2
* iface
,
945 IAutoCompleteImpl
*This
= impl_from_IAutoComplete2(iface
);
948 TRACE("(%p) -> (0x%x)\n", This
, dwFlag
);
950 This
->options
= dwFlag
;
952 if ((This
->options
& ACO_AUTOSUGGEST
) && This
->hwndEdit
&& !This
->hwndListBox
)
953 create_listbox(This
);
954 else if (!(This
->options
& ACO_AUTOSUGGEST
) && This
->hwndListBox
)
955 hide_listbox(This
, This
->hwndListBox
, TRUE
);
960 /**************************************************************************
961 * IAutoComplete2 VTable
963 static const IAutoComplete2Vtbl acvt
=
965 IAutoComplete2_fnQueryInterface
,
966 IAutoComplete2_fnAddRef
,
967 IAutoComplete2_fnRelease
,
968 IAutoComplete2_fnInit
,
969 IAutoComplete2_fnEnable
,
971 IAutoComplete2_fnSetOptions
,
972 IAutoComplete2_fnGetOptions
,
976 static HRESULT WINAPI
IAutoCompleteDropDown_fnQueryInterface(IAutoCompleteDropDown
*iface
,
977 REFIID riid
, LPVOID
*ppvObj
)
979 IAutoCompleteImpl
*This
= impl_from_IAutoCompleteDropDown(iface
);
980 return IAutoComplete2_QueryInterface(&This
->IAutoComplete2_iface
, riid
, ppvObj
);
983 static ULONG WINAPI
IAutoCompleteDropDown_fnAddRef(IAutoCompleteDropDown
*iface
)
985 IAutoCompleteImpl
*This
= impl_from_IAutoCompleteDropDown(iface
);
986 return IAutoComplete2_AddRef(&This
->IAutoComplete2_iface
);
989 static ULONG WINAPI
IAutoCompleteDropDown_fnRelease(IAutoCompleteDropDown
*iface
)
991 IAutoCompleteImpl
*This
= impl_from_IAutoCompleteDropDown(iface
);
992 return IAutoComplete2_Release(&This
->IAutoComplete2_iface
);
995 /**************************************************************************
996 * IAutoCompleteDropDown_fnGetDropDownStatus
998 static HRESULT WINAPI
IAutoCompleteDropDown_fnGetDropDownStatus(
999 IAutoCompleteDropDown
*iface
,
1001 LPWSTR
*ppwszString
)
1003 IAutoCompleteImpl
*This
= impl_from_IAutoCompleteDropDown(iface
);
1006 TRACE("(%p) -> (%p, %p)\n", This
, pdwFlags
, ppwszString
);
1008 dropped
= IsWindowVisible(This
->hwndListBox
);
1011 *pdwFlags
= (dropped
? ACDD_VISIBLE
: 0);
1017 sel
= SendMessageW(This
->hwndListBox
, LB_GETCURSEL
, 0, 0);
1022 len
= SendMessageW(This
->hwndListBox
, LB_GETTEXTLEN
, sel
, 0);
1023 *ppwszString
= CoTaskMemAlloc((len
+1)*sizeof(WCHAR
));
1024 SendMessageW(This
->hwndListBox
, LB_GETTEXT
, sel
, (LPARAM
)*ppwszString
);
1027 *ppwszString
= NULL
;
1030 *ppwszString
= NULL
;
1036 /**************************************************************************
1037 * IAutoCompleteDropDown_fnResetEnumarator
1039 static HRESULT WINAPI
IAutoCompleteDropDown_fnResetEnumerator(
1040 IAutoCompleteDropDown
*iface
)
1042 IAutoCompleteImpl
*This
= impl_from_IAutoCompleteDropDown(iface
);
1044 FIXME("(%p): stub\n", This
);
1049 /**************************************************************************
1050 * IAutoCompleteDropDown VTable
1052 static const IAutoCompleteDropDownVtbl acdropdownvt
=
1054 IAutoCompleteDropDown_fnQueryInterface
,
1055 IAutoCompleteDropDown_fnAddRef
,
1056 IAutoCompleteDropDown_fnRelease
,
1057 IAutoCompleteDropDown_fnGetDropDownStatus
,
1058 IAutoCompleteDropDown_fnResetEnumerator
,
1061 /**************************************************************************
1062 * IAutoComplete_Constructor
1064 HRESULT WINAPI
IAutoComplete_Constructor(IUnknown
* pUnkOuter
, REFIID riid
, LPVOID
* ppv
)
1066 IAutoCompleteImpl
*lpac
;
1069 if (pUnkOuter
&& !IsEqualIID (riid
, &IID_IUnknown
))
1070 return CLASS_E_NOAGGREGATION
;
1072 lpac
= heap_alloc_zero(sizeof(*lpac
));
1074 return E_OUTOFMEMORY
;
1077 lpac
->IAutoComplete2_iface
.lpVtbl
= &acvt
;
1078 lpac
->IAutoCompleteDropDown_iface
.lpVtbl
= &acdropdownvt
;
1079 lpac
->enabled
= TRUE
;
1080 lpac
->options
= ACO_AUTOAPPEND
;
1082 hr
= IAutoComplete2_QueryInterface(&lpac
->IAutoComplete2_iface
, riid
, ppv
);
1083 IAutoComplete2_Release(&lpac
->IAutoComplete2_iface
);
1085 TRACE("-- (%p)->\n",lpac
);