[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