mirror of
https://github.com/reactos/reactos.git
synced 2025-08-04 16:45:46 +00:00
[BROWSEUI] auto-completion: Support large number items (#3592)
If the items are too many, enable filtering in item enumeration. CORE-9281
This commit is contained in:
parent
82c908195c
commit
773ad7aebc
2 changed files with 134 additions and 76 deletions
|
@ -32,7 +32,7 @@
|
||||||
#define CX_LIST 30160 // width of m_hwndList (very wide but alright)
|
#define CX_LIST 30160 // width of m_hwndList (very wide but alright)
|
||||||
#define CY_LIST 288 // maximum height of drop-down window
|
#define CY_LIST 288 // maximum height of drop-down window
|
||||||
#define CY_ITEM 18 // default height of listview item
|
#define CY_ITEM 18 // default height of listview item
|
||||||
#define COMPLETION_TIMEOUT 250 // in milliseconds
|
#define COMPLETION_TIMEOUT 300 // in milliseconds
|
||||||
#define MAX_ITEM_COUNT 1000 // the maximum number of items
|
#define MAX_ITEM_COUNT 1000 // the maximum number of items
|
||||||
#define WATCH_TIMER_ID 0xFEEDBEEF // timer ID to watch m_rcEdit
|
#define WATCH_TIMER_ID 0xFEEDBEEF // timer ID to watch m_rcEdit
|
||||||
#define WATCH_INTERVAL 300 // in milliseconds
|
#define WATCH_INTERVAL 300 // in milliseconds
|
||||||
|
@ -640,6 +640,7 @@ CAutoComplete::CAutoComplete()
|
||||||
, m_bDowner(TRUE), m_dwOptions(ACO_AUTOAPPEND | ACO_AUTOSUGGEST)
|
, m_bDowner(TRUE), m_dwOptions(ACO_AUTOAPPEND | ACO_AUTOSUGGEST)
|
||||||
, m_bEnabled(TRUE), m_hwndCombo(NULL), m_hFont(NULL), m_bResized(FALSE)
|
, m_bEnabled(TRUE), m_hwndCombo(NULL), m_hFont(NULL), m_bResized(FALSE)
|
||||||
, m_hwndEdit(NULL), m_fnOldEditProc(NULL), m_fnOldWordBreakProc(NULL)
|
, m_hwndEdit(NULL), m_fnOldEditProc(NULL), m_fnOldWordBreakProc(NULL)
|
||||||
|
, m_bPartialList(FALSE), m_dwTick(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,46 +668,46 @@ CAutoComplete::~CAutoComplete()
|
||||||
m_pACList.Release();
|
m_pACList.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL CAutoComplete::CanAutoSuggest()
|
inline BOOL CAutoComplete::CanAutoSuggest() const
|
||||||
{
|
{
|
||||||
return !!(m_dwOptions & ACO_AUTOSUGGEST) && m_bEnabled;
|
return !!(m_dwOptions & ACO_AUTOSUGGEST) && m_bEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL CAutoComplete::CanAutoAppend()
|
inline BOOL CAutoComplete::CanAutoAppend() const
|
||||||
{
|
{
|
||||||
return !!(m_dwOptions & ACO_AUTOAPPEND) && m_bEnabled;
|
return !!(m_dwOptions & ACO_AUTOAPPEND) && m_bEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL CAutoComplete::UseTab()
|
inline BOOL CAutoComplete::UseTab() const
|
||||||
{
|
{
|
||||||
return !!(m_dwOptions & ACO_USETAB) && m_bEnabled;
|
return !!(m_dwOptions & ACO_USETAB) && m_bEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL CAutoComplete::IsComboBoxDropped()
|
inline BOOL CAutoComplete::IsComboBoxDropped() const
|
||||||
{
|
{
|
||||||
if (!::IsWindow(m_hwndCombo))
|
if (!::IsWindow(m_hwndCombo))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
return (BOOL)::SendMessageW(m_hwndCombo, CB_GETDROPPEDSTATE, 0, 0);
|
return (BOOL)::SendMessageW(m_hwndCombo, CB_GETDROPPEDSTATE, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL CAutoComplete::FilterPrefixes()
|
inline BOOL CAutoComplete::FilterPrefixes() const
|
||||||
{
|
{
|
||||||
return !!(m_dwOptions & ACO_FILTERPREFIXES) && m_bEnabled;
|
return !!(m_dwOptions & ACO_FILTERPREFIXES) && m_bEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
INT CAutoComplete::GetItemCount()
|
inline INT CAutoComplete::GetItemCount() const
|
||||||
{
|
{
|
||||||
return m_outerList.GetSize();
|
return m_outerList.GetSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
CStringW CAutoComplete::GetItemText(INT iItem)
|
CStringW CAutoComplete::GetItemText(INT iItem) const
|
||||||
{
|
{
|
||||||
if (iItem < 0 || m_outerList.GetSize() <= iItem)
|
if (iItem < 0 || m_outerList.GetSize() <= iItem)
|
||||||
return L"";
|
return L"";
|
||||||
return m_outerList[iItem];
|
return m_outerList[iItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
CStringW CAutoComplete::GetEditText()
|
inline CStringW CAutoComplete::GetEditText() const
|
||||||
{
|
{
|
||||||
WCHAR szText[L_MAX_URL_LENGTH];
|
WCHAR szText[L_MAX_URL_LENGTH];
|
||||||
if (::GetWindowTextW(m_hwndEdit, szText, _countof(szText)))
|
if (::GetWindowTextW(m_hwndEdit, szText, _countof(szText)))
|
||||||
|
@ -721,9 +722,8 @@ VOID CAutoComplete::SetEditText(LPCWSTR pszText)
|
||||||
m_bInSetText = FALSE;
|
m_bInSetText = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
CStringW CAutoComplete::GetStemText()
|
inline CStringW CAutoComplete::GetStemText(const CStringW& strText) const
|
||||||
{
|
{
|
||||||
CStringW strText = GetEditText();
|
|
||||||
INT ich = strText.ReverseFind(L'\\');
|
INT ich = strText.ReverseFind(L'\\');
|
||||||
if (ich == -1)
|
if (ich == -1)
|
||||||
return L""; // no stem
|
return L""; // no stem
|
||||||
|
@ -1284,7 +1284,7 @@ VOID CAutoComplete::UpdateDropDownState()
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate the positions of the controls
|
// calculate the positions of the controls
|
||||||
VOID CAutoComplete::CalcRects(BOOL bDowner, RECT& rcList, RECT& rcScrollBar, RECT& rcSizeBox)
|
VOID CAutoComplete::CalcRects(BOOL bDowner, RECT& rcList, RECT& rcScrollBar, RECT& rcSizeBox) const
|
||||||
{
|
{
|
||||||
// get the client rectangle
|
// get the client rectangle
|
||||||
RECT rcClient;
|
RECT rcClient;
|
||||||
|
@ -1354,7 +1354,7 @@ VOID CAutoComplete::LoadQuickComplete(LPCWSTR pwszRegKeyPath, LPCWSTR pwszQuickC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CStringW CAutoComplete::GetQuickEdit(LPCWSTR pszText)
|
CStringW CAutoComplete::GetQuickEdit(LPCWSTR pszText) const
|
||||||
{
|
{
|
||||||
if (pszText[0] == 0 || m_strQuickComplete.IsEmpty())
|
if (pszText[0] == 0 || m_strQuickComplete.IsEmpty())
|
||||||
return pszText;
|
return pszText;
|
||||||
|
@ -1444,20 +1444,45 @@ VOID CAutoComplete::RepositionDropDown()
|
||||||
ShowWindow(SW_SHOWNOACTIVATE);
|
ShowWindow(SW_SHOWNOACTIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
INT CAutoComplete::ReLoadInnerList()
|
inline BOOL
|
||||||
|
CAutoComplete::DoesMatch(const CStringW& strTarget, const CStringW& strText) const
|
||||||
|
{
|
||||||
|
CStringW strBody;
|
||||||
|
if (DropPrefix(strTarget, strBody))
|
||||||
|
{
|
||||||
|
if (::StrCmpNIW(strBody, strText, strText.GetLength()) == 0)
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
else if (::StrCmpNIW(strTarget, strText, strText.GetLength()) == 0)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID CAutoComplete::ScrapeOffList(const CStringW& strText, CSimpleArray<CStringW>& array)
|
||||||
|
{
|
||||||
|
for (INT iItem = array.GetSize() - 1; iItem >= 0; --iItem)
|
||||||
|
{
|
||||||
|
if (!DoesMatch(array[iItem], strText))
|
||||||
|
array.RemoveAt(iItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID CAutoComplete::ReLoadInnerList(const CStringW& strText)
|
||||||
{
|
{
|
||||||
m_innerList.RemoveAll(); // clear contents
|
m_innerList.RemoveAll(); // clear contents
|
||||||
|
m_bPartialList = FALSE;
|
||||||
|
|
||||||
if (!m_pEnum)
|
if (!m_pEnum || strText.IsEmpty())
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
DWORD dwTick = ::GetTickCount(); // used for timeout
|
|
||||||
|
|
||||||
// reload the items
|
// reload the items
|
||||||
LPWSTR pszItem;
|
LPWSTR pszItem;
|
||||||
ULONG cGot;
|
ULONG cGot;
|
||||||
|
CStringW strTarget;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
for (ULONG cTotal = 0; cTotal < MAX_ITEM_COUNT; ++cTotal)
|
for (;;)
|
||||||
{
|
{
|
||||||
// get next item
|
// get next item
|
||||||
hr = m_pEnum->Next(1, &pszItem, &cGot);
|
hr = m_pEnum->Next(1, &pszItem, &cGot);
|
||||||
|
@ -1465,23 +1490,45 @@ INT CAutoComplete::ReLoadInnerList()
|
||||||
if (hr != S_OK)
|
if (hr != S_OK)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
m_innerList.Add(pszItem); // append item to m_innerList
|
strTarget = pszItem;
|
||||||
::CoTaskMemFree(pszItem); // free
|
::CoTaskMemFree(pszItem); // free
|
||||||
|
|
||||||
|
if (m_bPartialList) // if items are too many
|
||||||
|
{
|
||||||
|
// do filter the items
|
||||||
|
if (DoesMatch(strTarget, strText))
|
||||||
|
{
|
||||||
|
m_innerList.Add(strTarget);
|
||||||
|
|
||||||
|
if (m_innerList.GetSize() >= MAX_ITEM_COUNT)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_innerList.Add(strTarget); // append item to m_innerList
|
||||||
|
|
||||||
|
// if items are too many
|
||||||
|
if (m_innerList.GetSize() >= MAX_ITEM_COUNT)
|
||||||
|
{
|
||||||
|
// filter the items now
|
||||||
|
m_bPartialList = TRUE;
|
||||||
|
ScrapeOffList(strText, m_innerList);
|
||||||
|
|
||||||
|
if (m_innerList.GetSize() >= MAX_ITEM_COUNT)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check the timeout
|
// check the timeout
|
||||||
if (::GetTickCount() - dwTick >= COMPLETION_TIMEOUT)
|
if (::GetTickCount() - m_dwTick >= COMPLETION_TIMEOUT)
|
||||||
break; // too late
|
break; // too late
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_innerList.GetSize(); // the number of items
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update inner list and m_strText and m_strStemText
|
// update inner list and m_strText and m_strStemText
|
||||||
INT CAutoComplete::UpdateInnerList()
|
VOID CAutoComplete::UpdateInnerList(const CStringW& strText)
|
||||||
{
|
{
|
||||||
// get text
|
|
||||||
CStringW strText = GetEditText();
|
|
||||||
|
|
||||||
BOOL bReset = FALSE, bExpand = FALSE; // flags
|
BOOL bReset = FALSE, bExpand = FALSE; // flags
|
||||||
|
|
||||||
// if previous text was empty
|
// if previous text was empty
|
||||||
|
@ -1493,7 +1540,7 @@ INT CAutoComplete::UpdateInnerList()
|
||||||
m_strText = strText;
|
m_strText = strText;
|
||||||
|
|
||||||
// do expand the items if the stem is changed
|
// do expand the items if the stem is changed
|
||||||
CStringW strStemText = GetStemText();
|
CStringW strStemText = GetStemText(strText);
|
||||||
if (m_strStemText.CompareNoCase(strStemText) != 0)
|
if (m_strStemText.CompareNoCase(strStemText) != 0)
|
||||||
{
|
{
|
||||||
m_strStemText = strStemText;
|
m_strStemText = strStemText;
|
||||||
|
@ -1501,6 +1548,10 @@ INT CAutoComplete::UpdateInnerList()
|
||||||
bExpand = !m_strStemText.IsEmpty();
|
bExpand = !m_strStemText.IsEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the previous enumeration is too large
|
||||||
|
if (m_bPartialList)
|
||||||
|
bReset = bExpand = TRUE; // retry enumeratation
|
||||||
|
|
||||||
// reset if necessary
|
// reset if necessary
|
||||||
if (bReset && m_pEnum)
|
if (bReset && m_pEnum)
|
||||||
{
|
{
|
||||||
|
@ -1521,55 +1572,57 @@ INT CAutoComplete::UpdateInnerList()
|
||||||
if (bExpand || m_innerList.GetSize() == 0)
|
if (bExpand || m_innerList.GetSize() == 0)
|
||||||
{
|
{
|
||||||
// reload the inner list
|
// reload the inner list
|
||||||
ReLoadInnerList();
|
ReLoadInnerList(strText);
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_innerList.GetSize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
INT CAutoComplete::UpdateOuterList()
|
VOID CAutoComplete::UpdateOuterList(const CStringW& strText)
|
||||||
{
|
{
|
||||||
CStringW strText = GetEditText(); // get the text
|
if (strText.IsEmpty())
|
||||||
|
|
||||||
// update the outer list from the inner list
|
|
||||||
m_outerList.RemoveAll();
|
|
||||||
for (INT iItem = 0; iItem < m_innerList.GetSize(); ++iItem)
|
|
||||||
{
|
{
|
||||||
// is the beginning matched?
|
m_outerList.RemoveAll();
|
||||||
const CStringW& strTarget = m_innerList[iItem];
|
return;
|
||||||
CStringW strBody;
|
}
|
||||||
if (DropPrefix(strTarget, strBody))
|
|
||||||
|
if (m_bPartialList)
|
||||||
|
{
|
||||||
|
// it is already filtered
|
||||||
|
m_outerList = m_innerList;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// do filtering
|
||||||
|
m_outerList.RemoveAll();
|
||||||
|
for (INT iItem = 0; iItem < m_innerList.GetSize(); ++iItem)
|
||||||
{
|
{
|
||||||
if (::StrCmpNIW(strBody, strText, strText.GetLength()) == 0)
|
const CStringW& strTarget = m_innerList[iItem];
|
||||||
{
|
|
||||||
|
if (DoesMatch(strTarget, strText))
|
||||||
m_outerList.Add(strTarget);
|
m_outerList.Add(strTarget);
|
||||||
continue;
|
|
||||||
}
|
// check the timeout
|
||||||
}
|
if (::GetTickCount() - m_dwTick >= COMPLETION_TIMEOUT)
|
||||||
if (::StrCmpNIW(strTarget, strText, strText.GetLength()) == 0)
|
break; // too late
|
||||||
{
|
|
||||||
m_outerList.Add(strTarget);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort the list
|
if (::GetTickCount() - m_dwTick < COMPLETION_TIMEOUT)
|
||||||
DoSort(m_outerList);
|
{
|
||||||
// unique
|
// sort and unique
|
||||||
DoUniqueAndTrim(m_outerList);
|
DoSort(m_outerList);
|
||||||
|
DoUniqueAndTrim(m_outerList);
|
||||||
|
}
|
||||||
|
|
||||||
// set the item count of the virtual listview
|
// set the item count of the virtual listview
|
||||||
INT cItems = m_outerList.GetSize();
|
m_hwndList.SendMessageW(LVM_SETITEMCOUNT, m_outerList.GetSize(), 0);
|
||||||
if (strText.IsEmpty())
|
|
||||||
cItems = 0;
|
|
||||||
m_hwndList.SendMessageW(LVM_SETITEMCOUNT, cItems, 0);
|
|
||||||
|
|
||||||
return cItems; // the number of items
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID CAutoComplete::UpdateCompletion(BOOL bAppendOK)
|
VOID CAutoComplete::UpdateCompletion(BOOL bAppendOK)
|
||||||
{
|
{
|
||||||
TRACE("CAutoComplete::UpdateCompletion(%p, %d)\n", this, bAppendOK);
|
TRACE("CAutoComplete::UpdateCompletion(%p, %d)\n", this, bAppendOK);
|
||||||
|
|
||||||
|
m_dwTick = GetTickCount(); // to check the timeout
|
||||||
|
|
||||||
CStringW strText = GetEditText();
|
CStringW strText = GetEditText();
|
||||||
if (m_strText.CompareNoCase(strText) == 0)
|
if (m_strText.CompareNoCase(strText) == 0)
|
||||||
{
|
{
|
||||||
|
@ -1578,8 +1631,8 @@ VOID CAutoComplete::UpdateCompletion(BOOL bAppendOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
// update inner list
|
// update inner list
|
||||||
UINT cItems = UpdateInnerList();
|
UpdateInnerList(strText);
|
||||||
if (cItems == 0) // no items
|
if (m_innerList.GetSize() <= 0) // no items
|
||||||
{
|
{
|
||||||
HideDropDown();
|
HideDropDown();
|
||||||
return;
|
return;
|
||||||
|
@ -1591,7 +1644,8 @@ VOID CAutoComplete::UpdateCompletion(BOOL bAppendOK)
|
||||||
SelectItem(-1); // select none
|
SelectItem(-1); // select none
|
||||||
m_bInSelectItem = FALSE;
|
m_bInSelectItem = FALSE;
|
||||||
|
|
||||||
if (UpdateOuterList())
|
UpdateOuterList(strText);
|
||||||
|
if (m_outerList.GetSize() > 0)
|
||||||
RepositionDropDown();
|
RepositionDropDown();
|
||||||
else
|
else
|
||||||
HideDropDown();
|
HideDropDown();
|
||||||
|
|
|
@ -140,17 +140,17 @@ public:
|
||||||
HWND CreateDropDown();
|
HWND CreateDropDown();
|
||||||
virtual ~CAutoComplete();
|
virtual ~CAutoComplete();
|
||||||
|
|
||||||
BOOL CanAutoSuggest();
|
BOOL CanAutoSuggest() const;
|
||||||
BOOL CanAutoAppend();
|
BOOL CanAutoAppend() const;
|
||||||
BOOL UseTab();
|
BOOL UseTab() const;
|
||||||
BOOL IsComboBoxDropped();
|
BOOL IsComboBoxDropped() const;
|
||||||
BOOL FilterPrefixes();
|
BOOL FilterPrefixes() const;
|
||||||
INT GetItemCount();
|
INT GetItemCount() const;
|
||||||
CStringW GetItemText(INT iItem);
|
CStringW GetItemText(INT iItem) const;
|
||||||
|
|
||||||
CStringW GetEditText();
|
CStringW GetEditText() const;
|
||||||
VOID SetEditText(LPCWSTR pszText);
|
VOID SetEditText(LPCWSTR pszText);
|
||||||
CStringW GetStemText();
|
CStringW GetStemText(const CStringW& strText) const;
|
||||||
VOID SetEditSel(INT ich0, INT ich1);
|
VOID SetEditSel(INT ich0, INT ich1);
|
||||||
|
|
||||||
VOID ShowDropDown();
|
VOID ShowDropDown();
|
||||||
|
@ -194,6 +194,8 @@ protected:
|
||||||
HWND m_hwndEdit; // the textbox
|
HWND m_hwndEdit; // the textbox
|
||||||
WNDPROC m_fnOldEditProc; // old textbox procedure
|
WNDPROC m_fnOldEditProc; // old textbox procedure
|
||||||
EDITWORDBREAKPROCW m_fnOldWordBreakProc;
|
EDITWORDBREAKPROCW m_fnOldWordBreakProc;
|
||||||
|
BOOL m_bPartialList; // is the list partial?
|
||||||
|
DWORD m_dwTick; // to check timeout
|
||||||
// The following variables are non-POD:
|
// The following variables are non-POD:
|
||||||
CStringW m_strText; // internal text (used in selecting item and reverting text)
|
CStringW m_strText; // internal text (used in selecting item and reverting text)
|
||||||
CStringW m_strStemText; // dirname + '\\'
|
CStringW m_strStemText; // dirname + '\\'
|
||||||
|
@ -207,14 +209,16 @@ protected:
|
||||||
CSimpleArray<CStringW> m_outerList; // owner data for virtual listview
|
CSimpleArray<CStringW> m_outerList; // owner data for virtual listview
|
||||||
// protected methods
|
// protected methods
|
||||||
VOID UpdateDropDownState();
|
VOID UpdateDropDownState();
|
||||||
VOID CalcRects(BOOL bDowner, RECT& rcListView, RECT& rcScrollBar, RECT& rcSizeBox);
|
VOID CalcRects(BOOL bDowner, RECT& rcListView, RECT& rcScrollBar, RECT& rcSizeBox) const;
|
||||||
VOID LoadQuickComplete(LPCWSTR pwszRegKeyPath, LPCWSTR pwszQuickComplete);
|
VOID LoadQuickComplete(LPCWSTR pwszRegKeyPath, LPCWSTR pwszQuickComplete);
|
||||||
CStringW GetQuickEdit(LPCWSTR pszText);
|
CStringW GetQuickEdit(LPCWSTR pszText) const;
|
||||||
VOID RepositionDropDown();
|
VOID RepositionDropDown();
|
||||||
INT ReLoadInnerList();
|
VOID ReLoadInnerList(const CStringW& strText);
|
||||||
INT UpdateInnerList();
|
VOID UpdateInnerList(const CStringW& strText);
|
||||||
INT UpdateOuterList();
|
VOID UpdateOuterList(const CStringW& strText);
|
||||||
VOID UpdateCompletion(BOOL bAppendOK);
|
VOID UpdateCompletion(BOOL bAppendOK);
|
||||||
|
VOID ScrapeOffList(const CStringW& strText, CSimpleArray<CStringW>& array);
|
||||||
|
BOOL DoesMatch(const CStringW& strTarget, const CStringW& strText) const;
|
||||||
// message map
|
// message map
|
||||||
BEGIN_MSG_MAP(CAutoComplete)
|
BEGIN_MSG_MAP(CAutoComplete)
|
||||||
MESSAGE_HANDLER(WM_CREATE, OnCreate)
|
MESSAGE_HANDLER(WM_CREATE, OnCreate)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue