https://git.reactos.org/?p=reactos.git;a=commitdiff;h=91e591b3d590df3c5164e…
commit 91e591b3d590df3c5164ed6f88ac8da389d42cba
Author:     Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
AuthorDate: Tue Mar 30 06:50:32 2021 +0900
Commit:     GitHub <noreply(a)github.com>
CommitDate: Tue Mar 30 06:50:32 2021 +0900
    [BROWSEUI] Enable AutoAppend of auto-completion (#3552)
    Fix and improve Auto-Append of auto-completion. CORE-9281
    - Implement CAutoComplete::DoAutoAppend method.
---
 dll/win32/browseui/CAutoComplete.cpp | 481 +++++++++++++++++------------------
 dll/win32/browseui/CAutoComplete.h   |  58 +----
 2 files changed, 239 insertions(+), 300 deletions(-)
diff --git a/dll/win32/browseui/CAutoComplete.cpp b/dll/win32/browseui/CAutoComplete.cpp
index b2ebc12ba68..d23b6d5cb78 100644
--- a/dll/win32/browseui/CAutoComplete.cpp
+++ b/dll/win32/browseui/CAutoComplete.cpp
@@ -40,6 +40,35 @@
 static HHOOK s_hMouseHook = NULL; // hook handle
 static HWND s_hWatchWnd = NULL; // the window handle to watch
+struct PREFIX_INFO
+{
+    LPCWSTR psz;
+    INT cch;
+};
+static const PREFIX_INFO s_prefixes[] =
+{
+    { L"https://", 8 },
+    { L"http://www.", 11 },
+    { L"http://", 7 },
+    { L"www.", 4 },
+};
+
+static inline BOOL DropPrefix(const CStringW& str, CStringW& strBody)
+{
+    for (size_t iPrefix = 0; iPrefix < _countof(s_prefixes); ++iPrefix)
+    {
+        LPCWSTR psz = s_prefixes[iPrefix].psz;
+        INT cch = s_prefixes[iPrefix].cch;
+        if (::StrCmpNIW(str, psz, cch) == 0)
+        {
+            strBody = str.Mid(cch);
+            return TRUE;
+        }
+    }
+    strBody = str;
+    return FALSE;
+}
+
 // mouse hook procedure to watch the mouse click
 //
https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/m…
 static LRESULT CALLBACK MouseProc(INT nCode, WPARAM wParam, LPARAM lParam)
@@ -80,33 +109,41 @@ static LRESULT CALLBACK MouseProc(INT nCode, WPARAM wParam, LPARAM
lParam)
 typedef CSimpleArray<CStringW> list_t;
-static inline INT pivot(list_t& a, INT i, INT j)
+static inline INT compare1(const CStringW& str1, const CStringW& str2)
+{
+    CStringW s1, s2;
+    DropPrefix(str1, s1);
+    DropPrefix(str2, s2);
+    return s1.CompareNoCase(s2);
+}
+
+static inline INT pivot(list_t& list, INT i, INT j)
 {
     INT k = i + 1;
-    while (k <= j && a[i].CompareNoCase(a[k]) == 0)
+    while (k <= j && compare1(list[i], list[k]) == 0)
         k++;
     if (k > j)
         return -1;
-    if (a[i].CompareNoCase(a[k]) >= 0)
+    if (compare1(list[i], list[k]) >= 0)
         return i;
     return k;
- }
+}
-static inline INT partition(list_t& a, INT i, INT j, const CStringW& x)
+static inline INT partition(list_t& list, INT i, INT j, const CStringW& x)
 {
     INT left = i, right = j;
     while (left <= right)
     {
-        while (left <= j && a[left].CompareNoCase(x) < 0)
+        while (left <= j && compare1(list[left], x) < 0)
             left++;
-        while (right >= i && a[right].CompareNoCase(x) >= 0)
+        while (right >= i && compare1(list[right], x) >= 0)
             right--;
         if (left > right)
             break;
-        CStringW tmp = a[left];
-        a[left] = a[right];
-        a[right] = tmp;
+        CStringW tmp = list[left];
+        list[left] = list[right];
+        list[right] = tmp;
         left++;
         right--;
@@ -114,16 +151,16 @@ static inline INT partition(list_t& a, INT i, INT j, const
CStringW& x)
     return left;
 }
-static void quicksort(list_t& a, INT i, INT j)
+static void quicksort(list_t& list, INT i, INT j)
 {
     if (i == j)
         return;
-    INT p = pivot(a, i, j);
+    INT p = pivot(list, i, j);
     if (p == -1)
         return;
-    INT k = partition(a, i, j, a[p]);
-    quicksort(a, i, k - 1);
-    quicksort(a, k, j);
+    INT k = partition(list, i, j, list[p]);
+    quicksort(list, i, k - 1);
+    quicksort(list, k, j);
 }
 static inline void DoSort(list_t& list)
@@ -142,7 +179,7 @@ static INT DoUnique(list_t& list)
     INT result = first;
     while (++first != last)
     {
-        if (list[result].CompareNoCase(list[first]) != 0)
+        if (compare1(list[result], list[first]) != 0)
             list[++result] = list[first];
     }
     return ++result;
@@ -158,7 +195,6 @@ static inline void DoUniqueAndTrim(list_t& list)
 }
 //////////////////////////////////////////////////////////////////////////////
-// CACEditCtrl
 // range of WCHAR (inclusive)
 struct RANGE
@@ -217,176 +253,103 @@ EditWordBreakProcW(LPWSTR lpch, INT index, INT count, INT code)
     {
         case WB_ISDELIMITER:
             return IsWordBreak(lpch[index]);
-
         case WB_LEFT:
+        {
             if (index)
                 --index;
             while (index && !IsWordBreak(lpch[index]))
                 --index;
             return index;
-
+        }
         case WB_RIGHT:
+        {
             if (!count)
                 break;
             while (index < count && lpch[index] &&
!IsWordBreak(lpch[index]))
                 ++index;
             return index;
-
-        default:
-            break;
+        }
     }
     return 0;
 }
-CACEditCtrl::CACEditCtrl() : m_pDropDown(NULL), m_fnOldWordBreakProc(NULL)
+static LRESULT CALLBACK
+EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
+                 UINT_PTR uSubclassID, DWORD_PTR dwData)
 {
+    CAutoComplete *pThis = reinterpret_cast<CAutoComplete *>(dwData);
+    return pThis->EditWndProc(hwnd, uMsg, wParam, lParam);
 }
-VOID CACEditCtrl::HookWordBreakProc(BOOL bHook)
+LRESULT CAutoComplete::EditWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
-    if (bHook)
+    LRESULT ret;
+    HWND hwndGotFocus;
+    switch (uMsg)
     {
-        m_fnOldWordBreakProc = reinterpret_cast<EDITWORDBREAKPROCW>(
-            SendMessageW(EM_SETWORDBREAKPROC, 0,
-                reinterpret_cast<LPARAM>(EditWordBreakProcW)));
-    }
-    else
-    {
-        SendMessageW(EM_SETWORDBREAKPROC, 0,
-                     reinterpret_cast<LPARAM>(m_fnOldWordBreakProc));
-    }
-}
-
-// WM_CHAR
-// This message is posted to the window with the keyboard focus when WM_KEYDOWN is
translated.
-LRESULT CACEditCtrl::OnChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
-{
-    TRACE("CACEditCtrl::OnChar(%p, %p)\n", this, wParam);
-    ATLASSERT(m_pDropDown);
-    return m_pDropDown->OnEditChar(wParam, lParam);
-}
-
-// WM_CUT / WM_PASTE / WM_CLEAR @implemented
-LRESULT CACEditCtrl::OnCutPasteClear(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL
&bHandled)
-{
-    TRACE("CACEditCtrl::OnCutPasteClear(%p)\n", this);
-    ATLASSERT(m_pDropDown);
-    LRESULT ret = DefWindowProcW(uMsg, wParam, lParam); // do default
-    m_pDropDown->OnEditUpdate(TRUE);
-    return ret;
-}
-
-// WM_DESTROY
-// This message is sent when a window is being destroyed.
-LRESULT CACEditCtrl::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL
&bHandled)
-{
-    TRACE("CACEditCtrl::OnDestroy(%p)\n", this);
-    ATLASSERT(m_pDropDown);
-    CAutoComplete *pDropDown = m_pDropDown;
-
-    // unhook word break procedure
-    HookWordBreakProc(FALSE);
-
-    // unsubclass because we don't watch any more
-    HWND hwndEdit = UnsubclassWindow();
-
-    // close the drop-down window
-    if (pDropDown)
-    {
-        pDropDown->PostMessageW(WM_CLOSE, 0, 0);
-    }
-
-    return ::DefWindowProcW(hwndEdit, uMsg, wParam, lParam); // do default
-}
-
-// WM_GETDLGCODE
-// By responding to this message, an application can take control of a particular type of
-// input and process the input itself.
-LRESULT CACEditCtrl::OnGetDlgCode(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL
&bHandled)
-{
-    TRACE("CACEditCtrl::OnGetDlgCode(%p)\n", this);
-    ATLASSERT(m_pDropDown);
-
-    LRESULT ret = DefWindowProcW(uMsg, wParam, lParam); // get default
-
-    if (m_pDropDown)
-    {
-        // some special keys need default processing. we handle them here
-        switch (wParam)
-        {
-            case VK_RETURN:
-                if (m_pDropDown->IsWindowVisible() || ::GetKeyState(VK_CONTROL) <
0)
-                    m_pDropDown->OnEditKeyDown(VK_RETURN, 0);
-                break;
-            case VK_TAB:
-                if (m_pDropDown->IsWindowVisible() &&
m_pDropDown->UseTab())
-                    ret |= DLGC_WANTALLKEYS; // we want all keys to manipulate the list
-                break;
-            case VK_ESCAPE:
-                if (m_pDropDown->IsWindowVisible())
+        case WM_CHAR:
+            return OnEditChar(wParam, lParam);
+        case WM_CUT: case WM_PASTE: case WM_CLEAR:
+            ret = ::DefSubclassProc(hwnd, uMsg, wParam, lParam); // do default
+            OnEditUpdate(TRUE);
+            return ret;
+        case WM_GETDLGCODE:
+            ret = ::DefSubclassProc(hwnd, uMsg, wParam, lParam); // do default
+            // some special keys need default processing. we handle them here
+            switch (wParam)
+            {
+                case VK_RETURN:
+                    if (IsWindowVisible() || ::GetKeyState(VK_CONTROL) < 0)
+                        OnEditKeyDown(VK_RETURN, 0);
+                    break;
+                case VK_TAB:
+                    if (IsWindowVisible() && UseTab())
+                        ret |= DLGC_WANTALLKEYS; // we want all keys to manipulate the
list
+                    break;
+                case VK_ESCAPE:
+                    if (IsWindowVisible())
+                        ret |= DLGC_WANTALLKEYS; // we want all keys to manipulate the
list
+                    break;
+                default:
+                {
                     ret |= DLGC_WANTALLKEYS; // we want all keys to manipulate the list
-                break;
-            default:
+                    break;
+                }
+            }
+            return ret;
+        case WM_KEYDOWN:
+            if (OnEditKeyDown(wParam, lParam))
+                return TRUE; // eat
+            break;
+        case WM_SETFOCUS:
+            break;
+        case WM_KILLFOCUS:
+            // hide the list if lost focus
+            hwndGotFocus = (HWND)wParam;
+            if (hwndGotFocus != m_hwndEdit && hwndGotFocus != m_hWnd)
+                HideDropDown();
+            break;
+        case WM_SETTEXT:
+            if (!m_bInSetText)
+                HideDropDown(); // it's mechanical WM_SETTEXT
+            break;
+        case WM_DESTROY:
+        {
+            if (m_fnOldWordBreakProc)
             {
-                ret |= DLGC_WANTALLKEYS; // we want all keys to manipulate the list
-                break;
+                ::SendMessageW(hwnd, EM_SETWORDBREAKPROC, 0,
reinterpret_cast<LPARAM>(m_fnOldWordBreakProc));
+                m_fnOldWordBreakProc = NULL;
             }
+            ::RemoveWindowSubclass(hwnd, EditSubclassProc, 0);
+            if (::IsWindow(m_hWnd))
+                PostMessageW(WM_CLOSE, 0, 0);
+            // remove reference to m_hwndEdit
+            Release();
+            break;
         }
     }
-    return ret;
-}
-
-// WM_KEYDOWN
-// This message is posted to the window with the keyboard focus when a non-system key is
pressed.
-LRESULT CACEditCtrl::OnKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL
&bHandled)
-{
-    TRACE("CACEditCtrl::OnKeyDown(%p, %p)\n", this, wParam);
-    ATLASSERT(m_pDropDown);
-    if (m_pDropDown->OnEditKeyDown(wParam, lParam))
-        return 1; // eat
-    bHandled = FALSE; // do default
-    return 0;
-}
-
-// WM_KILLFOCUS @implemented
-// This message is sent to a window immediately before it loses the keyboard focus.
-LRESULT CACEditCtrl::OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL
&bHandled)
-{
-    TRACE("CACEditCtrl::OnKillFocus(%p)\n", this);
-    ATLASSERT(m_pDropDown);
-
-    // hide the list if lost focus
-    HWND hwndGotFocus = (HWND)wParam;
-    if (hwndGotFocus != m_hWnd && hwndGotFocus != m_pDropDown->m_hWnd)
-    {
-        m_pDropDown->HideDropDown();
-    }
-
-    bHandled = FALSE; // do default
-    return 0;
-}
-
-// WM_SETFOCUS
-// This message is sent to a window after it has gained the keyboard focus.
-LRESULT CACEditCtrl::OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL
&bHandled)
-{
-    TRACE("CACEditCtrl::OnSetFocus(%p)\n", this);
-    ATLASSERT(m_pDropDown);
-    bHandled = FALSE; // do default
-    return 0;
-}
-
-// WM_SETTEXT
-// An application sends this message to set the text of a window.
-LRESULT CACEditCtrl::OnSetText(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL
&bHandled)
-{
-    TRACE("CACEditCtrl::OnSetText(%p)\n", this);
-    ATLASSERT(m_pDropDown);
-    if (!m_pDropDown->m_bInSetText)
-        m_pDropDown->HideDropDown(); // it's mechanical WM_SETTEXT
-    bHandled = FALSE; // do default
-    return 0;
+    return ::DefSubclassProc(hwnd, uMsg, wParam, lParam); // do default
 }
 //////////////////////////////////////////////////////////////////////////////
@@ -503,7 +466,7 @@ LRESULT CACListView::OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM
lParam, BOOL
     if (iItem != -1)
     {
         m_pDropDown->SelectItem(iItem); // select the item
-        CString strText = GetItemText(iItem); // get text of item
+        CStringW strText = GetItemText(iItem); // get text of item
         m_pDropDown->SetEditText(strText); // set text
         m_pDropDown->SetEditSel(0, strText.GetLength()); // select all
         m_pDropDown->HideDropDown(); // hide
@@ -545,10 +508,6 @@ LRESULT CACListView::OnNCHitTest(UINT uMsg, WPARAM wParam, LPARAM
lParam, BOOL &
 //////////////////////////////////////////////////////////////////////////////
 // CACScrollBar
-CACScrollBar::CACScrollBar() : m_pDropDown(NULL)
-{
-}
-
 HWND CACScrollBar::Create(HWND hwndParent)
 {
     ATLASSERT(m_hWnd == NULL);
@@ -563,10 +522,6 @@ HWND CACScrollBar::Create(HWND hwndParent)
 //////////////////////////////////////////////////////////////////////////////
 // CACSizeBox
-CACSizeBox::CACSizeBox() : m_pDropDown(NULL), m_bDowner(TRUE), m_bLongList(FALSE)
-{
-}
-
 HWND CACSizeBox::Create(HWND hwndParent)
 {
     ATLASSERT(m_hWnd == NULL);
@@ -684,6 +639,7 @@ CAutoComplete::CAutoComplete()
     : m_bInSetText(FALSE), m_bInSelectItem(FALSE)
     , m_bDowner(TRUE), m_dwOptions(ACO_AUTOAPPEND | ACO_AUTOSUGGEST)
     , m_bEnabled(TRUE), m_hwndCombo(NULL), m_hFont(NULL), m_bResized(FALSE)
+    , m_hwndEdit(NULL), m_fnOldEditProc(NULL), m_fnOldWordBreakProc(NULL)
 {
 }
@@ -693,7 +649,8 @@ HWND CAutoComplete::CreateDropDown()
     DWORD dwStyle = WS_POPUP | /*WS_VISIBLE |*/ WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
WS_BORDER;
     DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_NOPARENTNOTIFY;
     Create(NULL, NULL, NULL, dwStyle, dwExStyle);
-    TRACE("CAutoComplete::CreateDropDown(%p): m_hWnd == %p\n", this, m_hWnd);
+    TRACE("CAutoComplete::CreateDropDown(%p): m_hWnd=%p, m_hwndEdit=%p\n",
+          this, m_hWnd, m_hwndEdit);
     return m_hWnd;
 }
@@ -705,6 +662,9 @@ CAutoComplete::~CAutoComplete()
         ::DeleteObject(m_hFont);
         m_hFont = NULL;
     }
+    // quit holding them
+    m_pEnum.Release();
+    m_pACList.Release();
 }
 BOOL CAutoComplete::CanAutoSuggest()
@@ -729,6 +689,11 @@ BOOL CAutoComplete::IsComboBoxDropped()
     return (BOOL)::SendMessageW(m_hwndCombo, CB_GETDROPPEDSTATE, 0, 0);
 }
+BOOL CAutoComplete::FilterPrefixes()
+{
+    return !!(m_dwOptions & ACO_FILTERPREFIXES) && m_bEnabled;
+}
+
 INT CAutoComplete::GetItemCount()
 {
     return m_outerList.GetSize();
@@ -743,20 +708,16 @@ CStringW CAutoComplete::GetItemText(INT iItem)
 CStringW CAutoComplete::GetEditText()
 {
-    BSTR bstrText = NULL;
-    CStringW strText;
-    if (m_hwndEdit.GetWindowTextW(bstrText))
-    {
-        strText = bstrText;
-        ::SysFreeString(bstrText);
-    }
-    return strText;
+    WCHAR szText[L_MAX_URL_LENGTH];
+    if (::GetWindowTextW(m_hwndEdit, szText, _countof(szText)))
+        return szText;
+    return L"";
 }
 VOID CAutoComplete::SetEditText(LPCWSTR pszText)
 {
     m_bInSetText = TRUE; // don't hide drop-down
-    m_hwndEdit.SetWindowTextW(pszText);
+    ::CallWindowProcW(m_fnOldEditProc, m_hwndEdit, WM_SETTEXT, 0,
reinterpret_cast<LPARAM>(pszText));
     m_bInSetText = FALSE;
 }
@@ -771,7 +732,7 @@ CStringW CAutoComplete::GetStemText()
 VOID CAutoComplete::SetEditSel(INT ich0, INT ich1)
 {
-    m_hwndEdit.SendMessageW(EM_SETSEL, ich0, ich1);
+    ::CallWindowProcW(m_fnOldEditProc, m_hwndEdit, EM_SETSEL, ich0, ich1);
 }
 VOID CAutoComplete::ShowDropDown()
@@ -817,23 +778,52 @@ VOID CAutoComplete::DoAutoAppend()
     // get the common string
     CStringW strCommon;
+    BOOL bFound = FALSE;
     for (INT iItem = 0; iItem < cItems; ++iItem)
     {
-        const CString& strItem = m_innerList[iItem]; // get the text of the item
+        const CStringW& strItem = m_innerList[iItem]; // get the text of the item
-        if (iItem == 0) // the first item
+        CStringW strBody;
+        if (DropPrefix(strItem, strBody) &&
+            ::StrCmpNIW(strBody, strText, strText.GetLength()) == 0)
         {
-            strCommon = strItem; // store the text
+            if (!bFound)
+            {
+                bFound = TRUE;
+                strCommon = strBody;
+                continue;
+            }
+            for (INT ich = 0; ich < strBody.GetLength(); ++ich)
+            {
+                if (strCommon.GetLength() <= ich)
+                    break;
+                if (ChrCmpIW(strCommon[ich], strBody[ich]) != 0)
+                {
+                    strCommon = strCommon.Left(ich);
+                    break;
+                }
+            }
             continue;
         }
-        for (INT ich = 0; ich < strCommon.GetLength(); ++ich)
+        if (::StrCmpNIW(strItem, strText, strText.GetLength()) == 0)
         {
-            if (ich < strItem.GetLength() &&
-                ::ChrCmpIW(strCommon[ich], strItem[ich]) != 0)
+            if (!bFound)
             {
-                strCommon = strCommon.Left(ich); // shrink the common string
-                break;
+                bFound = TRUE;
+                strCommon = strItem;
+                continue;
+            }
+
+            for (INT ich = 0; ich < strItem.GetLength(); ++ich)
+            {
+                if (strCommon.GetLength() <= ich)
+                    break;
+                if (ChrCmpIW(strCommon[ich], strItem[ich]) != 0)
+                {
+                    strCommon = strCommon.Left(ich);
+                    break;
+                }
             }
         }
     }
@@ -842,13 +832,10 @@ VOID CAutoComplete::DoAutoAppend()
         return; // no suggestion
     // append suggestion
-    INT cchOld = strText.GetLength();
-    INT cchAppend = strCommon.GetLength() - cchOld;
-    strText += strCommon.Right(cchAppend);
-    SetEditText(strText);
+    SetEditText(strCommon);
     // select the appended suggestion
-    SetEditSel(cchOld, strText.GetLength());
+    SetEditSel(strText.GetLength(), strCommon.GetLength());
 }
 // go back a word ([Ctrl]+[Backspace])
@@ -856,8 +843,8 @@ VOID CAutoComplete::DoBackWord()
 {
     // get current selection
     INT ich0, ich1;
-    m_hwndEdit.SendMessageW(EM_GETSEL, reinterpret_cast<WPARAM>(&ich0),
-                                       reinterpret_cast<LPARAM>(&ich1));
+    ::CallWindowProcW(m_fnOldEditProc, m_hwndEdit, EM_GETSEL,
+                      reinterpret_cast<WPARAM>(&ich0),
reinterpret_cast<LPARAM>(&ich1));
     if (ich0 <= 0 || ich0 != ich1) // there is selection or left-side end
         return; // don't do anything
     // get text
@@ -870,7 +857,7 @@ VOID CAutoComplete::DoBackWord()
     // select range
     SetEditSel(ich0, ich1);
     // replace selection with empty text (this is actually deletion)
-    m_hwndEdit.SendMessageW(EM_REPLACESEL, TRUE, (LPARAM)L"");
+    ::CallWindowProcW(m_fnOldEditProc, m_hwndEdit, EM_REPLACESEL, TRUE,
(LPARAM)L"");
 }
 VOID CAutoComplete::UpdateScrollBar()
@@ -940,7 +927,7 @@ BOOL CAutoComplete::OnEditKeyDown(WPARAM wParam, LPARAM lParam)
                 }
             }
             // select all
-            INT cch = m_hwndEdit.GetWindowTextLengthW();
+            INT cch = ::CallWindowProcW(m_fnOldEditProc, m_hwndEdit, WM_GETTEXTLENGTH, 0,
0);
             SetEditSel(0, cch);
             // hide
             HideDropDown();
@@ -963,7 +950,7 @@ BOOL CAutoComplete::OnEditKeyDown(WPARAM wParam, LPARAM lParam)
             // is suggestion available?
             if (!CanAutoSuggest())
                 return FALSE; // do default
-            m_hwndEdit.DefWindowProcW(WM_KEYDOWN, VK_DELETE, 0); // do default
+            ::DefSubclassProc(m_hwndEdit, WM_KEYDOWN, VK_DELETE, 0); // do default
             OnEditUpdate(FALSE);
             return TRUE; // eat
         }
@@ -976,10 +963,6 @@ BOOL CAutoComplete::OnEditKeyDown(WPARAM wParam, LPARAM lParam)
             }
             break;
         }
-        default:
-        {
-            break;
-        }
     }
     return FALSE; // default
 }
@@ -989,7 +972,7 @@ LRESULT CAutoComplete::OnEditChar(WPARAM wParam, LPARAM lParam)
     TRACE("CACEditCtrl::OnEditChar(%p, %p)\n", this, wParam);
     if (wParam == L'\n' || wParam == L'\t')
         return 0; // eat
-    LRESULT ret = m_hwndEdit.DefWindowProcW(WM_CHAR, wParam, lParam); // do default
+    LRESULT ret = ::DefSubclassProc(m_hwndEdit, WM_CHAR, wParam, lParam); // do default
     if (CanAutoSuggest() || CanAutoAppend())
         OnEditUpdate(wParam != VK_BACK);
     return ret;
@@ -997,7 +980,7 @@ LRESULT CAutoComplete::OnEditChar(WPARAM wParam, LPARAM lParam)
 VOID CAutoComplete::OnEditUpdate(BOOL bAppendOK)
 {
-    CString strText = GetEditText();
+    CStringW strText = GetEditText();
     if (m_strText.CompareNoCase(strText) == 0)
     {
         // no change
@@ -1105,7 +1088,7 @@ BOOL CAutoComplete::OnListUpDown(UINT vk)
 // @implemented
 STDMETHODIMP CAutoComplete::Enable(BOOL fEnable)
 {
-    TRACE("(%p)->(%d)\n", this, fEnable);
+    TRACE("(%p)->Enable(%d)\n", this, fEnable);
     m_bEnabled = fEnable;
     return S_OK;
 }
@@ -1114,37 +1097,42 @@ STDMETHODIMP
 CAutoComplete::Init(HWND hwndEdit, IUnknown *punkACL,
                     LPCOLESTR pwszRegKeyPath, LPCOLESTR pwszQuickComplete)
 {
-    TRACE("(%p)->(0x%08lx, %p, %s, %s)\n",
+    TRACE("(%p)->Init(0x%08lx, %p, %s, %s)\n",
           this, hwndEdit, punkACL, debugstr_w(pwszRegKeyPath),
debugstr_w(pwszQuickComplete));
-
     // sanity check
     if (m_hwndEdit || !punkACL)
-    {
-        ATLASSERT(0);
         return E_FAIL;
-    }
-
-    // set this pointer to m_hwndEdit
-    m_hwndEdit.m_pDropDown = this;
-
+    if (!hwndEdit)
+        return E_INVALIDARG;
     // do subclass textbox to watch messages
-    m_hwndEdit.SubclassWindow(hwndEdit);
+    m_fnOldEditProc = reinterpret_cast<WNDPROC>(::GetWindowLongPtrW(hwndEdit,
GWLP_WNDPROC));
+    if (!m_fnOldEditProc)
+        return E_FAIL;
+    if (!::SetWindowSubclass(hwndEdit, EditSubclassProc, 0,
reinterpret_cast<DWORD_PTR>(this)))
+        return E_FAIL;
+    m_hwndEdit = hwndEdit;
+    // add reference to m_hwndEdit
+    AddRef();
     // set word break procedure
-    m_hwndEdit.HookWordBreakProc(TRUE);
+    m_fnOldWordBreakProc = reinterpret_cast<EDITWORDBREAKPROCW>(
+        ::SendMessageW(m_hwndEdit, EM_SETWORDBREAKPROC, 0,
+                       reinterpret_cast<LPARAM>(EditWordBreakProcW)));
     // save position
-    m_hwndEdit.GetWindowRect(&m_rcEdit);
+    ::GetWindowRect(m_hwndEdit, &m_rcEdit);
     // get an IEnumString
     ATLASSERT(!m_pEnum);
     punkACL->QueryInterface(IID_IEnumString, (VOID **)&m_pEnum);
     TRACE("m_pEnum: %p\n", static_cast<void *>(m_pEnum));
+    if (m_pEnum)
+        m_pEnum->AddRef(); // hold not to be freed
     // get an IACList
     ATLASSERT(!m_pACList);
     punkACL->QueryInterface(IID_IACList, (VOID **)&m_pACList);
     TRACE("m_pACList: %p\n", static_cast<void *>(m_pACList));
-
-    AddRef(); // add reference
+    if (m_pACList)
+        m_pACList->AddRef(); // hold not to be freed
     UpdateDropDownState(); // create/hide the drop-down window if necessary
@@ -1238,13 +1226,8 @@ STDMETHODIMP CAutoComplete::ResetEnumerator()
 {
     FIXME("(%p): stub\n", this);
-    HideDropDown();
-
-    if (IsWindowVisible())
-    {
-        OnEditUpdate(FALSE);
-    }
-
+    Reset();
+    m_innerList.RemoveAll();
     return S_OK;
 }
@@ -1305,11 +1288,7 @@ VOID CAutoComplete::UpdateDropDownState()
     {
         // create the drop-down window if not existed
         if (!m_hWnd)
-        {
-            AddRef();
-            if (!CreateDropDown())
-                Release();
-        }
+            CreateDropDown();
     }
     else
     {
@@ -1424,7 +1403,7 @@ VOID CAutoComplete::RepositionDropDown()
     // get m_hwndEdit position
     RECT rcEdit;
-    m_hwndEdit.GetWindowRect(&rcEdit);
+    ::GetWindowRect(m_hwndEdit, &rcEdit);
     INT x = rcEdit.left, y = rcEdit.bottom;
     // get list extent
@@ -1572,6 +1551,15 @@ INT CAutoComplete::UpdateOuterList()
     {
         // is the beginning matched?
         const CStringW& strTarget = m_innerList[iItem];
+        CStringW strBody;
+        if (DropPrefix(strTarget, strBody))
+        {
+            if (::StrCmpNIW(strBody, strText, strText.GetLength()) == 0)
+            {
+                m_outerList.Add(strTarget);
+                continue;
+            }
+        }
         if (::StrCmpNIW(strTarget, strText, strText.GetLength()) == 0)
         {
             m_outerList.Add(strTarget);
@@ -1614,13 +1602,11 @@ VOID CAutoComplete::UpdateCompletion(BOOL bAppendOK)
             RepositionDropDown();
         else
             HideDropDown();
-        return;
     }
     if (CanAutoAppend() && bAppendOK) // can we auto-append?
     {
         DoAutoAppend();
-        return;
     }
 }
@@ -1659,28 +1645,21 @@ LRESULT CAutoComplete::OnCreate(UINT uMsg, WPARAM wParam, LPARAM
lParam, BOOL &b
     m_hFont = reinterpret_cast<HFONT>(::GetStockObject(DEFAULT_GUI_FONT));
     m_hwndList.SetFont(m_hFont);
+    // add reference to CAutoComplete::m_hWnd
+    AddRef();
     return 0; // success
 }
-// WM_DESTROY
-// This message is sent when a window is being destroyed.
-LRESULT CAutoComplete::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL
&bHandled)
+// WM_NCDESTROY
+LRESULT CAutoComplete::OnNCDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL
&bHandled)
 {
-    TRACE("CAutoComplete::OnDestroy(%p)\n", this);
+    TRACE("CAutoComplete::OnNCDestroy(%p)\n", this);
     // hide
     if (IsWindowVisible())
         HideDropDown();
-    // unsubclass EDIT control
-    if (m_hwndEdit)
-    {
-        m_hwndEdit.HookWordBreakProc(FALSE);
-        m_hwndEdit.UnsubclassWindow();
-    }
-
     // clear CAutoComplete pointers
-    m_hwndEdit.m_pDropDown = NULL;
     m_hwndList.m_pDropDown = NULL;
     m_hwndScrollBar.m_pDropDown = NULL;
     m_hwndSizeBox.m_pDropDown = NULL;
@@ -1692,8 +1671,8 @@ LRESULT CAutoComplete::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM
lParam, BOOL &
     // clean up
     m_hwndCombo = NULL;
+    // remove reference to CAutoComplete::m_hWnd
     Release();
-
     return 0;
 }
@@ -1709,7 +1688,7 @@ LRESULT CAutoComplete::OnExitSizeMove(UINT uMsg, WPARAM wParam,
LPARAM lParam, B
     UINT uSWP_ = SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED |
SWP_NOACTIVATE;
     SetWindowPos(NULL, 0, 0, 0, 0, uSWP_);
-    m_hwndEdit.SetFocus(); // restore focus
+    ::SetFocus(m_hwndEdit); // restore focus
     return 0;
 }
@@ -1943,8 +1922,6 @@ LRESULT CAutoComplete::OnNCHitTest(UINT uMsg, WPARAM wParam, LPARAM
lParam, BOOL
 // This message is sent when the window size is changed.
 LRESULT CAutoComplete::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL
&bHandled)
 {
-    TRACE("CAutoComplete::OnSize(%p)\n", this);
-
     // calculate the positions of the controls
     CRect rcList, rcScrollBar, rcSizeBox;
     CalcRects(m_bDowner, rcList, rcScrollBar, rcSizeBox);
@@ -1974,6 +1951,8 @@ LRESULT CAutoComplete::OnShowWindow(UINT uMsg, WPARAM wParam, LPARAM
lParam, BOO
     BOOL bShow = (BOOL)wParam;
     if (bShow)
     {
+        if (s_hWatchWnd != m_hWnd && ::IsWindowVisible(s_hWatchWnd))
+            ::ShowWindowAsync(s_hWatchWnd, SW_HIDE);
         s_hWatchWnd = m_hWnd; // watch this
         // unhook mouse if any
@@ -2034,7 +2013,7 @@ LRESULT CAutoComplete::OnTimer(UINT uMsg, WPARAM wParam, LPARAM
lParam, BOOL &bH
     // m_hwndEdit is moved?
     RECT rcEdit;
-    m_hwndEdit.GetWindowRect(&rcEdit);
+    ::GetWindowRect(m_hwndEdit, &rcEdit);
     if (!::EqualRect(&rcEdit, &m_rcEdit))
     {
         // if so, hide
diff --git a/dll/win32/browseui/CAutoComplete.h b/dll/win32/browseui/CAutoComplete.h
index eb979baf314..f779fdaf4bf 100644
--- a/dll/win32/browseui/CAutoComplete.h
+++ b/dll/win32/browseui/CAutoComplete.h
@@ -24,53 +24,11 @@
 #include "atltypes.h"
 #include "rosctrls.h"
-class CACEditCtrl;
 class CACListView;
 class CACScrollBar;
 class CACSizeBox;
 class CAutoComplete;
-//////////////////////////////////////////////////////////////////////////////
-// CACEditCtrl --- auto-completion textbox
-
-class CACEditCtrl
-    : public CWindowImpl<CACEditCtrl, CWindow, CControlWinTraits>
-{
-public:
-    CAutoComplete* m_pDropDown;
-    static LPCWSTR GetWndClassName() { return WC_EDITW; }
-
-    CACEditCtrl();
-    VOID HookWordBreakProc(BOOL bHook);
-
-    // message map
-    BEGIN_MSG_MAP(CACEditCtrl)
-        MESSAGE_HANDLER(WM_CHAR, OnChar)
-        MESSAGE_HANDLER(WM_CLEAR, OnCutPasteClear)
-        MESSAGE_HANDLER(WM_CUT, OnCutPasteClear)
-        MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
-        MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode)
-        MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
-        MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
-        MESSAGE_HANDLER(WM_PASTE, OnCutPasteClear)
-        MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
-        MESSAGE_HANDLER(WM_SETTEXT, OnSetText)
-    END_MSG_MAP()
-
-protected:
-    // protected variables
-    EDITWORDBREAKPROCW m_fnOldWordBreakProc;
-    // message handlers
-    LRESULT OnChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
-    LRESULT OnCutPasteClear(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
-    LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
-    LRESULT OnGetDlgCode(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
-    LRESULT OnKeyDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
-    LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
-    LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
-    LRESULT OnSetText(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
-};
-
 //////////////////////////////////////////////////////////////////////////////
 // CACListView --- auto-completion list control
@@ -121,8 +79,7 @@ class CACScrollBar : public CWindowImpl<CACScrollBar>
 public:
     CAutoComplete* m_pDropDown;
     static LPCWSTR GetWndClassName() { return WC_SCROLLBARW; }
-
-    CACScrollBar();
+    CACScrollBar() : m_pDropDown(NULL) { }
     HWND Create(HWND hwndParent);
 protected:
@@ -139,8 +96,7 @@ class CACSizeBox : public CWindowImpl<CACSizeBox>
 public:
     CAutoComplete* m_pDropDown;
     static LPCWSTR GetWndClassName() { return WC_SCROLLBARW; }
-
-    CACSizeBox();
+    CACSizeBox() : m_pDropDown(NULL), m_bDowner(TRUE), m_bLongList(FALSE) { }
     HWND Create(HWND hwndParent);
     VOID SetStatus(BOOL bDowner, BOOL bLongList);
@@ -188,6 +144,7 @@ public:
     BOOL CanAutoAppend();
     BOOL UseTab();
     BOOL IsComboBoxDropped();
+    BOOL FilterPrefixes();
     INT GetItemCount();
     CStringW GetItemText(INT iItem);
@@ -203,6 +160,7 @@ public:
     VOID DoBackWord();
     VOID UpdateScrollBar();
+    LRESULT EditWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
     LRESULT OnEditChar(WPARAM wParam, LPARAM lParam);
     BOOL OnEditKeyDown(WPARAM wParam, LPARAM lParam);
     VOID OnEditUpdate(BOOL bAppendOK);
@@ -234,11 +192,13 @@ protected:
     HFONT m_hFont; // the font
     BOOL m_bResized; // re-sized by size-box?
     RECT m_rcEdit; // in screen coordinates, to watch the position
+    HWND m_hwndEdit; // the textbox
+    WNDPROC m_fnOldEditProc; // old textbox procedure
+    EDITWORDBREAKPROCW m_fnOldWordBreakProc;
     // The following variables are non-POD:
     CStringW m_strText; // internal text (used in selecting item and reverting text)
     CStringW m_strStemText; // dirname + '\\'
     CStringW m_strQuickComplete; // used for [Ctrl]+[Enter]
-    CACEditCtrl m_hwndEdit; // subclassed to watch
     CACListView m_hwndList; // this listview is virtual
     CACScrollBar m_hwndScrollBar; // scroll bar contol
     CACSizeBox m_hwndSizeBox; // the size grip
@@ -259,7 +219,7 @@ protected:
     // message map
     BEGIN_MSG_MAP(CAutoComplete)
         MESSAGE_HANDLER(WM_CREATE, OnCreate)
-        MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
+        MESSAGE_HANDLER(WM_NCDESTROY, OnNCDestroy)
         MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem)
         MESSAGE_HANDLER(WM_EXITSIZEMOVE, OnExitSizeMove)
         MESSAGE_HANDLER(WM_GETMINMAXINFO, OnGetMinMaxInfo)
@@ -276,7 +236,7 @@ protected:
     END_MSG_MAP()
     // message handlers
     LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
-    LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
+    LRESULT OnNCDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
     LRESULT OnDrawItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
     LRESULT OnExitSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
     LRESULT OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);