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