[PATCH v2 08/11] shell32/autocomplete: Redesign the window proc to trigger on key presses instead of key release
Huw Davies
huw at codeweavers.com
Fri Sep 7 04:07:54 CDT 2018
On Thu, Sep 06, 2018 at 09:26:18PM +0300, Gabriel Ivăncescu wrote:
> 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 at gmail.com>
> ---
>
> v2: Most of it has been split into multiple patches, and I split it as much
> as I could without introducing extra code that would just get removed on
> the very next patch in the series.
>
> A difficulty here stems from the fact that we need to handle not just
> WM_KEYDOWN but also WM_CHAR, which is harder than WM_KEYUP especially when
> trying to share code paths because control characters have to be handled
> separately (for auto-append purposes).
>
> For example VK_BACK is not processed in WM_KEYDOWN because it's done during
> WM_CHAR as the escape '\b' along the rest. That's not the case with VK_DELETE
> which has no WM_CHAR escape. This is important since the edit control *must*
> receive the message before us, otherwise the behavior will be wrong, so
> they have to be deferred to WM_CHAR and we can't do it in WM_KEYDOWN. That's
> also why we always make sure to pass the corresponding message to the edit
> control first.
>
> Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
> ---
>
> dlls/shell32/autocomplete.c | 111 ++++++++++++++++++++++++++------------------
> 1 file changed, 67 insertions(+), 44 deletions(-)
>
> diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c
> index 4485167..fa965c0 100644
> --- a/dlls/shell32/autocomplete.c
> +++ b/dlls/shell32/autocomplete.c
> @@ -108,10 +108,12 @@ static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
> {
> IAutoCompleteImpl *This = GetPropW(hwnd, autocomplete_propertyW);
> HRESULT hr;
> - WCHAR hwndText[255];
> + LRESULT ret;
> + WCHAR *hwndText;
While changing hwndText from a static buffer to a dynamically
allocated array is a good plan, it doesn't belong in this patch.
This whole function is a mess anyway. It would probably help
to have the WM_KEYUP (or the new WM_KEYDOWN/WM_CHAR) handling
done in helper functions.
> + UINT len, size, cpt;
> RECT r;
> - BOOL displayall = FALSE;
> - int cpt, height, sel;
> + BOOLEAN displayall = FALSE, noautoappend = !(This->options & ACO_AUTOAPPEND);
> + int height, sel;
>
> if (!This->enabled) return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
>
> @@ -127,21 +129,20 @@ 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:
> - {
> - int len;
> -
> - GetWindowTextW(hwnd, hwndText, ARRAY_SIZE(hwndText));
> -
> + case WM_KEYDOWN:
> 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 len = strlenW(hwndText);
> - size_t sz = strlenW(This->quickComplete) + 1;
> - sz += max(len, 2); /* %s is 2 chars */
> + size_t sz;
> + len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0) + 1; /* include NUL */
> + if (!(hwndText = heap_alloc(len * sizeof(WCHAR))))
> + return 0;
> + len = SendMessageW(hwnd, WM_GETTEXT, len, (LPARAM)hwndText);
> +
> + sz = strlenW(This->quickComplete)+1 + max(len, 2); /* %s is 2 chars */
>
> /* Replace the first %s directly without using snprintf, to avoid
> exploits since the format string can be retrieved from the registry */
> @@ -162,29 +163,31 @@ static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
> SendMessageW(hwnd, EM_SETSEL, 0, sz-1);
> heap_free(buf);
> }
> + if (This->options & ACO_AUTOSUGGEST)
> + ShowWindow(This->hwndListBox, SW_HIDE);
> + heap_free(hwndText);
> + return 0;
> }
>
> if (This->options & ACO_AUTOSUGGEST)
> ShowWindow(This->hwndListBox, SW_HIDE);
> - return 0;
> - case VK_LEFT:
> - case VK_RIGHT:
> - return 0;
> + break;
> 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.
> + /* 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 ( (This->options & (ACO_AUTOSUGGEST | ACO_UPDOWNKEYDROPSLIST))
> - && (!IsWindowVisible(This->hwndListBox) && (! *hwndText)) )
> - {
> - /* We must display all the entries */
> - displayall = TRUE;
> - } else {
> - if (IsWindowVisible(This->hwndListBox)) {
> + if (This->options & ACO_AUTOSUGGEST) {
> + if (!IsWindowVisible(This->hwndListBox)) {
> + if (This->options & ACO_UPDOWNKEYDROPSLIST) {
> + ret = 0;
> + displayall = TRUE;
> + noautoappend = TRUE;
> + goto autocomplete_text;
> + }
> + } else {
> int count;
>
> count = SendMessageW(This->hwndListBox, LB_GETCOUNT, 0, 0);
> @@ -211,29 +214,51 @@ static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
> len = strlenW(This->txtbackup);
> SendMessageW(hwnd, EM_SETSEL, len, len);
> }
> + return 0;
> }
> - return 0;
> }
> break;
> - case VK_BACK:
> case VK_DELETE:
> - if ((! *hwndText) && (This->options & ACO_AUTOSUGGEST)) {
> - ShowWindow(This->hwndListBox, SW_HIDE);
> - return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
> - }
> - break;
> + ret = CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
> + goto handle_control_char;
> + }
> + return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
> + case WM_CHAR:
> + case WM_UNICHAR:
> + ret = CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
> + if (wParam < ' ')
> + {
> + handle_control_char:
> + len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0);
> + if ((This->options & ACO_AUTOSUGGEST) && len == 0)
> + {
> + ShowWindow(This->hwndListBox, SW_HIDE);
> + return ret;
> + }
> + if (wParam != 0x16 /* ^V (paste) */)
> + noautoappend = TRUE;
> }
> + else
> + {
> + autocomplete_text:
> + len = SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0);
> + }
> +
> + size = len+1;
> + if (!(hwndText = heap_alloc(size * sizeof(WCHAR))))
> + return ret;
> + len = SendMessageW(hwnd, WM_GETTEXT, size, (LPARAM)hwndText);
> + if (len+1 != size)
> + hwndText = heap_realloc(hwndText, len+1);
>
> SendMessageW(This->hwndListBox, LB_RESETCONTENT, 0, 0);
>
> + /* Set txtbackup to point to hwndText itself (which must not be released) */
> heap_free(This->txtbackup);
> - len = strlenW(hwndText);
> - This->txtbackup = heap_alloc((len + 1)*sizeof(WCHAR));
> - lstrcpyW(This->txtbackup, hwndText);
> + This->txtbackup = hwndText;
>
> - /* Returns if there is no text to search and we doesn't want to display all the entries */
> - if ((!displayall) && (! *hwndText) )
> - break;
> + if (!displayall && !len)
> + return ret;
>
> IEnumString_Reset(This->enumstr);
> for(cpt = 0;;) {
> @@ -245,7 +270,7 @@ static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
> break;
>
> if (!strncmpiW(hwndText, strs, len)) {
> - if (cpt == 0 && (This->options & ACO_AUTOAPPEND)) {
> + if (cpt == 0 && noautoappend == FALSE) {
> WCHAR buffW[255];
>
> strcpyW(buffW, hwndText);
> @@ -282,9 +307,7 @@ static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
> ShowWindow(This->hwndListBox, SW_HIDE);
> }
> }
> -
> - break;
> - }
> + return ret;
> case WM_DESTROY:
> {
> WNDPROC proc = This->wpOrigEditProc;
> --
> 1.9.1
>
>
>
More information about the wine-devel
mailing list