[PATCH 2/4] shell32/autocomplete: Implement ACO_FILTERPREFIXES
Gabriel Ivăncescu
gabrielopcode at gmail.com
Wed Nov 28 10:27:47 CST 2018
Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---
dlls/shell32/autocomplete.c | 148 ++++++++++++++++++++++++++++++++----
1 file changed, 132 insertions(+), 16 deletions(-)
diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c
index b9ea68f..9292eec 100644
--- a/dlls/shell32/autocomplete.c
+++ b/dlls/shell32/autocomplete.c
@@ -22,7 +22,6 @@
/*
TODO:
- implement ACO_SEARCH style
- - implement ACO_FILTERPREFIXES style
- implement ACO_RTLREADING style
- implement ACO_WORD_FILTER style
*/
@@ -81,6 +80,13 @@ enum autoappend_flag
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',
'c','o','m','p','l','e','t','e',' ',
'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);
}
-static int sort_strs_cmpfn(const void *a, const void *b)
+static inline WCHAR *filter_protocol(WCHAR *str)
+{
+ 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 inline WCHAR *filter_www(WCHAR *str)
+{
+ 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 strcmpiW(*(WCHAR* const*)a, *(WCHAR* const*)b);
+ return sort_strs_cmpfn_impl(*(WCHAR* const*)a, *(WCHAR* const*)b, prefix_filtering_all);
}
-static void sort_strs(WCHAR **strs, UINT numstrs)
+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);
+ 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
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;
LPOLESTR *strs = NULL, *tmp;
@@ -145,7 +235,7 @@ static void enumerate_strings(IAutoCompleteImpl *ac)
{
strs = tmp;
if (cur > 0)
- sort_strs(strs, cur);
+ sort_strs(strs, cur, pfx_filter);
ac->enum_strs = strs;
ac->enum_strs_num = cur;
@@ -159,14 +249,14 @@ fail:
}
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;
UINT index = ~0, a = start, b = ac->enum_strs_num;
while (a < b)
{
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)
{
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,
- 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 */
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 */
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)
return (ac->options & ACO_AUTOSUGGEST) ? FALSE : TRUE;
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))
return TRUE;
/* 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;
}
else
@@ -450,10 +541,26 @@ static BOOL display_matching_strs(IAutoCompleteImpl *ac, WCHAR *text, UINT len,
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)
{
WCHAR *text;
BOOL expanded = FALSE;
+ enum prefix_filtering pfx_filter;
UINT size, len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 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;
expanded = aclist_expand(ac, text);
}
+ pfx_filter = setup_prefix_filtering(ac, text);
+
if (expanded || !ac->enum_strs)
{
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),
@@ -488,7 +597,7 @@ static void autocomplete_text(IAutoCompleteImpl *ac, HWND hwnd, enum autoappend_
heap_free(ac->txtbackup);
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);
}
@@ -832,7 +941,6 @@ static HRESULT WINAPI IAutoComplete2_fnInit(
This, hwndEdit, punkACL, debugstr_w(pwzsRegKeyPath), debugstr_w(pwszQuickComplete));
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_WORD_FILTER) FIXME(" ACO_WORD_FILTER not supported\n");
@@ -965,6 +1073,7 @@ static HRESULT WINAPI IAutoComplete2_fnSetOptions(
DWORD dwFlag)
{
IAutoCompleteImpl *This = impl_from_IAutoComplete2(iface);
+ DWORD changed = This->options ^ dwFlag;
HRESULT hr = S_OK;
TRACE("(%p) -> (0x%x)\n", This, dwFlag);
@@ -976,6 +1085,13 @@ static HRESULT WINAPI IAutoComplete2_fnSetOptions(
else if (!(This->options & ACO_AUTOSUGGEST) && This->hwndListBox)
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;
}
--
2.19.1
More information about the wine-devel
mailing list