[PATCH v2 08/11] shell32/autocomplete: Redesign the window proc to trigger on key presses instead of key release
Gabriel Ivăncescu
gabrielopcode at gmail.com
Thu Sep 6 13:26:18 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>
---
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;
+ 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