Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
v7: Add a space after for/switch.
dlls/shell32/autocomplete.c | 336 ++++++++++++++++++++++++-------------------- 1 file changed, 182 insertions(+), 154 deletions(-)
diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c index 9f35ff7..874d831 100644 --- a/dlls/shell32/autocomplete.c +++ b/dlls/shell32/autocomplete.c @@ -119,6 +119,76 @@ static size_t format_quick_complete(WCHAR *dst, const WCHAR *qc, const WCHAR *st return dst - base; }
+static void autocomplete_text(IAutoCompleteImpl *ac, WCHAR *text, UINT len, HWND hwnd, BOOL displayall) +{ + HRESULT hr; + UINT cpt; + + SendMessageW(ac->hwndListBox, LB_RESETCONTENT, 0, 0); + + /* Set txtbackup to point to text itself (which must not be released) */ + heap_free(ac->txtbackup); + ac->txtbackup = text; + + if (!displayall && !len) + return; + + IEnumString_Reset(ac->enumstr); + for (cpt = 0;;) + { + LPOLESTR strs = NULL; + ULONG fetched; + + hr = IEnumString_Next(ac->enumstr, 1, &strs, &fetched); + if (hr != S_OK) + break; + + if (!strncmpiW(text, strs, len)) + { + if (cpt == 0 && (ac->options & ACO_AUTOAPPEND)) + { + WCHAR buffW[255]; + + strcpyW(buffW, text); + strcatW(buffW, &strs[len]); + SetWindowTextW(hwnd, buffW); + SendMessageW(hwnd, EM_SETSEL, len, strlenW(strs)); + if (!(ac->options & ACO_AUTOSUGGEST)) + { + CoTaskMemFree(strs); + break; + } + } + + if (ac->options & ACO_AUTOSUGGEST) + SendMessageW(ac->hwndListBox, LB_ADDSTRING, 0, (LPARAM)strs); + + cpt++; + } + + CoTaskMemFree(strs); + } + + if (ac->options & ACO_AUTOSUGGEST) + { + if (cpt) + { + RECT r; + UINT height = SendMessageW(ac->hwndListBox, LB_GETITEMHEIGHT, 0, 0); + SendMessageW(ac->hwndListBox, LB_CARETOFF, 0, 0); + GetWindowRect(hwnd, &r); + SetParent(ac->hwndListBox, HWND_DESKTOP); + /* It seems that Windows XP displays 7 lines at most + and otherwise displays a vertical scroll bar */ + SetWindowPos(ac->hwndListBox, HWND_TOP, + r.left, r.bottom + 1, r.right - r.left, min(height * 7, height*(cpt+1)), + SWP_SHOWWINDOW ); + } + else + ShowWindow(ac->hwndListBox, SW_HIDE); + } +} + static void destroy_autocomplete_object(IAutoCompleteImpl *ac) { ac->hwndEdit = NULL; @@ -128,17 +198,122 @@ static void destroy_autocomplete_object(IAutoCompleteImpl *ac) }
/* + Helper for ACEditSubclassProc +*/ +static LRESULT ACEditSubclassProc_KeyUp(IAutoCompleteImpl *ac, HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam) +{ + WCHAR *text; + UINT len, size; + BOOL displayall = FALSE; + + len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0); + size = len + 1; + if (!(text = heap_alloc(size * sizeof(WCHAR)))) + return 0; + len = SendMessageW(hwnd, WM_GETTEXT, size, (LPARAM)text); + + switch (wParam) + { + case VK_RETURN: + /* If quickComplete is set and control is pressed, replace the string */ + if (ac->quickComplete && (GetKeyState(VK_CONTROL) & 0x8000)) + { + WCHAR *buf; + size_t sz = strlenW(ac->quickComplete) + 1 + len; + if ((buf = heap_alloc(sz * sizeof(WCHAR)))) + { + len = format_quick_complete(buf, ac->quickComplete, text, len); + SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)buf); + SendMessageW(hwnd, EM_SETSEL, 0, len); + heap_free(buf); + } + } + + if (ac->options & ACO_AUTOSUGGEST) + ShowWindow(ac->hwndListBox, SW_HIDE); + heap_free(text); + return 0; + case VK_LEFT: + case VK_RIGHT: + heap_free(text); + return 0; + case VK_UP: + case VK_DOWN: + /* Two cases here: + - if the listbox is not visible and ACO_UPDOWNKEYDROPSLIST is + set, display it with all the entries, without selecting any + - if the listbox is visible, change the selection + */ + if ( (ac->options & (ACO_AUTOSUGGEST | ACO_UPDOWNKEYDROPSLIST)) + && (!IsWindowVisible(ac->hwndListBox) && (! *text)) ) + { + /* We must display all the entries */ + displayall = TRUE; + } + else + { + INT count, sel; + heap_free(text); + if (!IsWindowVisible(ac->hwndListBox)) + return 0; + + count = SendMessageW(ac->hwndListBox, LB_GETCOUNT, 0, 0); + + /* Change the selection */ + sel = SendMessageW(ac->hwndListBox, LB_GETCURSEL, 0, 0); + if (wParam == VK_UP) + sel = ((sel - 1) < 0) ? count - 1 : sel - 1; + else + sel = ((sel + 1) >= count) ? -1 : sel + 1; + SendMessageW(ac->hwndListBox, LB_SETCURSEL, sel, 0); + if (sel >= 0) + { + WCHAR *msg; + UINT len; + + len = SendMessageW(ac->hwndListBox, LB_GETTEXTLEN, sel, 0); + if (!(msg = heap_alloc((len + 1) * sizeof(WCHAR)))) + return 0; + len = SendMessageW(ac->hwndListBox, LB_GETTEXT, sel, (LPARAM)msg); + SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)msg); + SendMessageW(hwnd, EM_SETSEL, len, len); + heap_free(msg); + } + else + { + UINT len; + SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)ac->txtbackup); + len = strlenW(ac->txtbackup); + SendMessageW(hwnd, EM_SETSEL, len, len); + } + return 0; + } + break; + case VK_BACK: + case VK_DELETE: + if ((! *text) && (ac->options & ACO_AUTOSUGGEST)) + { + heap_free(text); + ShowWindow(ac->hwndListBox, SW_HIDE); + return CallWindowProcW(ac->wpOrigEditProc, hwnd, uMsg, wParam, lParam); + } + break; + } + + if (len + 1 != size) + text = heap_realloc(text, (len + 1) * sizeof(WCHAR)); + + autocomplete_text(ac, text, len, hwnd, displayall); + return 0; +} + +/* Window procedure for autocompletion */ static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { IAutoCompleteImpl *This = GetPropW(hwnd, autocomplete_propertyW); - HRESULT hr; - WCHAR *hwndText; - UINT len, size, cpt; - RECT r; - BOOL displayall = FALSE; - int height, sel;
if (!This->enabled) return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
@@ -155,154 +330,7 @@ static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, } return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam); case WM_KEYUP: - len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0); - size = len + 1; - if (!(hwndText = heap_alloc(size * sizeof(WCHAR)))) - return 0; - len = SendMessageW(hwnd, WM_GETTEXT, size, (LPARAM)hwndText); - - switch(wParam) { - case VK_RETURN: - /* If quickComplete is set and control is pressed, replace the string */ - if (This->quickComplete && (GetKeyState(VK_CONTROL) & 0x8000)) - { - WCHAR *buf; - size_t sz = strlenW(This->quickComplete) + 1 + len; - if ((buf = heap_alloc(sz * sizeof(WCHAR)))) - { - len = format_quick_complete(buf, This->quickComplete, hwndText, len); - SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)buf); - SendMessageW(hwnd, EM_SETSEL, 0, len); - heap_free(buf); - } - } - - if (This->options & ACO_AUTOSUGGEST) - ShowWindow(This->hwndListBox, SW_HIDE); - heap_free(hwndText); - return 0; - case VK_LEFT: - case VK_RIGHT: - heap_free(hwndText); - return 0; - case VK_UP: - case VK_DOWN: - /* Two cases here : - - if the listbox is not visible, displays it - with all the entries if the style ACO_UPDOWNKEYDROPSLIST - is present but does not select anything. - - if the listbox is visible, change the selection - */ - if ( (This->options & (ACO_AUTOSUGGEST | ACO_UPDOWNKEYDROPSLIST)) - && (!IsWindowVisible(This->hwndListBox) && (! *hwndText)) ) - { - /* We must display all the entries */ - displayall = TRUE; - } else { - heap_free(hwndText); - if (IsWindowVisible(This->hwndListBox)) { - int count; - - count = SendMessageW(This->hwndListBox, LB_GETCOUNT, 0, 0); - /* Change the selection */ - sel = SendMessageW(This->hwndListBox, LB_GETCURSEL, 0, 0); - if (wParam == VK_UP) - sel = ((sel-1) < 0) ? count-1 : sel-1; - else - sel = ((sel+1) >= count) ? -1 : sel+1; - SendMessageW(This->hwndListBox, LB_SETCURSEL, sel, 0); - if (sel != -1) { - WCHAR *msg; - int len; - - len = SendMessageW(This->hwndListBox, LB_GETTEXTLEN, sel, 0); - if (!(msg = heap_alloc((len + 1) * sizeof(WCHAR)))) - return 0; - len = SendMessageW(This->hwndListBox, LB_GETTEXT, sel, (LPARAM)msg); - SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)msg); - SendMessageW(hwnd, EM_SETSEL, len, len); - heap_free(msg); - } else { - UINT len; - SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)This->txtbackup); - len = strlenW(This->txtbackup); - SendMessageW(hwnd, EM_SETSEL, len, len); - } - } - return 0; - } - break; - case VK_BACK: - case VK_DELETE: - if ((! *hwndText) && (This->options & ACO_AUTOSUGGEST)) { - heap_free(hwndText); - ShowWindow(This->hwndListBox, SW_HIDE); - return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam); - } - break; - } - - if (len + 1 != size) - hwndText = heap_realloc(hwndText, (len + 1) * sizeof(WCHAR)); - - SendMessageW(This->hwndListBox, LB_RESETCONTENT, 0, 0); - - /* Set txtbackup to point to hwndText itself (which must not be released) */ - heap_free(This->txtbackup); - This->txtbackup = hwndText; - - if (!displayall && !len) - break; - - IEnumString_Reset(This->enumstr); - for(cpt = 0;;) { - LPOLESTR strs = NULL; - ULONG fetched; - - hr = IEnumString_Next(This->enumstr, 1, &strs, &fetched); - if (hr != S_OK) - break; - - if (!strncmpiW(hwndText, strs, len)) { - if (cpt == 0 && (This->options & ACO_AUTOAPPEND)) { - WCHAR buffW[255]; - - strcpyW(buffW, hwndText); - strcatW(buffW, &strs[len]); - SetWindowTextW(hwnd, buffW); - SendMessageW(hwnd, EM_SETSEL, len, strlenW(strs)); - if (!(This->options & ACO_AUTOSUGGEST)) { - CoTaskMemFree(strs); - break; - } - } - - if (This->options & ACO_AUTOSUGGEST) - SendMessageW(This->hwndListBox, LB_ADDSTRING, 0, (LPARAM)strs); - - cpt++; - } - - CoTaskMemFree(strs); - } - - if (This->options & ACO_AUTOSUGGEST) { - if (cpt) { - height = SendMessageW(This->hwndListBox, LB_GETITEMHEIGHT, 0, 0); - SendMessageW(This->hwndListBox, LB_CARETOFF, 0, 0); - GetWindowRect(hwnd, &r); - SetParent(This->hwndListBox, HWND_DESKTOP); - /* It seems that Windows XP displays 7 lines at most - and otherwise displays a vertical scroll bar */ - SetWindowPos(This->hwndListBox, HWND_TOP, - r.left, r.bottom + 1, r.right - r.left, min(height * 7, height*(cpt+1)), - SWP_SHOWWINDOW ); - } else { - ShowWindow(This->hwndListBox, SW_HIDE); - } - } - - break; + return ACEditSubclassProc_KeyUp(This, hwnd, uMsg, wParam, lParam); case WM_DESTROY: { WNDPROC proc = This->wpOrigEditProc;
AutoComplete currently shows up when the user releases a key, which is wrong. Windows does it when the user presses a key, so use both WM_KEYDOWN and WM_CHAR and redesign it so that it matches Windows behavior.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
v7: Don't handle control characters and WM_PASTE in this patch.
dlls/shell32/autocomplete.c | 115 +++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 50 deletions(-)
diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c index 874d831..183b37b 100644 --- a/dlls/shell32/autocomplete.c +++ b/dlls/shell32/autocomplete.c @@ -79,6 +79,13 @@ typedef struct AUTOCOMPLETEOPTIONS options; } IAutoCompleteImpl;
+enum autoappend_flag +{ + autoappend_flag_yes, + autoappend_flag_no, + autoappend_flag_displayempty +}; + 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}; @@ -119,10 +126,25 @@ static size_t format_quick_complete(WCHAR *dst, const WCHAR *qc, const WCHAR *st return dst - base; }
-static void autocomplete_text(IAutoCompleteImpl *ac, WCHAR *text, UINT len, HWND hwnd, BOOL displayall) +static void autocomplete_text(IAutoCompleteImpl *ac, HWND hwnd, enum autoappend_flag flag) { HRESULT hr; - UINT cpt; + WCHAR *text; + UINT cpt, size, len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0); + + if (flag != autoappend_flag_displayempty && len == 0) + { + if (ac->options & ACO_AUTOSUGGEST) + ShowWindow(ac->hwndListBox, SW_HIDE); + return; + } + + size = len + 1; + if (!(text = heap_alloc(size * sizeof(WCHAR)))) + return; + len = SendMessageW(hwnd, WM_GETTEXT, size, (LPARAM)text); + if (len + 1 != size) + text = heap_realloc(text, (len + 1) * sizeof(WCHAR));
SendMessageW(ac->hwndListBox, LB_RESETCONTENT, 0, 0);
@@ -130,9 +152,6 @@ static void autocomplete_text(IAutoCompleteImpl *ac, WCHAR *text, UINT len, HWND heap_free(ac->txtbackup); ac->txtbackup = text;
- if (!displayall && !len) - return; - IEnumString_Reset(ac->enumstr); for (cpt = 0;;) { @@ -145,7 +164,7 @@ static void autocomplete_text(IAutoCompleteImpl *ac, WCHAR *text, UINT len, HWND
if (!strncmpiW(text, strs, len)) { - if (cpt == 0 && (ac->options & ACO_AUTOAPPEND)) + if (cpt == 0 && flag == autoappend_flag_yes) { WCHAR buffW[255];
@@ -200,27 +219,23 @@ static void destroy_autocomplete_object(IAutoCompleteImpl *ac) /* Helper for ACEditSubclassProc */ -static LRESULT ACEditSubclassProc_KeyUp(IAutoCompleteImpl *ac, HWND hwnd, UINT uMsg, - WPARAM wParam, LPARAM lParam) +static LRESULT ACEditSubclassProc_KeyDown(IAutoCompleteImpl *ac, HWND hwnd, UINT uMsg, + WPARAM wParam, LPARAM lParam) { - WCHAR *text; - UINT len, size; - BOOL displayall = FALSE; - - len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0); - size = len + 1; - if (!(text = heap_alloc(size * sizeof(WCHAR)))) - return 0; - len = SendMessageW(hwnd, WM_GETTEXT, size, (LPARAM)text); - switch (wParam) { case VK_RETURN: /* If quickComplete is set and control is pressed, replace the string */ if (ac->quickComplete && (GetKeyState(VK_CONTROL) & 0x8000)) { - WCHAR *buf; - size_t sz = strlenW(ac->quickComplete) + 1 + len; + WCHAR *text, *buf; + size_t sz; + UINT len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0); + if (!(text = heap_alloc((len + 1) * sizeof(WCHAR)))) + return 0; + len = SendMessageW(hwnd, WM_GETTEXT, len + 1, (LPARAM)text); + sz = strlenW(ac->quickComplete) + 1 + len; + if ((buf = heap_alloc(sz * sizeof(WCHAR)))) { len = format_quick_complete(buf, ac->quickComplete, text, len); @@ -228,16 +243,16 @@ static LRESULT ACEditSubclassProc_KeyUp(IAutoCompleteImpl *ac, HWND hwnd, UINT u SendMessageW(hwnd, EM_SETSEL, 0, len); heap_free(buf); } + + if (ac->options & ACO_AUTOSUGGEST) + ShowWindow(ac->hwndListBox, SW_HIDE); + heap_free(text); + return 0; }
if (ac->options & ACO_AUTOSUGGEST) ShowWindow(ac->hwndListBox, SW_HIDE); - heap_free(text); - return 0; - case VK_LEFT: - case VK_RIGHT: - heap_free(text); - return 0; + break; case VK_UP: case VK_DOWN: /* Two cases here: @@ -245,19 +260,20 @@ static LRESULT ACEditSubclassProc_KeyUp(IAutoCompleteImpl *ac, HWND hwnd, UINT u set, display it with all the entries, without selecting any - if the listbox is visible, change the selection */ - if ( (ac->options & (ACO_AUTOSUGGEST | ACO_UPDOWNKEYDROPSLIST)) - && (!IsWindowVisible(ac->hwndListBox) && (! *text)) ) + if (!(ac->options & ACO_AUTOSUGGEST)) + break; + + if (!IsWindowVisible(ac->hwndListBox)) { - /* We must display all the entries */ - displayall = TRUE; + if (ac->options & ACO_UPDOWNKEYDROPSLIST) + { + autocomplete_text(ac, hwnd, autoappend_flag_displayempty); + return 0; + } } else { INT count, sel; - heap_free(text); - if (!IsWindowVisible(ac->hwndListBox)) - return 0; - count = SendMessageW(ac->hwndListBox, LB_GETCOUNT, 0, 0);
/* Change the selection */ @@ -290,22 +306,14 @@ static LRESULT ACEditSubclassProc_KeyUp(IAutoCompleteImpl *ac, HWND hwnd, UINT u return 0; } break; - case VK_BACK: case VK_DELETE: - if ((! *text) && (ac->options & ACO_AUTOSUGGEST)) - { - heap_free(text); - ShowWindow(ac->hwndListBox, SW_HIDE); - return CallWindowProcW(ac->wpOrigEditProc, hwnd, uMsg, wParam, lParam); - } - break; + { + LRESULT ret = CallWindowProcW(ac->wpOrigEditProc, hwnd, uMsg, wParam, lParam); + autocomplete_text(ac, hwnd, autoappend_flag_no); + return ret; + } } - - if (len + 1 != size) - text = heap_realloc(text, (len + 1) * sizeof(WCHAR)); - - autocomplete_text(ac, text, len, hwnd, displayall); - return 0; + return CallWindowProcW(ac->wpOrigEditProc, hwnd, uMsg, wParam, lParam); }
/* @@ -314,6 +322,7 @@ static LRESULT ACEditSubclassProc_KeyUp(IAutoCompleteImpl *ac, HWND hwnd, UINT u static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { IAutoCompleteImpl *This = GetPropW(hwnd, autocomplete_propertyW); + LRESULT ret;
if (!This->enabled) return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
@@ -329,8 +338,14 @@ static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, ShowWindow(This->hwndListBox, SW_HIDE); } return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam); - case WM_KEYUP: - return ACEditSubclassProc_KeyUp(This, hwnd, uMsg, wParam, lParam); + case WM_KEYDOWN: + return ACEditSubclassProc_KeyDown(This, hwnd, uMsg, wParam, lParam); + case WM_CHAR: + case WM_UNICHAR: + ret = CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam); + autocomplete_text(This, hwnd, (This->options & ACO_AUTOAPPEND) + ? autoappend_flag_yes : autoappend_flag_no); + return ret; case WM_DESTROY: { WNDPROC proc = This->wpOrigEditProc;
Signed-off-by: Huw Davies huw@codeweavers.com
We must not auto-append on control characters, most notably Backspace, but also ^X (cut) and so on. An exception is ^V, which auto-appends on Windows, so this is needed to match Windows behavior (also needed when receiving the ^V message directly).
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/shell32/autocomplete.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c index 183b37b..429b4fb 100644 --- a/dlls/shell32/autocomplete.c +++ b/dlls/shell32/autocomplete.c @@ -343,7 +343,8 @@ static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, case WM_CHAR: case WM_UNICHAR: ret = CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam); - autocomplete_text(This, hwnd, (This->options & ACO_AUTOAPPEND) + autocomplete_text(This, hwnd, (This->options & ACO_AUTOAPPEND) && + (wParam >= ' ' || wParam == 0x16 /* ^V (paste) */) ? autoappend_flag_yes : autoappend_flag_no); return ret; case WM_DESTROY:
On Mon, Sep 17, 2018 at 10:23:11PM +0300, Gabriel Ivăncescu wrote:
We must not auto-append on control characters, most notably Backspace, but also ^X (cut) and so on. An exception is ^V, which auto-appends on Windows, so this is needed to match Windows behavior (also needed when receiving the ^V message directly).
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com
dlls/shell32/autocomplete.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c index 183b37b..429b4fb 100644 --- a/dlls/shell32/autocomplete.c +++ b/dlls/shell32/autocomplete.c @@ -343,7 +343,8 @@ static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, case WM_CHAR: case WM_UNICHAR: ret = CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
autocomplete_text(This, hwnd, (This->options & ACO_AUTOAPPEND)
autocomplete_text(This, hwnd, (This->options & ACO_AUTOAPPEND) &&
(wParam >= ' ' || wParam == 0x16 /* ^V (paste) */) ? autoappend_flag_yes : autoappend_flag_no);
Let's leave the ^V out of this.
On Tue, Sep 18, 2018 at 3:24 PM, Huw Davies huw@codeweavers.com wrote:
Let's leave the ^V out of this.
Ok. I will resend after they are committed. And after those (resent) get in, the next patch series will include the WM_PASTE also for proper behavior here.
The previous code caps the auto-append text at 255 characters, which can be easily exploited. It's also less efficient as it scans the string multiple times.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/shell32/autocomplete.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-)
diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c index 429b4fb..4dafb22 100644 --- a/dlls/shell32/autocomplete.c +++ b/dlls/shell32/autocomplete.c @@ -126,6 +126,28 @@ static size_t format_quick_complete(WCHAR *dst, const WCHAR *qc, const WCHAR *st return dst - base; }
+static void autoappend_str(IAutoCompleteImpl *ac, WCHAR *text, UINT len, WCHAR *str, HWND hwnd) +{ + WCHAR *tmp; + size_t size; + + /* The character capitalization can be different, + so merge text and str into a new string */ + size = len + strlenW(&str[len]) + 1; + + if ((tmp = heap_alloc(size * sizeof(*tmp)))) + { + memcpy(tmp, text, len * sizeof(*tmp)); + memcpy(&tmp[len], &str[len], (size - len) * sizeof(*tmp)); + } + else tmp = str; + + SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)tmp); + SendMessageW(hwnd, EM_SETSEL, len, size - 1); + if (tmp != str) + heap_free(tmp); +} + static void autocomplete_text(IAutoCompleteImpl *ac, HWND hwnd, enum autoappend_flag flag) { HRESULT hr; @@ -166,12 +188,7 @@ static void autocomplete_text(IAutoCompleteImpl *ac, HWND hwnd, enum autoappend_ { if (cpt == 0 && flag == autoappend_flag_yes) { - WCHAR buffW[255]; - - strcpyW(buffW, text); - strcatW(buffW, &strs[len]); - SetWindowTextW(hwnd, buffW); - SendMessageW(hwnd, EM_SETSEL, len, strlenW(strs)); + autoappend_str(ac, text, len, strs, hwnd); if (!(ac->options & ACO_AUTOSUGGEST)) { CoTaskMemFree(strs);
On Mon, Sep 17, 2018 at 10:23:12PM +0300, Gabriel Ivăncescu wrote:
The previous code caps the auto-append text at 255 characters, which can be easily exploited. It's also less efficient as it scans the string multiple times.
Thia and the other patches in the series look ok. Please resend after [1] and [2] have been committed with a fixed up version of [3].
Huw.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/shell32/autocomplete.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c index 4dafb22..8513b0a 100644 --- a/dlls/shell32/autocomplete.c +++ b/dlls/shell32/autocomplete.c @@ -217,7 +217,7 @@ static void autocomplete_text(IAutoCompleteImpl *ac, HWND hwnd, enum autoappend_ /* It seems that Windows XP displays 7 lines at most and otherwise displays a vertical scroll bar */ SetWindowPos(ac->hwndListBox, HWND_TOP, - r.left, r.bottom + 1, r.right - r.left, min(height * 7, height*(cpt+1)), + r.left, r.bottom + 1, r.right - r.left, height * min(cpt + 1, 7), SWP_SHOWWINDOW ); } else
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Avoids a race condition in extremely rare situations (i.e. if another thread calls the window procedure while the property is removed, it will crash). It should never happen since it should be tied to 1 thread, but there's no harm to it anyway and it feels better to me. (other patches don't depend on this one so it doesn't matter anyway if you feel it's pointless)
dlls/shell32/autocomplete.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c index 8513b0a..dc9e5c7 100644 --- a/dlls/shell32/autocomplete.c +++ b/dlls/shell32/autocomplete.c @@ -368,8 +368,8 @@ static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, { WNDPROC proc = This->wpOrigEditProc;
- RemovePropW(hwnd, autocomplete_propertyW); SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)proc); + RemovePropW(hwnd, autocomplete_propertyW); destroy_autocomplete_object(This); return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam); }
There's no need to send a WM_KEYUP anymore since it now matches Windows behavior.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/shell32/tests/autocomplete.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/dlls/shell32/tests/autocomplete.c b/dlls/shell32/tests/autocomplete.c index 3c8de88..b9c6374 100644 --- a/dlls/shell32/tests/autocomplete.c +++ b/dlls/shell32/tests/autocomplete.c @@ -386,8 +386,7 @@ static void test_custom_source(void) ok(hr == S_OK, "IAutoComplete_Init failed: %x\n", hr);
SendMessageW(hwnd_edit, WM_CHAR, 'a', 1); - /* Send a keyup message since wine doesn't handle WM_CHAR yet */ - SendMessageW(hwnd_edit, WM_KEYUP, 'u', 1); + SendMessageW(hwnd_edit, WM_CHAR, 'u', 1); Sleep(100); while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {