https://git.reactos.org/?p=reactos.git;a=commitdiff;h=c19d9df2593d6376f783dae78ba67fe53280fa4a

commit c19d9df2593d6376f783dae78ba67fe53280fa4a
Author:     Katayama Hirofumi MZ <[email protected]>
AuthorDate: Wed Mar 10 16:22:57 2021 +0900
Commit:     GitHub <[email protected]>
CommitDate: Wed Mar 10 16:22:57 2021 +0900

    [BROWSEUI] Implement auto-completion (#3507)
    
    Implement IAutoComplete to realize input auto completion. CORE-9281, 
CORE-1419
    
    - The main features of this PR are Auto-Suggest and Auto-Append.
    - Auto-Suggest shows a list near the textbox (an EDIT control) when the 
user has typed partial pathname into the textbox.
    - Auto-Append appends complement text into the textbox to complete the 
pathname with selected status.
    - The list of AutoSuggest is a top-level window whose window class is 
"Auto-Suggest Dropdown". We call it "the drop-down window".
    - The drop-down window contains three controls: a listview, a scrollbar and 
a sizebox.
    - The drop-down window watches the input into the textbox. If the textbox 
changed, then the window updates the list.
    - The sizebox control enables the user to resize the drop-down window.
---
 dll/win32/browseui/CAutoComplete.cpp | 2457 ++++++++++++++++++++++++++--------
 dll/win32/browseui/CAutoComplete.h   |  319 ++++-
 2 files changed, 2202 insertions(+), 574 deletions(-)

diff --git a/dll/win32/browseui/CAutoComplete.cpp 
b/dll/win32/browseui/CAutoComplete.cpp
index ffd14267d3e..d1ba30d4b86 100644
--- a/dll/win32/browseui/CAutoComplete.cpp
+++ b/dll/win32/browseui/CAutoComplete.cpp
@@ -20,669 +20,2076 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-/*
-  Implemented:
-  - ACO_AUTOAPPEND style
-  - ACO_AUTOSUGGEST style
-  - ACO_UPDOWNKEYDROPSLIST style
-
-  - Handle pwzsRegKeyPath and pwszQuickComplete in Init
+#include "precomp.h"
 
+/*
   TODO:
   - implement ACO_SEARCH style
   - implement ACO_FILTERPREFIXES style
   - implement ACO_USETAB style
   - implement ACO_RTLREADING style
-
  */
 
-#include "precomp.h"
+#define CX_LIST 30160 // width of m_hwndList (very wide but alright)
+#define CY_LIST 288 // maximum height of drop-down window
+#define CY_ITEM 18 // default height of listview item
+#define COMPLETION_TIMEOUT 250 // in milliseconds
+#define MAX_ITEM_COUNT 1000 // the maximum number of items
+#define WATCH_TIMER_ID 0xFEEDBEEF // timer ID to watch m_rcEdit
+#define WATCH_INTERVAL 300 // in milliseconds
 
-static const WCHAR autocomplete_propertyW[] = {'W','i','n','e',' 
','A','u','t','o',
-                                               
'c','o','m','p','l','e','t','e',' ',
-                                               'c','o','n','t','r','o','l',0};
+static HHOOK s_hMouseHook = NULL; // hook handle
+static HWND s_hWatchWnd = NULL; // the window handle to watch
 
-/**************************************************************************
- *  IAutoComplete_Constructor
- */
-CAutoComplete::CAutoComplete()
+// mouse hook procedure to watch the mouse click
+// 
https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644988(v=vs.85)
+static LRESULT CALLBACK MouseProc(INT nCode, WPARAM wParam, LPARAM lParam)
 {
-    m_enabled = TRUE;
-    m_initialized = FALSE;
-    m_options = ACO_AUTOAPPEND;
-    m_wpOrigEditProc = NULL;
-    m_hwndListBox = NULL;
-    m_txtbackup = NULL;
-    m_quickComplete = NULL;
-    m_hwndEdit = NULL;
-    m_wpOrigLBoxProc = NULL;
+    if (s_hMouseHook == NULL)
+        return 0; // do default
+    // if the user clicked the outside of s_hWatchWnd, then hide the drop-down 
window
+    if (nCode == HC_ACTION && // an action?
+        s_hWatchWnd && ::IsWindow(s_hWatchWnd) && // s_hWatchWnd is valid?
+        ::GetCapture() == NULL) // no capture? (dragging something?)
+    {
+        RECT rc;
+        MOUSEHOOKSTRUCT *pMouseHook = reinterpret_cast<MOUSEHOOKSTRUCT 
*>(lParam);
+        switch (wParam)
+        {
+            case WM_LBUTTONDOWN: case WM_LBUTTONUP:
+            case WM_RBUTTONDOWN: case WM_RBUTTONUP:
+            case WM_MBUTTONDOWN: case WM_MBUTTONUP:
+            case WM_NCLBUTTONDOWN: case WM_NCLBUTTONUP:
+            case WM_NCRBUTTONDOWN: case WM_NCRBUTTONUP:
+            case WM_NCMBUTTONDOWN: case WM_NCMBUTTONUP:
+            {
+                ::GetWindowRect(s_hWatchWnd, &rc);
+                if (!::PtInRect(&rc, pMouseHook->pt)) // outside of 
s_hWatchWnd?
+                {
+                    ::ShowWindowAsync(s_hWatchWnd, SW_HIDE); // hide it
+                }
+                break;
+            }
+        }
+    }
+    return ::CallNextHookEx(s_hMouseHook, nCode, wParam, lParam); // go next 
hook
 }
 
-/**************************************************************************
- *  IAutoComplete_Destructor
- */
-CAutoComplete::~CAutoComplete()
+//////////////////////////////////////////////////////////////////////////////
+// sorting algorithm
+// http://www.ics.kagoshima-u.ac.jp/~fuchida/edu/algorithm/sort-algorithm/
+
+typedef CSimpleArray<CStringW> list_t;
+
+static inline INT pivot(list_t& a, INT i, INT j)
+{
+    INT k = i + 1;
+    while (k <= j && a[i].CompareNoCase(a[k]) == 0)
+        k++;
+    if (k > j)
+        return -1;
+    if (a[i].CompareNoCase(a[k]) >= 0)
+        return i;
+    return k;
+ }
+
+static inline INT partition(list_t& a, INT i, INT j, const CStringW& x)
 {
-    TRACE(" destroying IAutoComplete(%p)\n", this);
-    HeapFree(GetProcessHeap(), 0, m_quickComplete);
-    HeapFree(GetProcessHeap(), 0, m_txtbackup);
-    if (m_wpOrigEditProc)
+    INT left = i, right = j;
+    while (left <= right)
     {
-        SetWindowLongPtrW(m_hwndEdit, GWLP_WNDPROC, 
(LONG_PTR)m_wpOrigEditProc);
-        RemovePropW(m_hwndEdit, autocomplete_propertyW);
+        while (left <= j && a[left].CompareNoCase(x) < 0)
+            left++;
+        while (right >= i && a[right].CompareNoCase(x) >= 0)
+            right--;
+        if (left > right)
+            break;
+
+        CStringW tmp = a[left];
+        a[left] = a[right];
+        a[right] = tmp;
+
+        left++;
+        right--;
     }
-    if (m_hwndListBox)
-        DestroyWindow(m_hwndListBox);
+    return left;
 }
 
-/******************************************************************************
- * IAutoComplete_fnEnable
- */
-HRESULT WINAPI CAutoComplete::Enable(BOOL fEnable)
+static void quicksort(list_t& a, INT i, INT j)
 {
-    HRESULT hr = S_OK;
-
-    TRACE("(%p)->(%s)\n", this, (fEnable) ? "true" : "false");
-
-    m_enabled = fEnable;
-
-    return hr;
+    if (i == j)
+        return;
+    INT p = pivot(a, i, j);
+    if (p == -1)
+        return;
+    INT k = partition(a, i, j, a[p]);
+    quicksort(a, i, k - 1);
+    quicksort(a, k, j);
 }
 
-/******************************************************************************
- * create_listbox
- */
-void CAutoComplete::CreateListbox()
+static inline void DoSort(list_t& list)
 {
-    HWND hwndParent = GetParent(m_hwndEdit);
+    if (list.GetSize() <= 1) // sanity check
+        return;
+    quicksort(list, 0, list.GetSize() - 1); // quick sort
+}
 
-    /* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME 
looks ugly */
-    m_hwndListBox = CreateWindowExW(0, WC_LISTBOXW, NULL,
-                                    WS_BORDER | WS_CHILD | WS_VSCROLL | 
LBS_HASSTRINGS | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT,
-                                    CW_USEDEFAULT, CW_USEDEFAULT, 
CW_USEDEFAULT, CW_USEDEFAULT,
-                                    hwndParent, NULL,
-                                    (HINSTANCE)GetWindowLongPtrW(hwndParent, 
GWLP_HINSTANCE), NULL);
+// std::unique
+static INT DoUnique(list_t& list)
+{
+    INT first = 0, last = list.GetSize();
+    if (first == last)
+        return last;
+    INT result = first;
+    while (++first != last)
+    {
+        if (list[result].CompareNoCase(list[first]) != 0)
+            list[++result] = list[first];
+    }
+    return ++result;
+}
 
-    if (m_hwndListBox)
+static inline void DoUniqueAndTrim(list_t& list)
+{
+    INT last = DoUnique(list);
+    while (list.GetSize() > last)
     {
-        m_wpOrigLBoxProc = (WNDPROC)SetWindowLongPtrW(m_hwndListBox, 
GWLP_WNDPROC, (LONG_PTR)ACLBoxSubclassProc);
-        SetWindowLongPtrW(m_hwndListBox, GWLP_USERDATA, (LONG_PTR)this);
+        list.RemoveAt(last);
     }
 }
 
+//////////////////////////////////////////////////////////////////////////////
+// CACEditCtrl
 
-/******************************************************************************
- * IAutoComplete_fnInit
- */
-HRESULT WINAPI CAutoComplete::Init(HWND hwndEdit, IUnknown *punkACL, LPCOLESTR 
pwzsRegKeyPath, LPCOLESTR pwszQuickComplete)
+// range of WCHAR (inclusive)
+struct RANGE
 {
-    TRACE("(%p)->(0x%08lx, %p, %s, %s)\n",
-      this, hwndEdit, punkACL, debugstr_w(pwzsRegKeyPath), 
debugstr_w(pwszQuickComplete));
-
-    if (m_options & ACO_AUTOSUGGEST)
-        TRACE(" ACO_AUTOSUGGEST\n");
-    if (m_options & ACO_AUTOAPPEND)
-        TRACE(" ACO_AUTOAPPEND\n");
-    if (m_options & ACO_SEARCH)
-        FIXME(" ACO_SEARCH not supported\n");
-    if (m_options & ACO_FILTERPREFIXES)
-        FIXME(" ACO_FILTERPREFIXES not supported\n");
-    if (m_options & ACO_USETAB)
-        FIXME(" ACO_USETAB not supported\n");
-    if (m_options & ACO_UPDOWNKEYDROPSLIST)
-        TRACE(" ACO_UPDOWNKEYDROPSLIST\n");
-    if (m_options & ACO_RTLREADING)
-        FIXME(" ACO_RTLREADING not supported\n");
+    WCHAR from, to;
+};
 
-    if (!hwndEdit || !punkACL)
-        return E_INVALIDARG;
+// a callback function for bsearch: comparison of two ranges
+static inline int RangeCompare(const void *x, const void *y)
+{
+    const RANGE *a = reinterpret_cast<const RANGE *>(x);
+    const RANGE *b = reinterpret_cast<const RANGE *>(y);
+    if (a->to < b->from)
+        return -1;
+    if (b->to < a->from)
+        return 1;
+    return 0;
+}
 
-    if (m_initialized)
+// is the WCHAR a word break?
+static inline BOOL IsWordBreak(WCHAR ch)
+{
+    // the ranges of word break characters
+    static const RANGE s_ranges[] =
     {
-        WARN("Autocompletion object is already initialized\n");
-        /* This->hwndEdit is set to NULL when the edit window is destroyed. */
-        return m_hwndEdit ? E_FAIL : E_UNEXPECTED;
-    }
+        { 0x0009, 0x0009 }, { 0x0020, 0x002f }, { 0x003a, 0x0040 }, { 0x005b, 
0x0060 },
+        { 0x007b, 0x007e }, { 0x00ab, 0x00ab }, { 0x00ad, 0x00ad }, { 0x00bb, 
0x00bb },
+        { 0x02c7, 0x02c7 }, { 0x02c9, 0x02c9 }, { 0x055d, 0x055d }, { 0x060c, 
0x060c },
+        { 0x2002, 0x200b }, { 0x2013, 0x2014 }, { 0x2016, 0x2016 }, { 0x2018, 
0x2018 },
+        { 0x201c, 0x201d }, { 0x2022, 0x2022 }, { 0x2025, 0x2027 }, { 0x2039, 
0x203a },
+        { 0x2045, 0x2046 }, { 0x207d, 0x207e }, { 0x208d, 0x208e }, { 0x226a, 
0x226b },
+        { 0x2574, 0x2574 }, { 0x3001, 0x3003 }, { 0x3005, 0x3005 }, { 0x3008, 
0x3011 },
+        { 0x3014, 0x301b }, { 0x301d, 0x301e }, { 0x3041, 0x3041 }, { 0x3043, 
0x3043 },
+        { 0x3045, 0x3045 }, { 0x3047, 0x3047 }, { 0x3049, 0x3049 }, { 0x3063, 
0x3063 },
+        { 0x3083, 0x3083 }, { 0x3085, 0x3085 }, { 0x3087, 0x3087 }, { 0x308e, 
0x308e },
+        { 0x309b, 0x309e }, { 0x30a1, 0x30a1 }, { 0x30a3, 0x30a3 }, { 0x30a5, 
0x30a5 },
+        { 0x30a7, 0x30a7 }, { 0x30a9, 0x30a9 }, { 0x30c3, 0x30c3 }, { 0x30e3, 
0x30e3 },
+        { 0x30e5, 0x30e5 }, { 0x30e7, 0x30e7 }, { 0x30ee, 0x30ee }, { 0x30f5, 
0x30f6 },
+        { 0x30fc, 0x30fe }, { 0xfd3e, 0xfd3f }, { 0xfe30, 0xfe31 }, { 0xfe33, 
0xfe44 },
+        { 0xfe4f, 0xfe51 }, { 0xfe59, 0xfe5e }, { 0xff08, 0xff09 }, { 0xff0c, 
0xff0c },
+        { 0xff0e, 0xff0e }, { 0xff1c, 0xff1c }, { 0xff1e, 0xff1e }, { 0xff3b, 
0xff3b },
+        { 0xff3d, 0xff3d }, { 0xff40, 0xff40 }, { 0xff5b, 0xff5e }, { 0xff61, 
0xff64 },
+        { 0xff67, 0xff70 }, { 0xff9e, 0xff9f }, { 0xffe9, 0xffe9 }, { 0xffeb, 
0xffeb },
+    };
+    // binary search
+    RANGE range = { ch, ch };
+    return !!bsearch(&range, s_ranges, _countof(s_ranges), sizeof(RANGE), 
RangeCompare);
+}
 
-    if (!SUCCEEDED(punkACL->QueryInterface(IID_PPV_ARG(IEnumString, 
&m_enumstr))))
+// This function is an application-defined callback function.
+// 
https://docs.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-editwordbreakprocw
+static INT CALLBACK
+EditWordBreakProcW(LPWSTR lpch, INT index, INT count, INT code)
+{
+    switch (code)
     {
-        TRACE("No IEnumString interface\n");
-        return  E_NOINTERFACE;
-    }
-
-    m_hwndEdit = hwndEdit;
-    m_initialized = TRUE;
+        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;
 
-    /* Keep at least one reference to the object until the edit window is 
destroyed. */
-    AddRef();
+        default:
+            break;
+    }
+    return 0;
+}
 
-    /* If another AutoComplete object was previously assigned to this edit 
control,
-       release it but keep the same callback on the control, to avoid an 
infinite
-       recursive loop in ACEditSubclassProc while the property is set to this 
object */
-    CAutoComplete *prev = static_cast<CAutoComplete *>(GetPropW(m_hwndEdit, 
autocomplete_propertyW));
+CACEditCtrl::CACEditCtrl() : m_pDropDown(NULL), m_fnOldWordBreakProc(NULL)
+{
+}
 
-    if (prev && prev->m_initialized)
+VOID CACEditCtrl::HookWordBreakProc(BOOL bHook)
+{
+    if (bHook)
     {
-        m_wpOrigEditProc = prev->m_wpOrigEditProc;
-        SetPropW(m_hwndEdit, autocomplete_propertyW, this);
-        prev->m_wpOrigEditProc = NULL;
-        prev->Release();
+        m_fnOldWordBreakProc = reinterpret_cast<EDITWORDBREAKPROCW>(
+            SendMessageW(EM_SETWORDBREAKPROC, 0,
+                reinterpret_cast<LPARAM>(EditWordBreakProcW)));
     }
     else
     {
-        SetPropW(m_hwndEdit, autocomplete_propertyW, (HANDLE)this);
-        m_wpOrigEditProc = (WNDPROC)SetWindowLongPtrW(m_hwndEdit, 
GWLP_WNDPROC, (LONG_PTR)ACEditSubclassProc);
-    }
-
-    if (m_options & ACO_AUTOSUGGEST)
-    {
-        CreateListbox();
+        SendMessageW(EM_SETWORDBREAKPROC, 0,
+                     reinterpret_cast<LPARAM>(m_fnOldWordBreakProc));
     }
+}
 
-    if (pwzsRegKeyPath)
-    {
-        WCHAR *key;
-        WCHAR result[MAX_PATH];
-        WCHAR *value;
-        HKEY hKey = 0;
-        LONG res;
-        LONG len;
+// 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);
+}
 
-        /* pwszRegKeyPath contains the key as well as the value, so we split */
-        key = (WCHAR *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 
(wcslen(pwzsRegKeyPath) + 1) * sizeof(WCHAR));
+// 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;
+}
 
-        if (key)
-        {
-            wcscpy(key, pwzsRegKeyPath);
-            value = const_cast<WCHAR *>(wcsrchr(key, '\\'));
+// 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;
 
-            if (value)
-            {
-                *value = 0;
-                value++;
-                /* Now value contains the value and buffer the key */
-                res = RegOpenKeyExW(HKEY_CURRENT_USER, key, 0, KEY_READ, 
&hKey);
-        
-                if (res != ERROR_SUCCESS)
-                {
-                    /* if the key is not found, MSDN states we must seek in 
HKEY_LOCAL_MACHINE */
-                    res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, 
&hKey);
-                }
-        
-                if (res == ERROR_SUCCESS)
-                {
-                    len = sizeof(result);
-                    res = RegQueryValueW(hKey, value, result, &len);
-                    if (res == ERROR_SUCCESS)
-                    {
-                        m_quickComplete = (WCHAR *)HeapAlloc(GetProcessHeap(), 
HEAP_ZERO_MEMORY, len * sizeof(WCHAR));
-                        wcscpy(m_quickComplete, result);
-                    }
-                    RegCloseKey(hKey);
-                }
-            }
+    // unhook word break procedure
+    HookWordBreakProc(FALSE);
 
-            HeapFree(GetProcessHeap(), 0, key);
-        }
-        else
-        {
-            TRACE("HeapAlloc Failed when trying to alloca %d bytes\n", 
(wcslen(pwzsRegKeyPath) + 1) * sizeof(WCHAR));
-            return S_FALSE;
-        }
-    }
+    // unsubclass because we don't watch any more
+    HWND hwndEdit = UnsubclassWindow();
 
-    if ((pwszQuickComplete) && (!m_quickComplete))
+    // close the drop-down window
+    if (pDropDown)
     {
-        m_quickComplete = (WCHAR *)HeapAlloc(GetProcessHeap(), 
HEAP_ZERO_MEMORY, (wcslen(pwszQuickComplete) + 1) * sizeof(WCHAR));
-
-        if (m_quickComplete)
-        {
-            wcscpy(m_quickComplete, pwszQuickComplete);
-        }
-        else
-        {
-            TRACE("HeapAlloc Failed when trying to alloca %d bytes\n", 
(wcslen(pwszQuickComplete) + 1) * sizeof(WCHAR));
-            return S_FALSE;
-        }
+        pDropDown->PostMessageW(WM_CLOSE, 0, 0);
     }
 
-    return S_OK;
+    return ::DefWindowProcW(hwndEdit, uMsg, wParam, lParam); // do default
 }
 
-/**************************************************************************
- *  IAutoComplete_fnGetOptions
- */
-HRESULT WINAPI CAutoComplete::GetOptions(DWORD *pdwFlag)
+// 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)
 {
-    HRESULT hr = S_OK;
+    TRACE("CACEditCtrl::OnGetDlgCode(%p)\n", this);
+    ATLASSERT(m_pDropDown);
 
-    TRACE("(%p) -> (%p)\n", this, pdwFlag);
+    LRESULT ret = DefWindowProcW(uMsg, wParam, lParam); // get default
 
-    *pdwFlag = m_options;
+    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())
+                    m_pDropDown->OnEditKeyDown(VK_TAB, 0);
+                break;
+            case VK_ESCAPE:
+                if (m_pDropDown->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;
+            }
+        }
+    }
 
-    return hr;
+    return ret;
 }
 
-/**************************************************************************
- *  IAutoComplete_fnSetOptions
- */
-HRESULT WINAPI CAutoComplete::SetOptions(DWORD dwFlag)
+// 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)
 {
-    HRESULT hr = S_OK;
+    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;
+}
 
-    TRACE("(%p) -> (0x%x)\n", this, dwFlag);
+// 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);
 
-    m_options = (AUTOCOMPLETEOPTIONS)dwFlag;
+    // hide the list if lost focus
+    HWND hwndGotFocus = (HWND)wParam;
+    if (hwndGotFocus != m_hWnd && hwndGotFocus != m_pDropDown->m_hWnd)
+    {
+        m_pDropDown->HideDropDown();
+    }
 
-    if ((m_options & ACO_AUTOSUGGEST) && m_hwndEdit && !m_hwndListBox)
-        CreateListbox();
+    bHandled = FALSE; // do default
+    return 0;
+}
 
-    return hr;
+// 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;
 }
 
-/* Edit_BackWord --- Delete previous word in text box */
-static void Edit_BackWord(HWND hwndEdit)
+// 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)
 {
-    INT iStart, iEnd;
-    iStart = iEnd = 0;
-    SendMessageW(hwndEdit, EM_GETSEL, (WPARAM)&iStart, (LPARAM)&iEnd);
+    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;
+}
 
-    if (iStart != iEnd || iStart < 0)
-        return;
+//////////////////////////////////////////////////////////////////////////////
+// CACListView
 
-    size_t cchText = GetWindowTextLengthW(hwndEdit);
-    if (cchText < (size_t)iStart || (INT)cchText <= 0)
-        return;
+CACListView::CACListView() : m_pDropDown(NULL), m_cyItem(CY_ITEM)
+{
+}
 
-    CComHeapPtr<WCHAR> pszText;
-    if (!pszText.Allocate(cchText + 1))
-        return;
+HWND CACListView::Create(HWND hwndParent)
+{
+    ATLASSERT(m_hWnd == NULL);
+    DWORD dwStyle = WS_CHILD | /*WS_VISIBLE |*/ WS_CLIPSIBLINGS | 
LVS_NOCOLUMNHEADER |
+                    LVS_OWNERDATA | LVS_OWNERDRAWFIXED | LVS_SINGLESEL | 
LVS_REPORT;
+    HWND hWnd = ::CreateWindowExW(0, GetWndClassName(), L"Internet Explorer", 
dwStyle,
+                                  0, 0, 0, 0, hwndParent, NULL,
+                                  _AtlBaseModule.GetModuleInstance(), NULL);
+    SubclassWindow(hWnd); // do subclass to handle messages
+    // set extended listview style
+    DWORD exstyle = LVS_EX_ONECLICKACTIVATE | LVS_EX_FULLROWSELECT | 
LVS_EX_TRACKSELECT;
+    SetExtendedListViewStyle(exstyle, exstyle);
+    // insert one column (needed to insert items)
+    LV_COLUMNW column = { LVCF_FMT | LVCF_WIDTH };
+    column.fmt = LVCFMT_LEFT;
+    column.cx = CX_LIST - ::GetSystemMetrics(SM_CXVSCROLL);
+    InsertColumn(0, &column);
+    return m_hWnd;
+}
 
-    if (GetWindowTextW(hwndEdit, pszText, cchText + 1) <= 0)
-        return;
+// set font handle
+VOID CACListView::SetFont(HFONT hFont)
+{
+    SendMessageW(WM_SETFONT, (WPARAM)hFont, TRUE); // set font
 
-    WORD types[2];
-    for (--iStart; 0 < iStart; --iStart)
+    // get listview item height
+    m_cyItem = CY_ITEM;
+    HDC hDC = GetDC();
+    if (hDC)
     {
-        GetStringTypeW(CT_CTYPE1, &pszText[iStart - 1], 2, types);
-        if (((types[0] & C1_PUNCT) && !(types[1] & C1_SPACE)) ||
-            ((types[0] & C1_SPACE) && (types[1] & (C1_ALPHA | C1_DIGIT))))
+        HGDIOBJ hFontOld = ::SelectObject(hDC, hFont);
+        TEXTMETRICW tm;
+        if (::GetTextMetricsW(hDC, &tm))
         {
-            SendMessageW(hwndEdit, EM_SETSEL, iStart, iEnd);
-            SendMessageW(hwndEdit, EM_REPLACESEL, TRUE, (LPARAM)L"");
-            return;
+            m_cyItem = (tm.tmHeight * 3) / 2; // 3/2 of text height
         }
+        ::SelectObject(hDC, hFontOld);
+        ReleaseDC(hDC);
     }
+}
 
-    if (iStart == 0)
-    {
-        SendMessageW(hwndEdit, EM_SETSEL, iStart, iEnd);
-        SendMessageW(hwndEdit, EM_REPLACESEL, TRUE, (LPARAM)L"");
-    }
+// get the number of visible items
+INT CACListView::GetVisibleCount()
+{
+    if (m_cyItem <= 0) // avoid "division by zero"
+        return 0;
+    CRect rc;
+    GetClientRect(&rc);
+    return rc.Height() / m_cyItem;
 }
 
-/*
-  Window procedure for autocompletion
- */
-LRESULT APIENTRY CAutoComplete::ACEditSubclassProc(HWND hwnd, UINT uMsg, 
WPARAM wParam, LPARAM lParam)
+// get the text of an item
+CStringW CACListView::GetItemText(INT iItem)
 {
-    CAutoComplete *pThis = static_cast<CAutoComplete *>(GetPropW(hwnd, 
autocomplete_propertyW));
-    HRESULT hr;
-    WCHAR hwndText[255];
-    WCHAR *hwndQCText;
-    RECT r;
-    BOOL control, filled, displayall = FALSE;
-    int cpt, height, sel;
-    ULONG fetched;
+    // NOTE: LVS_OWNERDATA doesn't support LVM_GETITEMTEXT.
+    ATLASSERT(m_pDropDown);
+    ATLASSERT(GetStyle() & LVS_OWNERDATA);
+    return m_pDropDown->GetItemText(iItem);
+}
 
-    if (!pThis->m_enabled)
-    {
-        return CallWindowProcW(pThis->m_wpOrigEditProc, hwnd, uMsg, wParam, 
lParam);
-    }
+// get the item index from position
+INT CACListView::ItemFromPoint(INT x, INT y)
+{
+    LV_HITTESTINFO hittest;
+    hittest.pt.x = x;
+    hittest.pt.y = y;
+    return HitTest(&hittest);
+}
 
-    switch (uMsg)
-    {
-        case CB_SHOWDROPDOWN:
-        {
-            ShowWindow(pThis->m_hwndListBox, SW_HIDE);
-        }; break;
+// get current selection
+INT CACListView::GetCurSel()
+{
+    return GetNextItem(-1, LVNI_ALL | LVNI_SELECTED);
+}
 
-        case WM_KILLFOCUS:
-        {
-            if ((pThis->m_options & ACO_AUTOSUGGEST) && ((HWND)wParam != 
pThis->m_hwndListBox))
-            {
-                ShowWindow(pThis->m_hwndListBox, SW_HIDE);
-            }
-            return CallWindowProcW(pThis->m_wpOrigEditProc, hwnd, uMsg, 
wParam, lParam);
-        }; break;
+// set current selection
+VOID CACListView::SetCurSel(INT iItem)
+{
+    if (iItem == -1)
+        SetItemState(-1, 0, LVIS_SELECTED); // select none
+    else
+        SetItemState(iItem, LVIS_SELECTED, LVIS_SELECTED);
+}
 
-        case WM_KEYUP:
-        {
-            GetWindowTextW(hwnd, (LPWSTR)hwndText, 255);
+// select the specific position (in client coordinates)
+VOID CACListView::SelectHere(INT x, INT y)
+{
+    SetCurSel(ItemFromPoint(x, y));
+}
 
-            switch(wParam)
-            {
-                case VK_RETURN:
-                {
-                    /* If quickComplete is set and control is pressed, replace 
the string */
-                    control = GetKeyState(VK_CONTROL) & 0x8000;
-                    if (control && pThis->m_quickComplete)
-                    {
-                        hwndQCText = (WCHAR *)HeapAlloc(GetProcessHeap(), 
HEAP_ZERO_MEMORY,
-                                           
(wcslen(pThis->m_quickComplete)+wcslen(hwndText))*sizeof(WCHAR));
-                        sel = swprintf(hwndQCText, pThis->m_quickComplete, 
hwndText);
-                        SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)hwndQCText);
-                        SendMessageW(hwnd, EM_SETSEL, 0, sel);
-                        HeapFree(GetProcessHeap(), 0, hwndQCText);
-                    }
-
-                    ShowWindow(pThis->m_hwndListBox, SW_HIDE);
-                    return 0;
-                }; break;
-
-                case VK_LEFT:
-                case VK_RIGHT:
-                {
-                    return 0;
-                }; break;
+// WM_LBUTTONUP / WM_MBUTTONUP / WM_RBUTTONUP @implemented
+LRESULT CACListView::OnButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled)
+{
+    TRACE("CACListView::OnButtonUp(%p)\n", this);
+    return 0; // eat
+}
 
-                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.
-                       - if the listbox is visible, change the selection
-                    */
-                    if ( (pThis->m_options & (ACO_AUTOSUGGEST | 
ACO_UPDOWNKEYDROPSLIST))
-                     && (!IsWindowVisible(pThis->m_hwndListBox) && (! 
*hwndText)) )
-                    {
-                        /* We must display all the entries */
-                        displayall = TRUE;
-                    }
-                    else
-                    {
-                        if (IsWindowVisible(pThis->m_hwndListBox))
-                        {
-                            int count;
-
-                            count = SendMessageW(pThis->m_hwndListBox, 
LB_GETCOUNT, 0, 0);
-                            /* Change the selection */
-                            sel = SendMessageW(pThis->m_hwndListBox, 
LB_GETCURSEL, 0, 0);
-                            if (wParam == VK_UP)
-                                sel = ((sel-1) < 0) ? count-1 : sel-1;
-                            else
-                                sel = ((sel+1) >= count) ? -1 : sel+1;
-                            
-                            SendMessageW(pThis->m_hwndListBox, LB_SETCURSEL, 
sel, 0);
-                            
-                            if (sel != -1)
-                            {
-                                WCHAR *msg;
-                                int len;
-
-                                len = SendMessageW(pThis->m_hwndListBox, 
LB_GETTEXTLEN, sel, (LPARAM)NULL);
-                                msg = (WCHAR *)HeapAlloc(GetProcessHeap(), 
HEAP_ZERO_MEMORY, (len + 1) * sizeof(WCHAR));
-                                
-                                if (msg)
-                                {
-                                    SendMessageW(pThis->m_hwndListBox, 
LB_GETTEXT, sel, (LPARAM)msg);
-                                    SendMessageW(hwnd, WM_SETTEXT, 0, 
(LPARAM)msg);
-                                    SendMessageW(hwnd, EM_SETSEL, wcslen(msg), 
wcslen(msg));
-                                
-                                    HeapFree(GetProcessHeap(), 0, msg);
-                                }
-                                else
-                                {
-                                    TRACE("HeapAlloc failed to allocate %d 
bytes\n", (len + 1) * sizeof(WCHAR));
-                                }
-                            }
-                            else
-                            {
-                                SendMessageW(hwnd, WM_SETTEXT, 0, 
(LPARAM)pThis->m_txtbackup);
-                                SendMessageW(hwnd, EM_SETSEL, 
wcslen(pThis->m_txtbackup), wcslen(pThis->m_txtbackup));
-                            }
-                        }
-                        return 0;
-                    }
-                }; break;
-                
-                case VK_BACK:
-                {
-                    if (GetKeyState(VK_CONTROL) < 0) // Ctrl+Backspace
-                    {
-                        Edit_BackWord(hwnd);
-                        return 0;
-                    }
-                }
-                // FALL THROUGH
-                case VK_DELETE:
-                {
-                    if ((! *hwndText) && (pThis->m_options & ACO_AUTOSUGGEST))
-                    {
-                        ShowWindow(pThis->m_hwndListBox, SW_HIDE);
-                        return CallWindowProcW(pThis->m_wpOrigEditProc, hwnd, 
uMsg, wParam, lParam);
-                    }
-                    
-                    if (pThis->m_options & ACO_AUTOAPPEND)
-                    {
-                        DWORD b;
-                        SendMessageW(hwnd, EM_GETSEL, (WPARAM)&b, 
(LPARAM)NULL);
-                        if (b>1)
-                        {
-                            hwndText[b-1] = '\0';
-                        }
-                        else
-                        {
-                            hwndText[0] = '\0';
-                            SetWindowTextW(hwnd, hwndText);
-                        }
-                    }
-                }; break;
-                
-                default:
-                    ;
-            }
+// WM_LBUTTONDOWN @implemented
+// This message is posted when the user pressed the left mouse button while 
the cursor is inside.
+LRESULT CACListView::OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, 
BOOL &bHandled)
+{
+    TRACE("CACListView::OnLButtonDown(%p)\n", this);
+    ATLASSERT(m_pDropDown);
+    INT iItem = ItemFromPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+    if (iItem != -1)
+    {
+        m_pDropDown->SelectItem(iItem); // select the item
+        CString 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
+    }
+    return 0;
+}
 
-            SendMessageW(pThis->m_hwndListBox, LB_RESETCONTENT, 0, 0);
+// WM_MBUTTONDOWN / WM_RBUTTONDOWN @implemented
+LRESULT CACListView::OnMRButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, 
BOOL &bHandled)
+{
+    TRACE("CACListView::OnMRButtonDown(%p)\n", this);
+    return 0; // eat
+}
 
-            HeapFree(GetProcessHeap(), 0, pThis->m_txtbackup);
+// WM_MOUSEWHEEL @implemented
+LRESULT CACListView::OnMouseWheel(UINT uMsg, WPARAM wParam, LPARAM lParam, 
BOOL &bHandled)
+{
+    TRACE("CACListView::OnMouseWheel(%p)\n", this);
+    ATLASSERT(m_pDropDown);
+    LRESULT ret = DefWindowProcW(uMsg, wParam, lParam); // do default
+    m_pDropDown->UpdateScrollBar();
+    return ret;
+}
 
-            pThis->m_txtbackup = (WCHAR *)HeapAlloc(GetProcessHeap(), 
HEAP_ZERO_MEMORY, (wcslen(hwndText)+1)*sizeof(WCHAR));
+// WM_NCHITTEST
+LRESULT CACListView::OnNCHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled)
+{
+    TRACE("CACListView::OnNCHitTest(%p)\n", this);
+    ATLASSERT(m_pDropDown);
+    POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; // in screen 
coordinates
+    ScreenToClient(&pt); // into client coordinates
+    HWND hwndTarget = m_pDropDown->ChildWindowFromPoint(pt);
+    if (hwndTarget != m_hWnd)
+        return HTTRANSPARENT; // pass through (for resizing the drop-down 
window)
+    bHandled = FALSE; // do default
+    return 0;
+}
 
-            if (pThis->m_txtbackup)
-            {
-                wcscpy(pThis->m_txtbackup, hwndText);
-            }
-            else
-            {
-                TRACE("HeapAlloc failed to allocate %d bytes\n", 
(wcslen(hwndText)+1)*sizeof(WCHAR));
-            }
+//////////////////////////////////////////////////////////////////////////////
+// CACScrollBar
 
-            /* Returns if there is no text to search and we doesn't want to 
display all the entries */
-            if ((!displayall) && (! *hwndText) )
-                break;
+CACScrollBar::CACScrollBar() : m_pDropDown(NULL)
+{
+}
 
-            pThis->m_enumstr->Reset();
-            filled = FALSE;
-            size_t curlen = wcslen(hwndText);
+HWND CACScrollBar::Create(HWND hwndParent)
+{
+    ATLASSERT(m_hWnd == NULL);
+    DWORD dwStyle = WS_CHILD | /*WS_VISIBLE |*/ SBS_BOTTOMALIGN | SBS_VERT;
+    m_hWnd = ::CreateWindowExW(0, GetWndClassName(), NULL, dwStyle,
+                               0, 0, 0, 0, hwndParent, NULL,
+                               _AtlBaseModule.GetModuleInstance(), NULL);
+    // we don't subclass because no message handling is needed
+    return m_hWnd;
+}
 
-            for(cpt = 0;;)
-            {
-                CComHeapPtr<OLECHAR> strs;
-                hr = pThis->m_enumstr->Next(1, &strs, &fetched);
-                if (hr != S_OK)
-                    break;
+//////////////////////////////////////////////////////////////////////////////
+// CACSizeBox
 
-                if (!_wcsnicmp(hwndText, strs, curlen))
-                {
+CACSizeBox::CACSizeBox() : m_pDropDown(NULL), m_bDowner(TRUE), 
m_bLongList(FALSE)
+{
+}
 
-                    if (pThis->m_options & ACO_AUTOAPPEND && *hwndText)
-                    {
-                        CComBSTR str((PCWSTR)strs);
-                        memcpy(str.m_str, hwndText, curlen * sizeof(WCHAR));
-                        SetWindowTextW(hwnd, str);
-                        SendMessageW(hwnd, EM_SETSEL, curlen, str.Length());
-                        if (!(pThis->m_options & ACO_AUTOSUGGEST))
-                            break;
-                    }
-
-                    if (pThis->m_options & ACO_AUTOSUGGEST)
-                    {
-                        SendMessageW(pThis->m_hwndListBox, LB_ADDSTRING, 0, 
(LPARAM)(LPOLESTR)strs);
-                        filled = TRUE;
-                        cpt++;
-                    }
-                }
-            }
+HWND CACSizeBox::Create(HWND hwndParent)
+{
+    ATLASSERT(m_hWnd == NULL);
+    DWORD dwStyle = WS_CHILD | /*WS_VISIBLE |*/ SBS_SIZEBOX;
+    HWND hWnd = ::CreateWindowExW(0, GetWndClassName(), NULL, dwStyle,
+                                  0, 0, 0, 0, hwndParent, NULL,
+                                  _AtlBaseModule.GetModuleInstance(), NULL);
+    SubclassWindow(hWnd); // do subclass to handle message
+    return m_hWnd;
+}
 
-            if (pThis->m_options & ACO_AUTOSUGGEST)
-            {
-                if (filled)
-                {
-                    height = SendMessageW(pThis->m_hwndListBox, 
LB_GETITEMHEIGHT, 0, 0);
-                    SendMessageW(pThis->m_hwndListBox, LB_CARETOFF, 0, 0);
-                    GetWindowRect(hwnd, &r);
-                    SetParent(pThis->m_hwndListBox, HWND_DESKTOP);
-                    /* It seems that Windows XP displays 7 lines at most
-                       and otherwise displays a vertical scroll bar */
-                    SetWindowPos(pThis->m_hwndListBox, HWND_TOP,
-                         r.left, r.bottom + 1, r.right - r.left, min(height * 
7, height * (cpt + 1)),
-                         SWP_SHOWWINDOW );
-                }
-                else
-                {
-                    ShowWindow(pThis->m_hwndListBox, SW_HIDE);
-                }
-            }
+VOID CACSizeBox::SetStatus(BOOL bDowner, BOOL bLongList)
+{
+    // set flags
+    m_bDowner = bDowner;
+    m_bLongList = bLongList;
 
-        }; break;
+    if (bLongList)
+    {
+        SetWindowRgn(NULL, TRUE); // reset window region
+        return;
+    }
 
-        case WM_DESTROY:
-        {
-            /* Release our reference that we had since ->Init() */
-            pThis->Release();
-            return 0;
-        }
+    RECT rc;
+    GetWindowRect(&rc); // get size-box size
+    ::OffsetRect(&rc, -rc.left, -rc.top); // window regions use special 
coordinates
+    ATLASSERT(rc.left == 0 && rc.top == 0);
 
+    // create a trianglar region
+    HDC hDC = ::CreateCompatibleDC(NULL);
+    ::BeginPath(hDC);
+    if (m_bDowner)
+    {
+        ::MoveToEx(hDC, rc.right, 0, NULL);
+        ::LineTo(hDC, rc.right, rc.bottom);
+        ::LineTo(hDC, 0, rc.bottom);
+        ::LineTo(hDC, rc.right, 0);
+    }
+    else
+    {
+        ::MoveToEx(hDC, rc.right, rc.bottom, NULL);
+        ::LineTo(hDC, rc.right, 0);
+        ::LineTo(hDC, 0, 0);
+        ::LineTo(hDC, rc.right, rc.bottom);
+    }
+    ::EndPath(hDC);
+    HRGN hRgn = ::PathToRegion(hDC);
+    ::DeleteDC(hDC);
 
-        default:
-        {
-            return CallWindowProcW(pThis->m_wpOrigEditProc, hwnd, uMsg, 
wParam, lParam);
-        }
+    SetWindowRgn(hRgn, TRUE); // set the trianglar region
+}
 
-    }
+// WM_ERASEBKGND
+LRESULT CACSizeBox::OnEraseBkGnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled)
+{
+    return TRUE; // do nothing (for quick drawing)
+}
 
-    return 0;
+// WM_NCHITTEST
+LRESULT CACSizeBox::OnNCHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled)
+{
+    return HTTRANSPARENT; // pass through
 }
 
-LRESULT APIENTRY CAutoComplete::ACLBoxSubclassProc(HWND hwnd, UINT uMsg, 
WPARAM wParam, LPARAM lParam)
+// WM_PAINT
+LRESULT CACSizeBox::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled)
 {
-    CAutoComplete *pThis = reinterpret_cast<CAutoComplete 
*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
-    WCHAR *msg;
-    int sel, len;
+    CRect rc;
+    GetClientRect(&rc);
+
+    PAINTSTRUCT ps;
+    HDC hDC = BeginPaint(&ps);
+    if (!hDC)
+        return 0;
 
-    switch (uMsg)
+    // fill background
+    ::FillRect(hDC, &rc, ::GetSysColorBrush(COLOR_3DFACE));
+
+    // draw size-box
+    INT cxy = rc.Width();
+    for (INT shift = 0; shift < 2; ++shift)
     {
-        case WM_MOUSEMOVE:
-        {
-            sel = SendMessageW(hwnd, LB_ITEMFROMPOINT, 0, lParam);
-            SendMessageW(hwnd, LB_SETCURSEL, (WPARAM)sel, (LPARAM)0);
-        }; break;
-        
-        case WM_LBUTTONDOWN:
+        // choose pen color
+        INT iColor = ((shift == 0) ? COLOR_HIGHLIGHTTEXT : COLOR_3DSHADOW);
+        HPEN hPen = ::CreatePen(PS_SOLID, 1, ::GetSysColor(iColor));
+        HGDIOBJ hPenOld = ::SelectObject(hDC, hPen);
+        // do loop to draw the slanted lines
+        for (INT delta = cxy / 4; delta < cxy; delta += cxy / 4)
         {
-            sel = SendMessageW(hwnd, LB_GETCURSEL, 0, 0);
-            
-            if (sel < 0)
-                break;
-            
-            len = SendMessageW(pThis->m_hwndListBox, LB_GETTEXTLEN, sel, 0);
-            msg = (WCHAR *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (len 
+ 1) * sizeof(WCHAR));
-            
-            if (msg)
+            // draw a grip line
+            if (m_bDowner)
             {
-                SendMessageW(hwnd, LB_GETTEXT, sel, (LPARAM)msg);
-                SendMessageW(pThis->m_hwndEdit, WM_SETTEXT, 0, (LPARAM)msg);
-                SendMessageW(pThis->m_hwndEdit, EM_SETSEL, 0, wcslen(msg));
-                ShowWindow(hwnd, SW_HIDE);
-            
-                HeapFree(GetProcessHeap(), 0, msg);
+                ::MoveToEx(hDC, rc.right, rc.top + delta + shift, NULL);
+                ::LineTo(hDC, rc.left + delta + shift, rc.bottom);
             }
             else
             {
-                TRACE("HeapAlloc failed to allocate %d bytes\n", (len + 1) * 
sizeof(WCHAR));
+                ::MoveToEx(hDC, rc.left + delta + shift, rc.top, NULL);
+                ::LineTo(hDC, rc.right, rc.bottom - delta - shift);
             }
-           
-        }; break;
-        
-        default:
-            return CallWindowProcW(pThis->m_wpOrigLBoxProc, hwnd, uMsg, 
wParam, lParam);
+        }
+        // delete pen
+        ::SelectObject(hDC, hPenOld);
+        ::DeleteObject(hPen);
     }
+
+    EndPaint(&ps);
     return 0;
 }
 
-/**************************************************************************
- *  IAutoCompleteDropDown
- */
-HRESULT STDMETHODCALLTYPE CAutoComplete::GetDropDownStatus(DWORD *pdwFlags, 
LPWSTR *ppwszString)
+//////////////////////////////////////////////////////////////////////////////
+// CAutoComplete public methods
+
+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)
 {
-    BOOL dropped = IsWindowVisible(m_hwndListBox);
+}
 
-    if (pdwFlags)
-        *pdwFlags = (dropped ? ACDD_VISIBLE : 0);
+HWND CAutoComplete::CreateDropDown()
+{
+    ATLASSERT(m_hWnd == NULL);
+    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);
+    return m_hWnd;
+}
 
-    if (ppwszString)
+CAutoComplete::~CAutoComplete()
+{
+    TRACE("CAutoComplete::~CAutoComplete(%p)\n", this);
+    if (m_hFont)
     {
-        *ppwszString = NULL;
-
-        if (dropped)
-        {
-            int sel = SendMessageW(m_hwndListBox, LB_GETCURSEL, 0, 0);
-            if (sel >= 0)
-            {
-                DWORD len = SendMessageW(m_hwndListBox, LB_GETTEXTLEN, sel, 0);
-                *ppwszString = (LPWSTR)CoTaskMemAlloc((len+1)*sizeof(WCHAR));
-                SendMessageW(m_hwndListBox, LB_GETTEXT, sel, 
(LPARAM)*ppwszString);
-            }
-        }
+        ::DeleteObject(m_hFont);
+        m_hFont = NULL;
     }
+}
 
-    return S_OK;
+BOOL CAutoComplete::CanAutoSuggest()
+{
+    return !!(m_dwOptions & ACO_AUTOSUGGEST) && m_bEnabled;
 }
 
-HRESULT STDMETHODCALLTYPE CAutoComplete::ResetEnumerator()
+BOOL CAutoComplete::CanAutoAppend()
 {
-    FIXME("(%p): stub\n", this);
-    return E_NOTIMPL;
+    return !!(m_dwOptions & ACO_AUTOAPPEND) && m_bEnabled;
 }
 
-/**************************************************************************
- *  IEnumString
- */
-HRESULT STDMETHODCALLTYPE CAutoComplete::Next(ULONG celt, LPOLESTR *rgelt, 
ULONG *pceltFetched)
+BOOL CAutoComplete::UseTab()
 {
-    FIXME("(%p, %d, %p, %p): stub\n", this, celt, rgelt, pceltFetched);
-    *pceltFetched = 0;
-    return E_NOTIMPL;
+    return !!(m_dwOptions & ACO_USETAB) && m_bEnabled;
 }
 
-HRESULT STDMETHODCALLTYPE CAutoComplete::Skip(ULONG celt)
+BOOL CAutoComplete::IsComboBoxDropped()
 {
-    FIXME("(%p, %d): stub\n", this, celt);
-    return E_NOTIMPL;
+    if (!::IsWindow(m_hwndCombo))
+        return FALSE;
+    return (BOOL)::SendMessageW(m_hwndCombo, CB_GETDROPPEDSTATE, 0, 0);
 }
 
-HRESULT STDMETHODCALLTYPE CAutoComplete::Reset()
+INT CAutoComplete::GetItemCount()
 {
-    FIXME("(%p): stub\n", this);
-    return E_NOTIMPL;
+    return m_outerList.GetSize();
 }
 
-HRESULT STDMETHODCALLTYPE CAutoComplete::Clone(IEnumString **ppOut)
+CStringW CAutoComplete::GetItemText(INT iItem)
 {
-    FIXME("(%p, %p): stub\n", this, ppOut);
-    *ppOut = NULL;
+    if (iItem < 0 || m_outerList.GetSize() <= iItem)
+        return L"";
+    return m_outerList[iItem];
+}
+
+CStringW CAutoComplete::GetEditText()
+{
+    BSTR bstrText = NULL;
+    CStringW strText;
+    if (m_hwndEdit.GetWindowTextW(bstrText))
+    {
+        strText = bstrText;
+        ::SysFreeString(bstrText);
+    }
+    return strText;
+}
+
+VOID CAutoComplete::SetEditText(LPCWSTR pszText)
+{
+    m_bInSetText = TRUE; // don't hide drop-down
+    m_hwndEdit.SetWindowTextW(pszText);
+    m_bInSetText = FALSE;
+}
+
+CStringW CAutoComplete::GetStemText()
+{
+    CStringW strText = GetEditText();
+    INT ich = strText.ReverseFind(L'\\');
+    if (ich == -1)
+        return L""; // no stem
+    return strText.Left(ich + 1);
+}
+
+VOID CAutoComplete::SetEditSel(INT ich0, INT ich1)
+{
+    m_hwndEdit.SendMessageW(EM_SETSEL, ich0, ich1);
+}
+
+VOID CAutoComplete::ShowDropDown()
+{
+    if (!m_hWnd || !CanAutoSuggest())
+        return;
+
+    INT cItems = GetItemCount();
+    if (cItems == 0 || ::GetFocus() != m_hwndEdit || IsComboBoxDropped())
+    {
+        // hide the drop-down if necessary
+        HideDropDown();
+        return;
+    }
+
+    RepositionDropDown();
+}
+
+VOID CAutoComplete::HideDropDown()
+{
+    ShowWindow(SW_HIDE);
+}
+
+VOID CAutoComplete::SelectItem(INT iItem)
+{
+    m_hwndList.SetCurSel(iItem);
+    if (iItem != -1)
+        m_hwndList.EnsureVisible(iItem, FALSE);
+}
+
+VOID CAutoComplete::DoAutoAppend()
+{
+    if (!CanAutoAppend()) // can we auto-append?
+        return; // don't append
+
+    CStringW strText = GetEditText(); // get the text
+    if (strText.IsEmpty())
+        return; // don't append
+
+    INT cItems = m_innerList.GetSize(); // get the number of items
+    if (cItems == 0)
+        return; // don't append
+
+    // get the common string
+    CStringW strCommon;
+    for (INT iItem = 0; iItem < cItems; ++iItem)
+    {
+        const CString& strItem = m_innerList[iItem]; // get the text of the 
item
+
+        if (iItem == 0) // the first item
+        {
+            strCommon = strItem; // store the text
+            continue;
+        }
+
+        for (INT ich = 0; ich < strCommon.GetLength(); ++ich)
+        {
+            if (ich < strItem.GetLength() && strCommon[ich] != strItem[ich])
+            {
+                strCommon = strCommon.Left(ich); // shrink the common string
+                break;
+            }
+        }
+    }
+
+    if (strCommon.IsEmpty() || strCommon.GetLength() <= strText.GetLength())
+        return; // no suggestion
+
+    // append suggestion
+    INT cchOld = strText.GetLength();
+    INT cchAppend = strCommon.GetLength() - cchOld;
+    strText += strCommon.Right(cchAppend);
+    SetEditText(strText);
+
+    // select the appended suggestion
+    SetEditSel(cchOld, strText.GetLength());
+}
+
+// go back a word ([Ctrl]+[Backspace])
+VOID CAutoComplete::DoBackWord()
+{
+    // get current selection
+    INT ich0, ich1;
+    m_hwndEdit.SendMessageW(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
+    CStringW str = GetEditText();
+    // extend the range
+    while (ich0 > 0 && IsWordBreak(str[ich0 - 1]))
+        --ich0;
+    while (ich0 > 0 && !IsWordBreak(str[ich0 - 1]))
+        --ich0;
+    // select range
+    SetEditSel(ich0, ich1);
+    // replace selection with empty text (this is actually deletion)
+    m_hwndEdit.SendMessageW(EM_REPLACESEL, TRUE, (LPARAM)L"");
+}
+
+VOID CAutoComplete::UpdateScrollBar()
+{
+    // copy scroll info from m_hwndList to m_hwndScrollBar
+    SCROLLINFO si = { sizeof(si), SIF_ALL };
+    m_hwndList.GetScrollInfo(SB_VERT, &si);
+    m_hwndScrollBar.SetScrollInfo(SB_CTL, &si, FALSE);
+
+    // show/hide scroll bar
+    INT cVisibles = m_hwndList.GetVisibleCount();
+    INT cItems = m_hwndList.GetItemCount();
+    BOOL bShowScroll = (cItems > cVisibles);
+    m_hwndScrollBar.ShowWindow(bShowScroll ? SW_SHOWNOACTIVATE : SW_HIDE);
+    if (bShowScroll)
+        m_hwndScrollBar.InvalidateRect(NULL, FALSE); // redraw
+}
+
+BOOL CAutoComplete::OnEditKeyDown(WPARAM wParam, LPARAM lParam)
+{
+    TRACE("CAutoComplete::OnEditKeyDown(%p, %p)\n", this, wParam);
+
+    UINT vk = (UINT)wParam; // virtual key
+    switch (vk)
+    {
+        case VK_HOME: case VK_END:
+        case VK_UP: case VK_DOWN:
+        case VK_PRIOR: case VK_NEXT:
+            // is suggestion available?
+            if (!CanAutoSuggest())
+                return FALSE; // do default
+            if (IsWindowVisible())
+                return OnListUpDown(vk);
+            break;
+        case VK_ESCAPE:
+        {
+            // is suggestion available?
+            if (!CanAutoSuggest())
+                return FALSE; // do default
+            if (IsWindowVisible())
+            {
+                SetEditText(m_strText); // revert the edit text
+                // select the end
+                INT cch = m_strText.GetLength();
+                SetEditSel(cch, cch);
+                HideDropDown(); // hide
+                return TRUE; // eat
+            }
+            break;
+        }
+        case VK_RETURN:
+        {
+            if (::GetKeyState(VK_CONTROL) < 0)
+            {
+                // quick edit
+                CStringW strText = GetEditText();
+                SetEditText(GetQuickEdit(strText));
+            }
+            else
+            {
+                // if item is selected, then update the edit text
+                INT iItem = m_hwndList.GetCurSel();
+                if (iItem != -1)
+                {
+                    CStringW strText = GetItemText(iItem);
+                    SetEditText(strText);
+                }
+            }
+            // select all
+            INT cch = m_hwndEdit.GetWindowTextLengthW();
+            SetEditSel(0, cch);
+            // hide
+            HideDropDown();
+            break;
+        }
+        case VK_TAB:
+        {
+            if (IsWindowVisible() && UseTab())
+            {
+                FIXME("ACO_USETAB\n");
+            }
+            break;
+        }
+        case VK_DELETE:
+        {
+            // is suggestion available?
+            if (!CanAutoSuggest())
+                return FALSE; // do default
+            m_hwndEdit.DefWindowProcW(WM_KEYDOWN, VK_DELETE, 0); // do default
+            if (IsWindowVisible())
+                OnEditUpdate(FALSE);
+            return TRUE; // eat
+        }
+        case VK_BACK:
+        {
+            if (::GetKeyState(VK_CONTROL) < 0)
+            {
+                DoBackWord();
+                return TRUE; // eat
+            }
+            break;
+        }
+        default:
+        {
+            break;
+        }
+    }
+    return FALSE; // default
+}
+
+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
+    if (CanAutoSuggest() || CanAutoAppend())
+        OnEditUpdate(wParam != VK_BACK);
+    return ret;
+}
+
+VOID CAutoComplete::OnEditUpdate(BOOL bAppendOK)
+{
+    CString strText = GetEditText();
+    if (m_strText.CompareNoCase(strText) == 0)
+    {
+        // no change
+        return;
+    }
+    UpdateCompletion(bAppendOK);
+}
+
+VOID CAutoComplete::OnListSelChange()
+{
+    // update EDIT text
+    INT iItem = m_hwndList.GetCurSel();
+    CStringW text = ((iItem != -1) ? GetItemText(iItem) : m_strText);
+    SetEditText(text);
+    // ensure the item visible
+    m_hwndList.EnsureVisible(iItem, FALSE);
+    // select the end
+    INT cch = text.GetLength();
+    SetEditSel(cch, cch);
+}
+
+BOOL CAutoComplete::OnListUpDown(UINT vk)
+{
+    if (!CanAutoSuggest())
+        return FALSE; // default
+
+    if ((m_dwOptions & ACO_UPDOWNKEYDROPSLIST) && !IsWindowVisible())
+    {
+        ShowDropDown();
+        return TRUE; // eat
+    }
+
+    INT iItem = m_hwndList.GetCurSel(); // current selection
+    INT cItems = m_hwndList.GetItemCount(); // the number of items
+    switch (vk)
+    {
+        case VK_HOME: case VK_END:
+            m_hwndList.SendMessageW(WM_KEYDOWN, vk, 0);
+            break;
+        case VK_UP:
+            if (iItem == -1)
+                iItem = cItems - 1;
+            else if (iItem == 0)
+                iItem = -1;
+            else
+                --iItem;
+            m_hwndList.SetCurSel(iItem);
+            break;
+        case VK_DOWN:
+            if (iItem == -1)
+                iItem = 0;
+            else if (iItem == cItems - 1)
+                iItem = -1;
+            else
+                ++iItem;
+            m_hwndList.SetCurSel(iItem);
+            break;
+        case VK_PRIOR:
+            if (iItem == -1)
+            {
+                iItem = cItems - 1;
+            }
+            else if (iItem == 0)
+            {
+                iItem = -1;
+            }
+            else
+            {
+                iItem -= m_hwndList.GetVisibleCount() - 1;
+                if (iItem < 0)
+                    iItem = 0;
+            }
+            m_hwndList.SetCurSel(iItem);
+            break;
+        case VK_NEXT:
+            if (iItem == -1)
+            {
+                iItem = 0;
+            }
+            else if (iItem == cItems - 1)
+            {
+                iItem = -1;
+            }
+            else
+            {
+                iItem += m_hwndList.GetVisibleCount() - 1;
+                if (iItem > cItems)
+                    iItem = cItems - 1;
+            }
+            m_hwndList.SetCurSel(iItem);
+            break;
+        default:
+        {
+            ATLASSERT(FALSE);
+            break;
+        }
+    }
+
+    return TRUE; // eat
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// CAutoComplete IAutoComplete methods
+
+// @implemented
+STDMETHODIMP CAutoComplete::Enable(BOOL fEnable)
+{
+    TRACE("(%p)->(%d)\n", this, fEnable);
+    m_bEnabled = fEnable;
+    return S_OK;
+}
+
+STDMETHODIMP
+CAutoComplete::Init(HWND hwndEdit, IUnknown *punkACL,
+                    LPCOLESTR pwszRegKeyPath, LPCOLESTR pwszQuickComplete)
+{
+    TRACE("(%p)->(0x%08lx, %p, %s, %s)\n",
+          this, hwndEdit, punkACL, debugstr_w(pwszRegKeyPath), 
debugstr_w(pwszQuickComplete));
+
+    if (m_dwOptions & ACO_AUTOSUGGEST)
+        TRACE(" ACO_AUTOSUGGEST\n");
+    if (m_dwOptions & ACO_AUTOAPPEND)
+        TRACE(" ACO_AUTOAPPEND\n");
+    if (m_dwOptions & ACO_SEARCH)
+        FIXME(" ACO_SEARCH not supported\n");
+    if (m_dwOptions & ACO_FILTERPREFIXES)
+        FIXME(" ACO_FILTERPREFIXES not supported\n");
+    if (m_dwOptions & ACO_USETAB)
+        FIXME(" ACO_USETAB not supported\n");
+    if (m_dwOptions & ACO_UPDOWNKEYDROPSLIST)
+        TRACE(" ACO_UPDOWNKEYDROPSLIST\n");
+    if (m_dwOptions & ACO_RTLREADING)
+        FIXME(" ACO_RTLREADING not supported\n");
+
+    // sanity check
+    if (m_hwndEdit || !punkACL)
+    {
+        ATLASSERT(0);
+        return E_INVALIDARG;
+    }
+
+    // set this pointer to m_hwndEdit
+    m_hwndEdit.m_pDropDown = this;
+
+    // do subclass textbox to watch messages
+    m_hwndEdit.SubclassWindow(hwndEdit);
+    // set word break procedure
+    m_hwndEdit.HookWordBreakProc(TRUE);
+    // save position
+    m_hwndEdit.GetWindowRect(&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));
+
+    // 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
+
+    UpdateDropDownState(); // create/hide the drop-down window if necessary
+
+    // load quick completion info
+    LoadQuickComplete(pwszRegKeyPath, pwszQuickComplete);
+
+    // any combobox for m_hwndEdit?
+    m_hwndCombo = NULL;
+    HWND hwndParent = ::GetParent(m_hwndEdit);
+    WCHAR szClass[16];
+    if (::GetClassNameW(hwndParent, szClass, _countof(szClass)))
+    {
+        if (::StrCmpIW(szClass, WC_COMBOBOXW) == 0 ||
+            ::StrCmpIW(szClass, WC_COMBOBOXEXW) == 0)
+        {
+            m_hwndCombo = hwndParent; // get combobox
+        }
+    }
+
+    return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// CAutoComplete IAutoComplete2 methods
+
+// @implemented
+STDMETHODIMP CAutoComplete::GetOptions(DWORD *pdwFlag)
+{
+    TRACE("(%p) -> (%p)\n", this, pdwFlag);
+    if (pdwFlag)
+    {
+        *pdwFlag = m_dwOptions;
+        return S_OK;
+    }
+    return E_INVALIDARG;
+}
+
+// @implemented
+STDMETHODIMP CAutoComplete::SetOptions(DWORD dwFlag)
+{
+    TRACE("(%p) -> (0x%x)\n", this, dwFlag);
+    m_dwOptions = dwFlag;
+    UpdateDropDownState(); // create/hide the drop-down window if necessary
+    return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// CAutoComplete IAutoCompleteDropDown methods
+
+// @implemented
+STDMETHODIMP CAutoComplete::GetDropDownStatus(DWORD *pdwFlags, LPWSTR 
*ppwszString)
+{
+    BOOL dropped = m_hwndList.IsWindowVisible();
+
+    if (pdwFlags)
+        *pdwFlags = (dropped ? ACDD_VISIBLE : 0);
+
+    if (ppwszString)
+    {
+        *ppwszString = NULL;
+
+        if (dropped)
+        {
+            // get selected item
+            INT iItem = m_hwndList.GetCurSel();
+            if (iItem >= 0)
+            {
+                // get the text of item
+                CStringW strText = m_hwndList.GetItemText(iItem);
+
+                // store to *ppwszString
+                SHStrDupW(strText, ppwszString);
+                if (*ppwszString == NULL)
+                    return E_OUTOFMEMORY;
+            }
+        }
+    }
+
+    return S_OK;
+}
+
+STDMETHODIMP CAutoComplete::ResetEnumerator()
+{
+    FIXME("(%p): stub\n", this);
+
+    HideDropDown();
+
+    if (IsWindowVisible())
+    {
+        OnEditUpdate(FALSE);
+    }
+
+    return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// CAutoComplete IEnumString methods
+
+// @implemented
+STDMETHODIMP CAutoComplete::Next(ULONG celt, LPOLESTR *rgelt, ULONG 
*pceltFetched)
+{
+    TRACE("(%p, %d, %p, %p)\n", this, celt, rgelt, pceltFetched);
+    if (rgelt)
+        *rgelt = NULL;
+    if (*pceltFetched)
+        *pceltFetched = 0;
+    if (celt != 1 || !rgelt || !pceltFetched || !m_pEnum)
+        return E_INVALIDARG;
+
+    LPWSTR pszText = NULL;
+    HRESULT hr = m_pEnum->Next(1, &pszText, pceltFetched);
+    if (hr == S_OK)
+        *rgelt = pszText;
+    else
+        ::CoTaskMemFree(pszText);
+    return hr;
+}
+
+// @implemented
+STDMETHODIMP CAutoComplete::Skip(ULONG celt)
+{
+    TRACE("(%p, %d)\n", this, celt);
     return E_NOTIMPL;
 }
+
+// @implemented
+STDMETHODIMP CAutoComplete::Reset()
+{
+    TRACE("(%p)\n", this);
+    if (m_pEnum)
+        return m_pEnum->Reset();
+    return E_FAIL;
+}
+
+// @implemented
+STDMETHODIMP CAutoComplete::Clone(IEnumString **ppOut)
+{
+    TRACE("(%p, %p)\n", this, ppOut);
+    if (ppOut)
+        *ppOut = NULL;
+    return E_NOTIMPL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// CAutoComplete protected methods
+
+VOID CAutoComplete::UpdateDropDownState()
+{
+    if (CanAutoSuggest())
+    {
+        // create the drop-down window if not existed
+        if (!m_hWnd)
+        {
+            AddRef();
+            if (!CreateDropDown())
+                Release();
+        }
+    }
+    else
+    {
+        // hide if existed
+        if (m_hWnd)
+            ShowWindow(SW_HIDE);
+    }
+}
+
+// calculate the positions of the controls
+VOID CAutoComplete::CalcRects(BOOL bDowner, RECT& rcList, RECT& rcScrollBar, 
RECT& rcSizeBox)
+{
+    // get the client rectangle
+    RECT rcClient;
+    GetClientRect(&rcClient);
+
+    // the list
+    rcList = rcClient;
+    rcList.right = rcList.left + CX_LIST;
+
+    // the scroll bar
+    rcScrollBar = rcClient;
+    rcScrollBar.left = rcClient.right - GetSystemMetrics(SM_CXVSCROLL);
+    if (bDowner)
+    {
+        rcScrollBar.top = 0;
+        rcScrollBar.bottom = rcClient.bottom - GetSystemMetrics(SM_CYHSCROLL);
+    }
+    else
+    {
+        rcScrollBar.top = GetSystemMetrics(SM_CYHSCROLL);
+    }
+
+    // the size box
+    rcSizeBox = rcClient;
+    rcSizeBox.left = rcClient.right - GetSystemMetrics(SM_CXVSCROLL);
+    if (bDowner)
+    {
+        rcSizeBox.top = rcClient.bottom - GetSystemMetrics(SM_CYHSCROLL);
+    }
+    else
+    {
+        rcSizeBox.top = 0;
+        rcSizeBox.bottom = rcClient.top + GetSystemMetrics(SM_CYHSCROLL);
+    }
+}
+
+VOID CAutoComplete::LoadQuickComplete(LPCWSTR pwszRegKeyPath, LPCWSTR 
pwszQuickComplete)
+{
+    m_strQuickComplete.Empty();
+
+    if (pwszRegKeyPath)
+    {
+        CStringW strPath = pwszRegKeyPath;
+        INT ichSep = strPath.ReverseFind(L'\\'); // find separator
+        if (ichSep != -1) // found the last separator
+        {
+            // split by the separator
+            CStringW strKey = strPath.Left(ichSep);
+            CStringW strName = strPath.Mid(ichSep + 1);
+
+            // load from registry
+            WCHAR szValue[MAX_PATH] = L"";
+            DWORD cbValue = sizeof(szValue), dwType = REG_NONE;
+            SHRegGetUSValueW(pwszRegKeyPath, strName, &dwType,
+                             szValue, &cbValue, FALSE, NULL, 0);
+            if (szValue[0] != 0 && cbValue != 0 &&
+                (dwType == REG_SZ || dwType == REG_EXPAND_SZ))
+            {
+                m_strQuickComplete = szValue;
+            }
+        }
+    }
+
+    if (pwszQuickComplete && m_strQuickComplete.IsEmpty())
+    {
+        m_strQuickComplete = pwszQuickComplete;
+    }
+}
+
+CStringW CAutoComplete::GetQuickEdit(LPCWSTR pszText)
+{
+    if (pszText[0] == 0 || m_strQuickComplete.IsEmpty())
+        return pszText;
+
+    // m_strQuickComplete will be "www.%s.com" etc.
+    CStringW ret;
+    ret.Format(m_strQuickComplete, pszText);
+    return ret;
+}
+
+VOID CAutoComplete::RepositionDropDown()
+{
+    // get nearest monitor from m_hwndEdit
+    HMONITOR hMon = ::MonitorFromWindow(m_hwndEdit, MONITOR_DEFAULTTONEAREST);
+    ATLASSERT(hMon != NULL);
+    if (hMon == NULL)
+        return;
+
+    // get nearest monitor info
+    MONITORINFO mi = { sizeof(mi) };
+    if (!::GetMonitorInfo(hMon, &mi))
+    {
+        ATLASSERT(FALSE);
+        return;
+    }
+
+    // get count and item height
+    INT cItems = GetItemCount();
+    INT cyItem = m_hwndList.m_cyItem;
+    ATLASSERT(cyItem > 0);
+
+    // get m_hwndEdit position
+    RECT rcEdit;
+    m_hwndEdit.GetWindowRect(&rcEdit);
+    INT x = rcEdit.left, y = rcEdit.bottom;
+
+    // get list extent
+    RECT rcMon = mi.rcMonitor;
+    INT cx = rcEdit.right - rcEdit.left, cy = cItems * cyItem;
+    BOOL bLongList = FALSE;
+    if (cy > CY_LIST)
+    {
+        cy = INT(CY_LIST / cyItem) * cyItem;
+        bLongList = TRUE;
+    }
+
+    // convert rectangle for frame
+    RECT rc = { 0, 0, cx, cy };
+    AdjustWindowRectEx(&rc, GetStyle(), FALSE, GetExStyle());
+    cy = rc.bottom - rc.top;
+
+    if (!m_bResized)
+    {
+        // is the drop-down window a 'downer' or 'upper'?
+        // NOTE: 'downer' is below the EDIT control. 'upper' is above the EDIT 
control.
+        m_bDowner = (rcEdit.bottom + cy < rcMon.bottom);
+    }
+
+    // adjust y and cy
+    if (m_bDowner)
+    {
+        if (rcMon.bottom < y + cy)
+        {
+            cy = ((rcMon.bottom - y) / cyItem) * cyItem;
+            bLongList = TRUE;
+        }
+    }
+    else
+    {
+        if (rcEdit.top < rcMon.top + cy)
+        {
+            cy = ((rcEdit.top - rcMon.top) / cyItem) * cyItem;
+            bLongList = TRUE;
+        }
+        y = rcEdit.top - cy;
+    }
+
+    // set status
+    m_hwndSizeBox.SetStatus(m_bDowner, bLongList);
+
+    if (m_bResized) // already resized?
+        PostMessageW(WM_SIZE, 0, 0); // re-layout
+    else
+        MoveWindow(x, y, cx, cy); // move
+
+    // show without activation
+    ShowWindow(SW_SHOWNOACTIVATE);
+}
+
+INT CAutoComplete::ReLoadInnerList()
+{
+    m_innerList.RemoveAll(); // clear contents
+
+    if (!m_pEnum)
+        return 0;
+
+    DWORD dwTick = ::GetTickCount(); // used for timeout
+
+    // reload the items
+    LPWSTR pszItem;
+    ULONG cGot;
+    HRESULT hr;
+    for (ULONG cTotal = 0; cTotal < MAX_ITEM_COUNT; ++cTotal)
+    {
+        // get next item
+        hr = m_pEnum->Next(1, &pszItem, &cGot);
+        //TRACE("m_pEnum->Next(%p): 0x%08lx\n", reinterpret_cast<IUnknown 
*>(m_pEnum), hr);
+        if (hr != S_OK)
+            break;
+
+        m_innerList.Add(pszItem); // append item to m_innerList
+        ::CoTaskMemFree(pszItem); // free
+
+        // check the timeout
+        if (::GetTickCount() - dwTick >= COMPLETION_TIMEOUT)
+            break; // too late
+    }
+
+    return m_innerList.GetSize(); // the number of items
+}
+
+// update inner list and m_strText and m_strStemText
+INT CAutoComplete::UpdateInnerList()
+{
+    // get text
+    CStringW strText = GetEditText();
+
+    BOOL bReset = FALSE, bExpand = FALSE; // flags
+
+    // if previous text was empty
+    if (m_strText.IsEmpty())
+    {
+        bReset = TRUE;
+    }
+    // save text
+    m_strText = strText;
+
+    // do expand the items if the stem is changed
+    CStringW strStemText = GetStemText();
+    if (m_strStemText.CompareNoCase(strStemText) != 0)
+    {
+        m_strStemText = strStemText;
+        bExpand = bReset = TRUE;
+    }
+
+    // reset if necessary
+    if (bReset && m_pEnum)
+    {
+        HRESULT hr = m_pEnum->Reset(); // IEnumString::Reset
+        TRACE("m_pEnum->Reset(%p): 0x%08lx\n",
+              static_cast<IUnknown *>(m_pEnum), hr);
+    }
+
+    // update ac list if necessary
+    if (bExpand && m_pACList)
+    {
+        HRESULT hr = m_pACList->Expand(strStemText); // IACList::Expand
+        TRACE("m_pACList->Expand(%p, %S): 0x%08lx\n",
+              static_cast<IUnknown *>(m_pACList),
+              static_cast<LPCWSTR>(strStemText), hr);
+    }
+
+    if (bExpand || m_innerList.GetSize() == 0)
+    {
+        // reload the inner list
+        ReLoadInnerList();
+    }
+
+    return m_innerList.GetSize();
+}
+
+INT CAutoComplete::UpdateOuterList()
+{
+    CStringW strText = GetEditText(); // get the text
+
+    // update the outer list from the inner list
+    m_outerList.RemoveAll();
+    for (INT iItem = 0; iItem < m_innerList.GetSize(); ++iItem)
+    {
+        // is the beginning matched?
+        const CStringW& strTarget = m_innerList[iItem];
+        if (::StrCmpNIW(strTarget, strText, strText.GetLength()) == 0)
+        {
+            m_outerList.Add(strTarget);
+        }
+    }
+
+    // sort the list
+    DoSort(m_outerList);
+    // unique
+    DoUniqueAndTrim(m_outerList);
+
+    // set the item count of the virtual listview
+    INT cItems = m_outerList.GetSize();
+    if (strText.IsEmpty())
+        cItems = 0;
+    m_hwndList.SendMessageW(LVM_SETITEMCOUNT, cItems, 0);
+
+    return cItems; // the number of items
+}
+
+VOID CAutoComplete::UpdateCompletion(BOOL bAppendOK)
+{
+    TRACE("CAutoComplete::UpdateCompletion(%p, %d)\n", this, bAppendOK);
+
+    // update inner list
+    UINT cItems = UpdateInnerList();
+    if (cItems == 0) // no items
+    {
+        HideDropDown();
+        return;
+    }
+
+    if (CanAutoSuggest()) // can we auto-suggest?
+    {
+        m_bInSelectItem = TRUE; // don't respond
+        SelectItem(-1); // select none
+        m_bInSelectItem = FALSE;
+
+        if (UpdateOuterList())
+            RepositionDropDown();
+        else
+            HideDropDown();
+        return;
+    }
+
+    if (CanAutoAppend() && bAppendOK) // can we auto-append?
+    {
+        DoAutoAppend();
+        return;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// CAutoComplete message handlers
+
+// WM_CREATE
+// This message is sent when the window is about to be created after 
WM_NCCREATE.
+// The return value is -1 (failure) or zero (success).
+LRESULT CAutoComplete::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled)
+{
+    TRACE("CAutoComplete::OnCreate(%p)\n", this);
+
+    // set the pointer of CAutoComplete
+    m_hwndList.m_pDropDown = this;
+    m_hwndScrollBar.m_pDropDown = this;
+    m_hwndSizeBox.m_pDropDown = this;
+
+    // create the children
+    m_hwndList.Create(m_hWnd);
+    if (!m_hwndList)
+        return -1; // failure
+    m_hwndSizeBox.Create(m_hWnd);
+    if (!m_hwndSizeBox)
+        return -1; // failure
+    m_hwndScrollBar.Create(m_hWnd);
+    if (!m_hwndScrollBar)
+        return -1; // failure
+
+    // show the controls
+    m_hwndList.ShowWindow(SW_SHOWNOACTIVATE);
+    m_hwndSizeBox.ShowWindow(SW_SHOWNOACTIVATE);
+    m_hwndScrollBar.ShowWindow(SW_SHOWNOACTIVATE);
+
+    // set the list font
+    m_hFont = reinterpret_cast<HFONT>(::GetStockObject(DEFAULT_GUI_FONT));
+    m_hwndList.SetFont(m_hFont);
+
+    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)
+{
+    TRACE("CAutoComplete::OnDestroy(%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;
+
+    // destroy controls
+    m_hwndList.DestroyWindow();
+    m_hwndScrollBar.DestroyWindow();
+    m_hwndSizeBox.DestroyWindow();
+
+    // clean up
+    m_hwndCombo = NULL;
+    Release();
+
+    return 0;
+}
+
+// WM_EXITSIZEMOVE
+// This message is sent once to a window after it has exited the moving or 
sizing mode.
+LRESULT CAutoComplete::OnExitSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, 
BOOL &bHandled)
+{
+    TRACE("CAutoComplete::OnExitSizeMove(%p)\n", this);
+    m_bResized = TRUE; // remember resized
+
+    ModifyStyle(WS_THICKFRAME, 0); // remove thick frame to resize
+    // frame changed
+    UINT uSWP_ = SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED | 
SWP_NOACTIVATE;
+    SetWindowPos(NULL, 0, 0, 0, 0, uSWP_);
+
+    m_hwndEdit.SetFocus(); // restore focus
+    return 0;
+}
+
+// WM_DRAWITEM @implemented
+// This message is sent to the owner window to draw m_hwndList.
+LRESULT CAutoComplete::OnDrawItem(UINT uMsg, WPARAM wParam, LPARAM lParam, 
BOOL &bHandled)
+{
+    LPDRAWITEMSTRUCT pDraw = reinterpret_cast<LPDRAWITEMSTRUCT>(lParam);
+    ATLASSERT(pDraw != NULL);
+    ATLASSERT(m_hwndList.GetStyle() & LVS_OWNERDRAWFIXED);
+
+    // sanity check
+    if (pDraw->CtlType != ODT_LISTVIEW || pDraw->hwndItem != m_hwndList)
+        return FALSE;
+
+    // item rectangle
+    RECT rcItem = pDraw->rcItem;
+
+    // get info
+    UINT iItem = pDraw->itemID; // the index of item
+    CStringW strItem = m_hwndList.GetItemText(iItem); // get text of item
+
+    // draw background and set text color
+    HDC hDC = pDraw->hDC;
+    BOOL bSelected = (pDraw->itemState & ODS_SELECTED);
+    if (bSelected)
+    {
+        ::FillRect(hDC, &rcItem, ::GetSysColorBrush(COLOR_HIGHLIGHT));
+        ::SetTextColor(hDC, ::GetSysColor(COLOR_HIGHLIGHTTEXT));
+    }
+    else
+    {
+        ::FillRect(hDC, &rcItem, ::GetSysColorBrush(COLOR_WINDOW));
+        ::SetTextColor(hDC, ::GetSysColor(COLOR_WINDOWTEXT));
+    }
+
+    // draw text
+    rcItem.left += ::GetSystemMetrics(SM_CXBORDER);
+    HGDIOBJ hFontOld = ::SelectObject(hDC, m_hFont);
+    const UINT uDT_ = DT_LEFT | DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER;
+    ::SetBkMode(hDC, TRANSPARENT);
+    ::DrawTextW(hDC, strItem, -1, &rcItem, uDT_);
+    ::SelectObject(hDC, hFontOld);
+
+    return TRUE;
+}
+
+// WM_GETMINMAXINFO @implemented
+// This message is sent to a window when the size or position of the window is 
about to change.
+LRESULT CAutoComplete::OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM 
lParam, BOOL &bHandled)
+{
+    // restrict minimum size
+    LPMINMAXINFO pInfo = reinterpret_cast<LPMINMAXINFO>(lParam);
+    pInfo->ptMinTrackSize.x = ::GetSystemMetrics(SM_CXVSCROLL);
+    pInfo->ptMinTrackSize.y = ::GetSystemMetrics(SM_CYHSCROLL);
+    return 0;
+}
+
+// WM_MEASUREITEM @implemented
+// This message is sent to the owner window to get the item extent of 
m_hwndList.
+LRESULT CAutoComplete::OnMeasureItem(UINT uMsg, WPARAM wParam, LPARAM lParam, 
BOOL &bHandled)
+{
+    LPMEASUREITEMSTRUCT pMeasure = 
reinterpret_cast<LPMEASUREITEMSTRUCT>(lParam);
+    ATLASSERT(pMeasure != NULL);
+    if (pMeasure->CtlType != ODT_LISTVIEW)
+        return FALSE;
+
+    ATLASSERT(m_hwndList.GetStyle() & LVS_OWNERDRAWFIXED);
+    pMeasure->itemHeight = m_hwndList.m_cyItem; // height of item
+    return TRUE;
+}
+
+// WM_MOUSEACTIVATE @implemented
+// The return value of this message specifies whether the window should be 
activated or not.
+LRESULT CAutoComplete::OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM 
lParam, BOOL &bHandled)
+{
+    return MA_NOACTIVATE; // don't activate by mouse
+}
+
+// WM_NCACTIVATE
+// This message is sent to a window to indicate an active or inactive state.
+LRESULT CAutoComplete::OnNCActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, 
BOOL &bHandled)
+{
+    bHandled = FALSE; // do default
+    return 0;
+}
+
+// WM_NCLBUTTONDOWN
+LRESULT CAutoComplete::OnNCLButtonDown(UINT uMsg, WPARAM wParam, LPARAM 
lParam, BOOL &bHandled)
+{
+    switch (wParam)
+    {
+        case HTBOTTOMRIGHT: case HTTOPRIGHT:
+        {
+            // add thick frame to resize.
+            ModifyStyle(0, WS_THICKFRAME);
+            // frame changed
+            UINT uSWP_ = SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | 
SWP_FRAMECHANGED | SWP_NOACTIVATE;
+            SetWindowPos(NULL, 0, 0, 0, 0, uSWP_);
+            break;
+        }
+    }
+    bHandled = FALSE; // do default
+    return 0;
+}
+
+// WM_NOTIFY
+// This message informs the parent window of a control that an event has 
occurred.
+LRESULT CAutoComplete::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled)
+{
+    LPNMHDR pnmh = reinterpret_cast<LPNMHDR>(lParam);
+    ATLASSERT(pnmh != NULL);
+
+    switch (pnmh->code)
+    {
+        case NM_DBLCLK: // double-clicked
+        {
+            TRACE("NM_DBLCLK\n");
+            HideDropDown();
+            break;
+        }
+        case NM_HOVER: // mouse is hovering
+        {
+            POINT pt;
+            ::GetCursorPos(&pt); // get cursor position in screen coordinates
+            m_hwndList.ScreenToClient(&pt); // into client coordinates
+            INT iItem = m_hwndList.ItemFromPoint(pt.x, pt.y);
+            if (iItem != -1)
+            {
+                m_bInSelectItem = TRUE; // don't respond
+                m_hwndList.SetCurSel(iItem); // select
+                m_bInSelectItem = FALSE;
+            }
+            return TRUE; // eat
+        }
+        case LVN_GETDISPINFOA: // for user's information only
+        {
+            TRACE("LVN_GETDISPINFOA\n");
+            if (pnmh->hwndFrom != m_hwndList)
+                break;
+
+            LV_DISPINFOA *pDispInfo = reinterpret_cast<LV_DISPINFOA *>(pnmh);
+            LV_ITEMA *pItem = &pDispInfo->item;
+            INT iItem = pItem->iItem;
+            if (iItem == -1)
+                break;
+
+            CStringW strText = GetItemText(iItem);
+            if (pItem->mask & LVIF_TEXT)
+                SHUnicodeToAnsi(strText, pItem->pszText, pItem->cchTextMax);
+            break;
+        }
+        case LVN_GETDISPINFOW: // for user's information only
+        {
+            TRACE("LVN_GETDISPINFOW\n");
+            if (pnmh->hwndFrom != m_hwndList)
+                break;
+
+            LV_DISPINFOW *pDispInfo = reinterpret_cast<LV_DISPINFOW *>(pnmh);
+            LV_ITEMW *pItem = &pDispInfo->item;
+            INT iItem = pItem->iItem;
+            if (iItem == -1)
+                break;
+
+            CStringW strText = GetItemText(iItem);
+            if (pItem->mask & LVIF_TEXT)
+                StringCbCopyW(pItem->pszText, pItem->cchTextMax, strText);
+            break;
+        }
+        case LVN_HOTTRACK: // enabled by LVS_EX_TRACKSELECT
+        {
+            TRACE("LVN_HOTTRACK\n");
+            LPNMLISTVIEW pListView = reinterpret_cast<LPNMLISTVIEW>(pnmh);
+            INT iItem = pListView->iItem;
+            TRACE("LVN_HOTTRACK: iItem:%d\n", iItem);
+            m_hwndList.SetCurSel(iItem);
+            m_hwndList.EnsureVisible(iItem, FALSE);
+            return TRUE;
+        }
+        case LVN_ITEMACTIVATE: // enabled by LVS_EX_ONECLICKACTIVATE
+        {
+            TRACE("LVN_ITEMACTIVATE\n");
+            LPNMITEMACTIVATE pItemActivate = 
reinterpret_cast<LPNMITEMACTIVATE>(pnmh);
+            INT iItem = pItemActivate->iItem;
+            TRACE("LVN_ITEMACTIVATE: iItem:%d\n", iItem);
+            if (iItem != -1) // the item is clicked
+            {
+                SelectItem(iItem);
+                HideDropDown();
+            }
+            break;
+        }
+        case LVN_ITEMCHANGED: // item info is changed
+        {
+            TRACE("LVN_ITEMCHANGED\n");
+            LPNMLISTVIEW pListView = reinterpret_cast<LPNMLISTVIEW>(pnmh);
+            if (pListView->uChanged & LVIF_STATE) // selection changed
+            {
+                // listview selection changed
+                if (!m_bInSelectItem)
+                {
+                    OnListSelChange();
+                }
+                UpdateScrollBar();
+            }
+            break;
+        }
+    }
+
+    return 0;
+}
+
+// WM_NCHITTEST @implemented
+// The return value is indicating the cursor shape and the behaviour.
+LRESULT CAutoComplete::OnNCHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, 
BOOL &bHandled)
+{
+    TRACE("CAutoComplete::OnNCHitTest(%p)\n", this);
+    POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) }; // in screen 
coordinates
+    ScreenToClient(&pt); // into client coordinates
+    if (ChildWindowFromPoint(pt) == m_hwndSizeBox) // hit?
+    {
+        // allow resizing (with cursor shape)
+        return m_bDowner ? HTBOTTOMRIGHT : HTTOPRIGHT;
+    }
+    bHandled = FALSE; // do default
+    return 0;
+}
+
+// WM_SIZE @implemented
+// 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);
+
+    // reposition the controls in smartest way
+    UINT uSWP_ = SWP_NOACTIVATE | SWP_NOCOPYBITS;
+    HDWP hDWP = ::BeginDeferWindowPos(3);
+    hDWP = ::DeferWindowPos(hDWP, m_hwndScrollBar, HWND_TOP,
+                            rcScrollBar.left, rcScrollBar.top,
+                            rcScrollBar.Width(), rcScrollBar.Height(), uSWP_);
+    hDWP = ::DeferWindowPos(hDWP, m_hwndSizeBox, m_hwndScrollBar,
+                            rcSizeBox.left, rcSizeBox.top,
+                            rcSizeBox.Width(), rcSizeBox.Height(), uSWP_);
+    hDWP = ::DeferWindowPos(hDWP, m_hwndList, m_hwndSizeBox,
+                            rcList.left, rcList.top,
+                            rcList.Width(), rcList.Height(), uSWP_);
+    ::EndDeferWindowPos(hDWP);
+
+    UpdateScrollBar();
+    return 0;
+}
+
+// WM_SHOWWINDOW
+LRESULT CAutoComplete::OnShowWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, 
BOOL &bHandled)
+{
+    // hook mouse events
+    BOOL bShow = (BOOL)wParam;
+    if (bShow)
+    {
+        s_hWatchWnd = m_hWnd; // watch this
+
+        // unhook mouse if any
+        if (s_hMouseHook)
+        {
+            HHOOK hHookOld = s_hMouseHook;
+            s_hMouseHook = NULL;
+            ::UnhookWindowsHookEx(hHookOld);
+        }
+
+        // hook mouse
+        s_hMouseHook = ::SetWindowsHookEx(WH_MOUSE, MouseProc, NULL, 
::GetCurrentThreadId());
+        ATLASSERT(s_hMouseHook != NULL);
+
+        // set timer
+        SetTimer(WATCH_TIMER_ID, WATCH_INTERVAL, NULL);
+
+        bHandled = FALSE; // do default
+        return 0;
+    }
+    else
+    {
+        // kill timer
+        KillTimer(WATCH_TIMER_ID);
+
+        s_hWatchWnd = NULL; // unwatch
+
+        // unhook mouse if any
+        if (s_hMouseHook)
+        {
+            HHOOK hHookOld = s_hMouseHook;
+            s_hMouseHook = NULL;
+            ::UnhookWindowsHookEx(hHookOld);
+        }
+
+        LRESULT ret = DefWindowProcW(uMsg, wParam, lParam); // do default
+
+        if (m_hwndCombo)
+            ::InvalidateRect(m_hwndCombo, NULL, TRUE); // redraw
+
+        m_outerList.RemoveAll(); // no use
+        return ret;
+    }
+}
+
+// WM_TIMER
+LRESULT CAutoComplete::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled)
+{
+    if (wParam != WATCH_TIMER_ID) // sanity check
+        return 0;
+
+    // if the textbox is dead, then kill the timer
+    if (!::IsWindow(m_hwndEdit))
+    {
+        KillTimer(WATCH_TIMER_ID);
+        return 0;
+    }
+
+    // m_hwndEdit is moved?
+    RECT rcEdit;
+    m_hwndEdit.GetWindowRect(&rcEdit);
+    if (!::EqualRect(&rcEdit, &m_rcEdit))
+    {
+        // if so, hide
+        HideDropDown();
+
+        m_rcEdit = rcEdit; // update rectangle
+        m_bResized = FALSE; // clear flag
+    }
+
+    return 0;
+}
+
+// WM_VSCROLL
+// This message is sent when a scroll event occurs.
+LRESULT CAutoComplete::OnVScroll(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled)
+{
+    TRACE("CAutoComplete::OnVScroll(%p)\n", this);
+    WORD code = LOWORD(wParam);
+    switch (code)
+    {
+        case SB_THUMBPOSITION: case SB_THUMBTRACK:
+        {
+            // get the scrolling info
+            INT nPos = HIWORD(wParam);
+            SCROLLINFO si = { sizeof(si), SIF_ALL };
+            m_hwndList.GetScrollInfo(SB_VERT, &si);
+
+            // scroll the list-view by CListView::EnsureVisible
+            INT cItems = m_hwndList.GetItemCount();
+            // iItem : cItems == (nPos - si.nMin) : (si.nMax - si.nMin).
+            INT iItem = cItems * (nPos - si.nMin) / (si.nMax - si.nMin);
+            if (nPos > si.nPos)
+            {
+                iItem += m_hwndList.GetVisibleCount();
+                if (iItem >= cItems)
+                    iItem = cItems - 1;
+            }
+            m_hwndList.EnsureVisible(iItem, FALSE);
+
+            // update scrolling position of m_hwndScrollBar
+            si.fMask = SIF_POS;
+            m_hwndList.GetScrollInfo(SB_VERT, &si);
+            m_hwndScrollBar.SetScrollInfo(SB_VERT, &si, FALSE);
+            break;
+        }
+        default:
+        {
+            // pass it to m_hwndList
+            m_hwndList.SendMessageW(WM_VSCROLL, wParam, lParam);
+            UpdateScrollBar();
+            break;
+        }
+    }
+    return 0;
+}
diff --git a/dll/win32/browseui/CAutoComplete.h 
b/dll/win32/browseui/CAutoComplete.h
index 7430307eef3..eb979baf314 100644
--- a/dll/win32/browseui/CAutoComplete.h
+++ b/dll/win32/browseui/CAutoComplete.h
@@ -19,65 +19,286 @@
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
+#pragma once
 
-#ifndef _AUTOCOMPLETE_H_
-#define _AUTOCOMPLETE_H_
+#include "atltypes.h"
+#include "rosctrls.h"
 
-class CAutoComplete :
-    public CComCoClass<CAutoComplete, &CLSID_AutoComplete>,
-    public CComObjectRootEx<CComMultiThreadModelNoCS>,
-    public IAutoComplete2,
-    public IAutoCompleteDropDown,
-    public IEnumString
+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
+
+class CACListView : public CWindowImpl<CACListView, CListView>
+{
+public:
+    CAutoComplete* m_pDropDown;
+    INT m_cyItem;
+    static LPCWSTR GetWndClassName() { return WC_LISTVIEW; }
+
+    CACListView();
+    HWND Create(HWND hwndParent);
+    VOID SetFont(HFONT hFont);
+
+    INT GetVisibleCount();
+    CStringW GetItemText(INT iItem);
+    INT ItemFromPoint(INT x, INT y);
+
+    INT GetCurSel();
+    VOID SetCurSel(INT iItem);
+    VOID SelectHere(INT x, INT y);
+
+protected:
+    // message map
+    BEGIN_MSG_MAP(CACListView)
+        MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
+        MESSAGE_HANDLER(WM_LBUTTONUP, OnButtonUp)
+        MESSAGE_HANDLER(WM_MBUTTONDOWN, OnMRButtonDown)
+        MESSAGE_HANDLER(WM_MBUTTONUP, OnButtonUp)
+        MESSAGE_HANDLER(WM_MOUSEWHEEL, OnMouseWheel)
+        MESSAGE_HANDLER(WM_NCHITTEST, OnNCHitTest)
+        MESSAGE_HANDLER(WM_RBUTTONDOWN, OnMRButtonDown)
+        MESSAGE_HANDLER(WM_RBUTTONUP, OnButtonUp)
+    END_MSG_MAP()
+    // message handlers
+    LRESULT OnButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled);
+    LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled);
+    LRESULT OnMRButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled);
+    LRESULT OnMouseWheel(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled);
+    LRESULT OnNCHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled);
+};
+
+//////////////////////////////////////////////////////////////////////////////
+// CACScrollBar --- auto-completion scrollbar control
+
+class CACScrollBar : public CWindowImpl<CACScrollBar>
 {
-private:
-    BOOL                    m_enabled;
-    BOOL                    m_initialized;
-    HWND                    m_hwndEdit;
-    HWND                    m_hwndListBox;
-    WNDPROC                 m_wpOrigEditProc;
-    WNDPROC                 m_wpOrigLBoxProc;
-    LPWSTR                  m_txtbackup;        // HeapAlloc'ed
-    LPWSTR                  m_quickComplete;    // HeapAlloc'ed
-    CComPtr<IEnumString>    m_enumstr;
-    AUTOCOMPLETEOPTIONS     m_options;
 public:
+    CAutoComplete* m_pDropDown;
+    static LPCWSTR GetWndClassName() { return WC_SCROLLBARW; }
 
+    CACScrollBar();
+    HWND Create(HWND hwndParent);
+
+protected:
+    // message map
+    BEGIN_MSG_MAP(CACScrollBar)
+    END_MSG_MAP()
+};
+
+//////////////////////////////////////////////////////////////////////////////
+// CACSizeBox --- auto-completion size-box control
+
+class CACSizeBox : public CWindowImpl<CACSizeBox>
+{
+public:
+    CAutoComplete* m_pDropDown;
+    static LPCWSTR GetWndClassName() { return WC_SCROLLBARW; }
+
+    CACSizeBox();
+    HWND Create(HWND hwndParent);
+    VOID SetStatus(BOOL bDowner, BOOL bLongList);
+
+protected:
+    // protected variables
+    BOOL m_bDowner;
+    BOOL m_bLongList;
+    // message map
+    BEGIN_MSG_MAP(CACSizeBox)
+        MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkGnd)
+        MESSAGE_HANDLER(WM_NCHITTEST, OnNCHitTest)
+        MESSAGE_HANDLER(WM_PAINT, OnPaint)
+    END_MSG_MAP()
+    // message handlers
+    LRESULT OnEraseBkGnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled);
+    LRESULT OnNCHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled);
+    LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
+};
+
+//////////////////////////////////////////////////////////////////////////////
+// CAutoComplete --- auto-completion drop-down window
+
+#define WC_DROPDOWNW L"Auto-Suggest Dropdown" // the window class name
+
+class CAutoComplete
+    : public CComCoClass<CAutoComplete, &CLSID_AutoComplete>
+    , public CComObjectRootEx<CComMultiThreadModelNoCS>
+    , public CWindowImpl<CAutoComplete>
+    , public IAutoComplete2
+    , public IAutoCompleteDropDown
+    , public IEnumString
+{
+public:
+    DECLARE_WND_CLASS_EX(WC_DROPDOWNW, CS_DROPSHADOW | CS_SAVEBITS, 
COLOR_3DFACE)
+    static LPCWSTR GetWndClassName() { return WC_DROPDOWNW; }
+    BOOL m_bInSetText; // this flag avoids subsequent action in WM_SETTEXT
+    BOOL m_bInSelectItem; // this flag avoids subsequent action in 
LVN_ITEMCHANGED
+
+    // public methods
     CAutoComplete();
-    ~CAutoComplete();
+    HWND CreateDropDown();
+    virtual ~CAutoComplete();
 
-    static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM 
wParam, LPARAM lParam);
-    static LRESULT APIENTRY ACLBoxSubclassProc(HWND hwnd, UINT uMsg, WPARAM 
wParam, LPARAM lParam);
+    BOOL CanAutoSuggest();
+    BOOL CanAutoAppend();
+    BOOL UseTab();
+    BOOL IsComboBoxDropped();
+    INT GetItemCount();
+    CStringW GetItemText(INT iItem);
 
-    void CreateListbox();
+    CStringW GetEditText();
+    VOID SetEditText(LPCWSTR pszText);
+    CStringW GetStemText();
+    VOID SetEditSel(INT ich0, INT ich1);
 
-    // IAutoComplete2
-    virtual HRESULT WINAPI Enable(BOOL fEnable);
-    virtual HRESULT WINAPI Init(HWND hwndEdit, IUnknown *punkACL, LPCOLESTR 
pwzsRegKeyPath, LPCOLESTR pwszQuickComplete);
-    virtual HRESULT WINAPI GetOptions(DWORD *pdwFlag);
-    virtual HRESULT WINAPI SetOptions(DWORD dwFlag);
+    VOID ShowDropDown();
+    VOID HideDropDown();
+    VOID SelectItem(INT iItem);
+    VOID DoAutoAppend();
+    VOID DoBackWord();
+    VOID UpdateScrollBar();
 
-    // IAutoCompleteDropDown
-    virtual HRESULT STDMETHODCALLTYPE GetDropDownStatus(DWORD *pdwFlags, 
LPWSTR *ppwszString);
-    virtual HRESULT STDMETHODCALLTYPE ResetEnumerator();
+    LRESULT OnEditChar(WPARAM wParam, LPARAM lParam);
+    BOOL OnEditKeyDown(WPARAM wParam, LPARAM lParam);
+    VOID OnEditUpdate(BOOL bAppendOK);
+    VOID OnListSelChange();
+    BOOL OnListUpDown(UINT vk);
 
+    // IAutoComplete methods
+    STDMETHODIMP Enable(BOOL fEnable) override;
+    STDMETHODIMP Init(HWND hwndEdit, IUnknown *punkACL, LPCOLESTR 
pwszRegKeyPath,
+                      LPCOLESTR pwszQuickComplete) override;
+    // IAutoComplete2 methods
+    STDMETHODIMP GetOptions(DWORD *pdwFlag) override;
+    STDMETHODIMP SetOptions(DWORD dwFlag) override;
+    // IAutoCompleteDropDown methods
+    STDMETHODIMP GetDropDownStatus(DWORD *pdwFlags, LPWSTR *ppwszString) 
override;
+    STDMETHODIMP ResetEnumerator() override;
     // IEnumString methods
-    virtual HRESULT STDMETHODCALLTYPE Next(ULONG celt, LPOLESTR *rgelt, ULONG 
*pceltFetched);
-    virtual HRESULT STDMETHODCALLTYPE Skip(ULONG celt);
-    virtual HRESULT STDMETHODCALLTYPE Reset();
-    virtual HRESULT STDMETHODCALLTYPE Clone(IEnumString **ppenum);
-
-DECLARE_REGISTRY_RESOURCEID(IDR_AUTOCOMPLETE)
-DECLARE_NOT_AGGREGATABLE(CAutoComplete)
-
-DECLARE_PROTECT_FINAL_CONSTRUCT()
-
-BEGIN_COM_MAP(CAutoComplete)
-    COM_INTERFACE_ENTRY_IID(IID_IAutoComplete, IAutoComplete)
-    COM_INTERFACE_ENTRY_IID(IID_IAutoComplete2, IAutoComplete2)
-    COM_INTERFACE_ENTRY_IID(IID_IAutoCompleteDropDown, IAutoCompleteDropDown)
-    COM_INTERFACE_ENTRY_IID(IID_IEnumString, IEnumString)
-END_COM_MAP()
-};
+    STDMETHODIMP Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched) 
override;
+    STDMETHODIMP Skip(ULONG celt) override;
+    STDMETHODIMP Reset() override;
+    STDMETHODIMP Clone(IEnumString **ppOut) override;
+
+protected:
+    // The following variables are POD (plain old data):
+    BOOL m_bDowner; // downer or upper? (below textbox or above textbox)
+    DWORD m_dwOptions; // for IAutoComplete2::SetOptions
+    DWORD m_bEnabled; // the auto-composition is enabled?
+    HWND m_hwndCombo; // the combobox if any
+    HFONT m_hFont; // the font
+    BOOL m_bResized; // re-sized by size-box?
+    RECT m_rcEdit; // in screen coordinates, to watch the position
+    // 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
+    CComPtr<IEnumString> m_pEnum; // used for enumeration
+    CComPtr<IACList> m_pACList; // for IACList::Expand to update the list
+    CSimpleArray<CStringW> m_innerList; // internal list
+    CSimpleArray<CStringW> m_outerList; // owner data for virtual listview
+    // protected methods
+    VOID UpdateDropDownState();
+    VOID CalcRects(BOOL bDowner, RECT& rcListView, RECT& rcScrollBar, RECT& 
rcSizeBox);
+    VOID LoadQuickComplete(LPCWSTR pwszRegKeyPath, LPCWSTR pwszQuickComplete);
+    CStringW GetQuickEdit(LPCWSTR pszText);
+    VOID RepositionDropDown();
+    INT ReLoadInnerList();
+    INT UpdateInnerList();
+    INT UpdateOuterList();
+    VOID UpdateCompletion(BOOL bAppendOK);
+    // message map
+    BEGIN_MSG_MAP(CAutoComplete)
+        MESSAGE_HANDLER(WM_CREATE, OnCreate)
+        MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
+        MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem)
+        MESSAGE_HANDLER(WM_EXITSIZEMOVE, OnExitSizeMove)
+        MESSAGE_HANDLER(WM_GETMINMAXINFO, OnGetMinMaxInfo)
+        MESSAGE_HANDLER(WM_MEASUREITEM, OnMeasureItem)
+        MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)
+        MESSAGE_HANDLER(WM_NCACTIVATE, OnNCActivate)
+        MESSAGE_HANDLER(WM_NCLBUTTONDOWN, OnNCLButtonDown)
+        MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
+        MESSAGE_HANDLER(WM_NCHITTEST, OnNCHitTest)
+        MESSAGE_HANDLER(WM_SIZE, OnSize)
+        MESSAGE_HANDLER(WM_SHOWWINDOW, OnShowWindow)
+        MESSAGE_HANDLER(WM_TIMER, OnTimer)
+        MESSAGE_HANDLER(WM_VSCROLL, OnVScroll)
+    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 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);
+    LRESULT OnMeasureItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled);
+    LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled);
+    LRESULT OnNCActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled);
+    LRESULT OnNCLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled);
+    LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
+    LRESULT OnNCHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled);
+    LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
+    LRESULT OnShowWindow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL 
&bHandled);
+    LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
+    LRESULT OnVScroll(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled);
 
-#endif /* _AUTOCOMPLETE_H_ */
+    DECLARE_REGISTRY_RESOURCEID(IDR_AUTOCOMPLETE)
+    DECLARE_NOT_AGGREGATABLE(CAutoComplete)
+    DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+    BEGIN_COM_MAP(CAutoComplete)
+        COM_INTERFACE_ENTRY_IID(IID_IAutoComplete, IAutoComplete)
+        COM_INTERFACE_ENTRY_IID(IID_IAutoComplete2, IAutoComplete2)
+        COM_INTERFACE_ENTRY_IID(IID_IAutoCompleteDropDown, 
IAutoCompleteDropDown)
+        COM_INTERFACE_ENTRY_IID(IID_IEnumString, IEnumString)
+    END_COM_MAP()
+};

Reply via email to