shell32/autocomplete: Implement ACO_FILTERPREFIXES.
Signed-off-by: Gabriel Ivăncescu <gabrielopcode@gmail.com> Signed-off-by: Huw Davies <huw@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
d288d4dbd1
commit
77b23a5e01
|
@ -22,7 +22,6 @@
|
||||||
/*
|
/*
|
||||||
TODO:
|
TODO:
|
||||||
- implement ACO_SEARCH style
|
- implement ACO_SEARCH style
|
||||||
- implement ACO_FILTERPREFIXES style
|
|
||||||
- implement ACO_RTLREADING style
|
- implement ACO_RTLREADING style
|
||||||
- implement ACO_WORD_FILTER style
|
- implement ACO_WORD_FILTER style
|
||||||
*/
|
*/
|
||||||
|
@ -81,6 +80,13 @@ enum autoappend_flag
|
||||||
autoappend_flag_displayempty
|
autoappend_flag_displayempty
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum prefix_filtering
|
||||||
|
{
|
||||||
|
prefix_filtering_none = 0, /* no prefix filtering (raw search) */
|
||||||
|
prefix_filtering_protocol, /* filter common protocol (e.g. http://) */
|
||||||
|
prefix_filtering_all /* filter all common prefixes (protocol & www. ) */
|
||||||
|
};
|
||||||
|
|
||||||
static const WCHAR autocomplete_propertyW[] = {'W','i','n','e',' ','A','u','t','o',
|
static const WCHAR autocomplete_propertyW[] = {'W','i','n','e',' ','A','u','t','o',
|
||||||
'c','o','m','p','l','e','t','e',' ',
|
'c','o','m','p','l','e','t','e',' ',
|
||||||
'c','o','n','t','r','o','l',0};
|
'c','o','n','t','r','o','l',0};
|
||||||
|
@ -103,14 +109,98 @@ static void set_text_and_selection(IAutoCompleteImpl *ac, HWND hwnd, WCHAR *text
|
||||||
CallWindowProcW(proc, hwnd, EM_SETSEL, start, end);
|
CallWindowProcW(proc, hwnd, EM_SETSEL, start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sort_strs_cmpfn(const void *a, const void *b)
|
static inline WCHAR *filter_protocol(WCHAR *str)
|
||||||
{
|
{
|
||||||
return strcmpiW(*(WCHAR* const*)a, *(WCHAR* const*)b);
|
static const WCHAR http[] = {'h','t','t','p'};
|
||||||
|
|
||||||
|
if (!strncmpW(str, http, ARRAY_SIZE(http)))
|
||||||
|
{
|
||||||
|
str += ARRAY_SIZE(http);
|
||||||
|
str += (*str == 's'); /* https */
|
||||||
|
if (str[0] == ':' && str[1] == '/' && str[2] == '/')
|
||||||
|
return str + 3;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sort_strs(WCHAR **strs, UINT numstrs)
|
static inline WCHAR *filter_www(WCHAR *str)
|
||||||
{
|
{
|
||||||
qsort(strs, numstrs, sizeof(*strs), sort_strs_cmpfn);
|
static const WCHAR www[] = {'w','w','w','.'};
|
||||||
|
|
||||||
|
if (!strncmpW(str, www, ARRAY_SIZE(www)))
|
||||||
|
return str + ARRAY_SIZE(www);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get the prefix filtering based on text, for example if text's prefix
|
||||||
|
is a protocol, then we return none because we actually filter nothing
|
||||||
|
*/
|
||||||
|
static enum prefix_filtering get_text_prefix_filtering(const WCHAR *text)
|
||||||
|
{
|
||||||
|
/* Convert to lowercase to perform case insensitive filtering,
|
||||||
|
using the longest possible prefix as the size of the buffer */
|
||||||
|
WCHAR buf[sizeof("https://")];
|
||||||
|
UINT i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(buf) - 1 && text[i]; i++)
|
||||||
|
buf[i] = tolowerW(text[i]);
|
||||||
|
buf[i] = '\0';
|
||||||
|
|
||||||
|
if (filter_protocol(buf)) return prefix_filtering_none;
|
||||||
|
if (filter_www(buf)) return prefix_filtering_protocol;
|
||||||
|
return prefix_filtering_all;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Filter the prefix of str based on the value of pfx_filter
|
||||||
|
This is used in sorting, so it's more performance sensitive
|
||||||
|
*/
|
||||||
|
static WCHAR *filter_str_prefix(WCHAR *str, enum prefix_filtering pfx_filter)
|
||||||
|
{
|
||||||
|
WCHAR *p = str;
|
||||||
|
|
||||||
|
if (pfx_filter == prefix_filtering_none) return str;
|
||||||
|
if ((p = filter_protocol(str))) str = p;
|
||||||
|
|
||||||
|
if (pfx_filter == prefix_filtering_protocol) return str;
|
||||||
|
if ((p = filter_www(str))) str = p;
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int sort_strs_cmpfn_impl(WCHAR *a, WCHAR *b, enum prefix_filtering pfx_filter)
|
||||||
|
{
|
||||||
|
WCHAR *str1 = filter_str_prefix(a, pfx_filter);
|
||||||
|
WCHAR *str2 = filter_str_prefix(b, pfx_filter);
|
||||||
|
return strcmpiW(str1, str2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sort_strs_cmpfn_none(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
return sort_strs_cmpfn_impl(*(WCHAR* const*)a, *(WCHAR* const*)b, prefix_filtering_none);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sort_strs_cmpfn_protocol(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
return sort_strs_cmpfn_impl(*(WCHAR* const*)a, *(WCHAR* const*)b, prefix_filtering_protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sort_strs_cmpfn_all(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
return sort_strs_cmpfn_impl(*(WCHAR* const*)a, *(WCHAR* const*)b, prefix_filtering_all);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int (*const sort_strs_cmpfn[])(const void*, const void*) =
|
||||||
|
{
|
||||||
|
sort_strs_cmpfn_none,
|
||||||
|
sort_strs_cmpfn_protocol,
|
||||||
|
sort_strs_cmpfn_all
|
||||||
|
};
|
||||||
|
|
||||||
|
static void sort_strs(WCHAR **strs, UINT numstrs, enum prefix_filtering pfx_filter)
|
||||||
|
{
|
||||||
|
qsort(strs, numstrs, sizeof(*strs), sort_strs_cmpfn[pfx_filter]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -119,7 +209,7 @@ static void sort_strs(WCHAR **strs, UINT numstrs)
|
||||||
We don't free the enumerated strings (except on error) to avoid needless
|
We don't free the enumerated strings (except on error) to avoid needless
|
||||||
copies, until the next reset (or the object itself is destroyed)
|
copies, until the next reset (or the object itself is destroyed)
|
||||||
*/
|
*/
|
||||||
static void enumerate_strings(IAutoCompleteImpl *ac)
|
static void enumerate_strings(IAutoCompleteImpl *ac, enum prefix_filtering pfx_filter)
|
||||||
{
|
{
|
||||||
UINT cur = 0, array_size = 1024;
|
UINT cur = 0, array_size = 1024;
|
||||||
LPOLESTR *strs = NULL, *tmp;
|
LPOLESTR *strs = NULL, *tmp;
|
||||||
|
@ -145,7 +235,7 @@ static void enumerate_strings(IAutoCompleteImpl *ac)
|
||||||
{
|
{
|
||||||
strs = tmp;
|
strs = tmp;
|
||||||
if (cur > 0)
|
if (cur > 0)
|
||||||
sort_strs(strs, cur);
|
sort_strs(strs, cur, pfx_filter);
|
||||||
|
|
||||||
ac->enum_strs = strs;
|
ac->enum_strs = strs;
|
||||||
ac->enum_strs_num = cur;
|
ac->enum_strs_num = cur;
|
||||||
|
@ -159,14 +249,14 @@ fail:
|
||||||
}
|
}
|
||||||
|
|
||||||
static UINT find_matching_enum_str(IAutoCompleteImpl *ac, UINT start, WCHAR *text,
|
static UINT find_matching_enum_str(IAutoCompleteImpl *ac, UINT start, WCHAR *text,
|
||||||
UINT len, int direction)
|
UINT len, enum prefix_filtering pfx_filter, int direction)
|
||||||
{
|
{
|
||||||
WCHAR **strs = ac->enum_strs;
|
WCHAR **strs = ac->enum_strs;
|
||||||
UINT index = ~0, a = start, b = ac->enum_strs_num;
|
UINT index = ~0, a = start, b = ac->enum_strs_num;
|
||||||
while (a < b)
|
while (a < b)
|
||||||
{
|
{
|
||||||
UINT i = (a + b - 1) / 2;
|
UINT i = (a + b - 1) / 2;
|
||||||
int cmp = strncmpiW(text, strs[i], len);
|
int cmp = strncmpiW(text, filter_str_prefix(strs[i], pfx_filter), len);
|
||||||
if (cmp == 0)
|
if (cmp == 0)
|
||||||
{
|
{
|
||||||
index = i;
|
index = i;
|
||||||
|
@ -406,7 +496,8 @@ static void autoappend_str(IAutoCompleteImpl *ac, WCHAR *text, UINT len, WCHAR *
|
||||||
}
|
}
|
||||||
|
|
||||||
static BOOL display_matching_strs(IAutoCompleteImpl *ac, WCHAR *text, UINT len,
|
static BOOL display_matching_strs(IAutoCompleteImpl *ac, WCHAR *text, UINT len,
|
||||||
HWND hwnd, enum autoappend_flag flag)
|
enum prefix_filtering pfx_filter, HWND hwnd,
|
||||||
|
enum autoappend_flag flag)
|
||||||
{
|
{
|
||||||
/* Return FALSE if we need to hide the listbox */
|
/* Return FALSE if we need to hide the listbox */
|
||||||
WCHAR **str = ac->enum_strs;
|
WCHAR **str = ac->enum_strs;
|
||||||
|
@ -416,17 +507,17 @@ static BOOL display_matching_strs(IAutoCompleteImpl *ac, WCHAR *text, UINT len,
|
||||||
/* Windows seems to disable autoappend if ACO_NOPREFIXFILTERING is set */
|
/* Windows seems to disable autoappend if ACO_NOPREFIXFILTERING is set */
|
||||||
if (!(ac->options & ACO_NOPREFIXFILTERING) && len)
|
if (!(ac->options & ACO_NOPREFIXFILTERING) && len)
|
||||||
{
|
{
|
||||||
start = find_matching_enum_str(ac, 0, text, len, -1);
|
start = find_matching_enum_str(ac, 0, text, len, pfx_filter, -1);
|
||||||
if (start == ~0)
|
if (start == ~0)
|
||||||
return (ac->options & ACO_AUTOSUGGEST) ? FALSE : TRUE;
|
return (ac->options & ACO_AUTOSUGGEST) ? FALSE : TRUE;
|
||||||
|
|
||||||
if (flag == autoappend_flag_yes)
|
if (flag == autoappend_flag_yes)
|
||||||
autoappend_str(ac, text, len, str[start], hwnd);
|
autoappend_str(ac, text, len, filter_str_prefix(str[start], pfx_filter), hwnd);
|
||||||
if (!(ac->options & ACO_AUTOSUGGEST))
|
if (!(ac->options & ACO_AUTOSUGGEST))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
/* Find the index beyond the last string that matches */
|
/* Find the index beyond the last string that matches */
|
||||||
end = find_matching_enum_str(ac, start + 1, text, len, 1);
|
end = find_matching_enum_str(ac, start + 1, text, len, pfx_filter, 1);
|
||||||
end = (end == ~0 ? start : end) + 1;
|
end = (end == ~0 ? start : end) + 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -450,10 +541,26 @@ static BOOL display_matching_strs(IAutoCompleteImpl *ac, WCHAR *text, UINT len,
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static enum prefix_filtering setup_prefix_filtering(IAutoCompleteImpl *ac, const WCHAR *text)
|
||||||
|
{
|
||||||
|
enum prefix_filtering pfx_filter;
|
||||||
|
if (!(ac->options & ACO_FILTERPREFIXES)) return prefix_filtering_none;
|
||||||
|
|
||||||
|
pfx_filter = get_text_prefix_filtering(text);
|
||||||
|
if (!ac->enum_strs) return pfx_filter;
|
||||||
|
|
||||||
|
/* If the prefix filtering is different, re-sort the filtered strings */
|
||||||
|
if (pfx_filter != get_text_prefix_filtering(ac->txtbackup))
|
||||||
|
sort_strs(ac->enum_strs, ac->enum_strs_num, pfx_filter);
|
||||||
|
|
||||||
|
return pfx_filter;
|
||||||
|
}
|
||||||
|
|
||||||
static void autocomplete_text(IAutoCompleteImpl *ac, HWND hwnd, enum autoappend_flag flag)
|
static void autocomplete_text(IAutoCompleteImpl *ac, HWND hwnd, enum autoappend_flag flag)
|
||||||
{
|
{
|
||||||
WCHAR *text;
|
WCHAR *text;
|
||||||
BOOL expanded = FALSE;
|
BOOL expanded = FALSE;
|
||||||
|
enum prefix_filtering pfx_filter;
|
||||||
UINT size, len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0);
|
UINT size, len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0);
|
||||||
|
|
||||||
if (flag != autoappend_flag_displayempty && len == 0)
|
if (flag != autoappend_flag_displayempty && len == 0)
|
||||||
|
@ -477,10 +584,12 @@ static void autocomplete_text(IAutoCompleteImpl *ac, HWND hwnd, enum autoappend_
|
||||||
flag = autoappend_flag_no;
|
flag = autoappend_flag_no;
|
||||||
expanded = aclist_expand(ac, text);
|
expanded = aclist_expand(ac, text);
|
||||||
}
|
}
|
||||||
|
pfx_filter = setup_prefix_filtering(ac, text);
|
||||||
|
|
||||||
if (expanded || !ac->enum_strs)
|
if (expanded || !ac->enum_strs)
|
||||||
{
|
{
|
||||||
if (!expanded) IEnumString_Reset(ac->enumstr);
|
if (!expanded) IEnumString_Reset(ac->enumstr);
|
||||||
enumerate_strings(ac);
|
enumerate_strings(ac, pfx_filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set txtbackup to point to text itself (which must not be released),
|
/* Set txtbackup to point to text itself (which must not be released),
|
||||||
|
@ -488,7 +597,7 @@ static void autocomplete_text(IAutoCompleteImpl *ac, HWND hwnd, enum autoappend_
|
||||||
heap_free(ac->txtbackup);
|
heap_free(ac->txtbackup);
|
||||||
ac->txtbackup = text;
|
ac->txtbackup = text;
|
||||||
|
|
||||||
if (!display_matching_strs(ac, text, len, hwnd, flag))
|
if (!display_matching_strs(ac, text, len, pfx_filter, hwnd, flag))
|
||||||
hide_listbox(ac, ac->hwndListBox, FALSE);
|
hide_listbox(ac, ac->hwndListBox, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -832,7 +941,6 @@ static HRESULT WINAPI IAutoComplete2_fnInit(
|
||||||
This, hwndEdit, punkACL, debugstr_w(pwzsRegKeyPath), debugstr_w(pwszQuickComplete));
|
This, hwndEdit, punkACL, debugstr_w(pwzsRegKeyPath), debugstr_w(pwszQuickComplete));
|
||||||
|
|
||||||
if (This->options & ACO_SEARCH) FIXME(" ACO_SEARCH not supported\n");
|
if (This->options & ACO_SEARCH) FIXME(" ACO_SEARCH not supported\n");
|
||||||
if (This->options & ACO_FILTERPREFIXES) FIXME(" ACO_FILTERPREFIXES not supported\n");
|
|
||||||
if (This->options & ACO_RTLREADING) FIXME(" ACO_RTLREADING not supported\n");
|
if (This->options & ACO_RTLREADING) FIXME(" ACO_RTLREADING not supported\n");
|
||||||
if (This->options & ACO_WORD_FILTER) FIXME(" ACO_WORD_FILTER not supported\n");
|
if (This->options & ACO_WORD_FILTER) FIXME(" ACO_WORD_FILTER not supported\n");
|
||||||
|
|
||||||
|
@ -965,6 +1073,7 @@ static HRESULT WINAPI IAutoComplete2_fnSetOptions(
|
||||||
DWORD dwFlag)
|
DWORD dwFlag)
|
||||||
{
|
{
|
||||||
IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
|
IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
|
||||||
|
DWORD changed = This->options ^ dwFlag;
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
TRACE("(%p) -> (0x%x)\n", This, dwFlag);
|
TRACE("(%p) -> (0x%x)\n", This, dwFlag);
|
||||||
|
@ -976,6 +1085,13 @@ static HRESULT WINAPI IAutoComplete2_fnSetOptions(
|
||||||
else if (!(This->options & ACO_AUTOSUGGEST) && This->hwndListBox)
|
else if (!(This->options & ACO_AUTOSUGGEST) && This->hwndListBox)
|
||||||
hide_listbox(This, This->hwndListBox, TRUE);
|
hide_listbox(This, This->hwndListBox, TRUE);
|
||||||
|
|
||||||
|
/* If ACO_FILTERPREFIXES changed we might have to reset the enumerator */
|
||||||
|
if ((changed & ACO_FILTERPREFIXES) && This->txtbackup)
|
||||||
|
{
|
||||||
|
if (get_text_prefix_filtering(This->txtbackup) != prefix_filtering_none)
|
||||||
|
IAutoCompleteDropDown_ResetEnumerator(&This->IAutoCompleteDropDown_iface);
|
||||||
|
}
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue