[PATCH v3 07/10] shell32/autocomplete: Redesign the window proc to trigger on key presses instead of key release

Gabriel Ivăncescu gabrielopcode at gmail.com
Sat Sep 8 06:50:53 CDT 2018


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>
---

v3: I've split as much as possible without a complete rewrite of the
function. Most of the other patches in the series (that will follow) will
still apply now with just a small rebase, so please try to consider it in
this shape (i.e. without rewriting it completely with helper functions which
would also make code sharing harder in this particular situation) since I
have more patches pending that will fix AutoComplete for good. Of course,
they will introduce some helper functions on their own without rewriting
the function.

The function can always be split into helper functions later, once all of
these patch series are settled (and ones I haven't sent yet, including
implementing ResetEnumerator), because it will already change over the
course of the series and some parts might be reduced (like the auto-append)
depending if they are accepted. IMO it's better to wait and see how it
shapes up first before refactoring it, if that's truly needed.

Besides, it's not worse than the current code mess, and honestly I wouldn't
want the current code to still be there for another Wine release, it's
just too broken and unusable for anything that relies on AutoComplete (it's
actually frustrating for users, even releasing the Shift key is annoying).

 dlls/shell32/autocomplete.c | 97 +++++++++++++++++++++++++++------------------
 1 file changed, 58 insertions(+), 39 deletions(-)

diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c
index 5038bb5..c46b897 100644
--- a/dlls/shell32/autocomplete.c
+++ b/dlls/shell32/autocomplete.c
@@ -136,10 +136,11 @@ static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
 {
     IAutoCompleteImpl *This = GetPropW(hwnd, autocomplete_propertyW);
     HRESULT hr;
+    LRESULT ret;
     WCHAR *hwndText;
     UINT len, size, cpt;
     RECT r;
-    BOOL displayall = FALSE;
+    BOOLEAN displayall = FALSE, noautoappend = !(This->options & ACO_AUTOAPPEND);
     int height, sel;
 
     if (!This->enabled) return CallWindowProcW(This->wpOrigEditProc, hwnd, uMsg, wParam, lParam);
@@ -156,20 +157,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:
-            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);
-
+        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 sz = strlenW(This->quickComplete) + 1 + len;
+                        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 + len;
+
                         if ((buf = heap_alloc(sz * sizeof(WCHAR))))
                         {
                             format_quick_complete(buf, This->quickComplete, hwndText, len);
@@ -177,32 +178,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);
-                    heap_free(hwndText);
-                    return 0;
-                case VK_LEFT:
-                case VK_RIGHT:
-                    heap_free(hwndText);
-                    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 {
-                        heap_free(hwndText);
-                        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 handle_char;
+                            }
+                        } else {
                             int count;
 
                             count = SendMessageW(This->hwndListBox, LB_GETCOUNT, 0, 0);
@@ -229,20 +229,40 @@ 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)) {
-                        heap_free(hwndText);
-                        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
+            {
+              handle_char:
+                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);
 
@@ -253,7 +273,7 @@ static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
             This->txtbackup = hwndText;
 
             if (!displayall && !len)
-                break;
+                return ret;
 
             IEnumString_Reset(This->enumstr);
             for(cpt = 0;;) {
@@ -265,7 +285,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);
@@ -302,8 +322,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