If the items are too many, enable filtering in item enumeration. CORE-9281
#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 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 COMPLETION_TIMEOUT 300 // 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
#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
, m_bDowner(TRUE), m_dwOptions(ACO_AUTOAPPEND | ACO_AUTOSUGGEST)
, m_bEnabled(TRUE), m_hwndCombo(NULL), m_hFont(NULL), m_bResized(FALSE)
, m_hwndEdit(NULL), m_fnOldEditProc(NULL), m_fnOldWordBreakProc(NULL)
, m_bDowner(TRUE), m_dwOptions(ACO_AUTOAPPEND | ACO_AUTOSUGGEST)
, m_bEnabled(TRUE), m_hwndCombo(NULL), m_hFont(NULL), m_bResized(FALSE)
, m_hwndEdit(NULL), m_fnOldEditProc(NULL), m_fnOldWordBreakProc(NULL)
+ , m_bPartialList(FALSE), m_dwTick(0)
-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))
return FALSE;
return (BOOL)::SendMessageW(m_hwndCombo, CB_GETDROPPEDSTATE, 0, 0);
}
{
if (!::IsWindow(m_hwndCombo))
return FALSE;
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)
return L"";
return m_outerList[iItem];
}
{
if (iItem < 0 || m_outerList.GetSize() <= iItem)
return L"";
return m_outerList[iItem];
}
-CStringW CAutoComplete::GetEditText()
+inline CStringW CAutoComplete::GetEditText() const
{
WCHAR szText[L_MAX_URL_LENGTH];
if (::GetWindowTextW(m_hwndEdit, szText, _countof(szText)))
{
WCHAR szText[L_MAX_URL_LENGTH];
if (::GetWindowTextW(m_hwndEdit, szText, _countof(szText)))
-CStringW CAutoComplete::GetStemText()
+inline CStringW CAutoComplete::GetStemText(const CStringW& strText) const
- CStringW strText = GetEditText();
INT ich = strText.ReverseFind(L'\\');
if (ich == -1)
return L""; // no stem
INT ich = strText.ReverseFind(L'\\');
if (ich == -1)
return L""; // no stem
}
// 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
RECT rcClient;
{
// get the client rectangle
RECT rcClient;
-CStringW CAutoComplete::GetQuickEdit(LPCWSTR pszText)
+CStringW CAutoComplete::GetQuickEdit(LPCWSTR pszText) const
{
if (pszText[0] == 0 || m_strQuickComplete.IsEmpty())
return pszText;
{
if (pszText[0] == 0 || m_strQuickComplete.IsEmpty())
return pszText;
ShowWindow(SW_SHOWNOACTIVATE);
}
ShowWindow(SW_SHOWNOACTIVATE);
}
-INT CAutoComplete::ReLoadInnerList()
+inline BOOL
+CAutoComplete::DoesMatch(const CStringW& strTarget, const CStringW& strText) const
- m_innerList.RemoveAll(); // clear contents
+ 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;
+}
- if (!m_pEnum)
- return 0;
+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_bPartialList = FALSE;
- DWORD dwTick = ::GetTickCount(); // used for timeout
+ if (!m_pEnum || strText.IsEmpty())
+ return;
// reload the items
LPWSTR pszItem;
ULONG cGot;
// reload the items
LPWSTR pszItem;
ULONG cGot;
- for (ULONG cTotal = 0; cTotal < MAX_ITEM_COUNT; ++cTotal)
{
// get next item
hr = m_pEnum->Next(1, &pszItem, &cGot);
{
// get next item
hr = m_pEnum->Next(1, &pszItem, &cGot);
- m_innerList.Add(pszItem); // append item to m_innerList
::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;
+ }
+ }
+
- if (::GetTickCount() - dwTick >= COMPLETION_TIMEOUT)
+ if (::GetTickCount() - m_dwTick >= COMPLETION_TIMEOUT)
-
- 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
// if previous text was empty
BOOL bReset = FALSE, bExpand = FALSE; // flags
// if previous text was empty
m_strText = strText;
// do expand the items if the stem is changed
m_strText = strText;
// do expand the items if the stem is changed
- CStringW strStemText = GetStemText();
+ CStringW strStemText = GetStemText(strText);
if (m_strStemText.CompareNoCase(strStemText) != 0)
{
m_strStemText = strStemText;
if (m_strStemText.CompareNoCase(strStemText) != 0)
{
m_strStemText = strStemText;
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
if (bReset && m_pEnum)
{
// reset if necessary
if (bReset && m_pEnum)
{
if (bExpand || m_innerList.GetSize() == 0)
{
// reload the inner list
if (bExpand || m_innerList.GetSize() == 0)
{
// reload the inner list
+ ReLoadInnerList(strText);
-
- return m_innerList.GetSize();
-INT CAutoComplete::UpdateOuterList()
+VOID CAutoComplete::UpdateOuterList(const CStringW& strText)
- CStringW strText = GetEditText(); // get the text
+ if (strText.IsEmpty())
+ {
+ m_outerList.RemoveAll();
+ return;
+ }
- // 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];
- CStringW strBody;
- if (DropPrefix(strTarget, strBody))
+ // 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;
- }
- }
- if (::StrCmpNIW(strTarget, strText, strText.GetLength()) == 0)
- {
- m_outerList.Add(strTarget);
+
+ // check the timeout
+ if (::GetTickCount() - m_dwTick >= COMPLETION_TIMEOUT)
+ break; // too late
- // sort the list
- DoSort(m_outerList);
- // unique
- DoUniqueAndTrim(m_outerList);
+ if (::GetTickCount() - m_dwTick < COMPLETION_TIMEOUT)
+ {
+ // sort and unique
+ 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();
- if (strText.IsEmpty())
- cItems = 0;
- m_hwndList.SendMessageW(LVM_SETITEMCOUNT, cItems, 0);
-
- return cItems; // the number of items
+ m_hwndList.SendMessageW(LVM_SETITEMCOUNT, m_outerList.GetSize(), 0);
}
VOID CAutoComplete::UpdateCompletion(BOOL bAppendOK)
{
TRACE("CAutoComplete::UpdateCompletion(%p, %d)\n", this, bAppendOK);
}
VOID CAutoComplete::UpdateCompletion(BOOL bAppendOK)
{
TRACE("CAutoComplete::UpdateCompletion(%p, %d)\n", this, bAppendOK);
+ m_dwTick = GetTickCount(); // to check the timeout
+
CStringW strText = GetEditText();
if (m_strText.CompareNoCase(strText) == 0)
{
CStringW strText = GetEditText();
if (m_strText.CompareNoCase(strText) == 0)
{
- UINT cItems = UpdateInnerList();
- if (cItems == 0) // no items
+ UpdateInnerList(strText);
+ if (m_innerList.GetSize() <= 0) // no items
{
HideDropDown();
return;
{
HideDropDown();
return;
SelectItem(-1); // select none
m_bInSelectItem = FALSE;
SelectItem(-1); // select none
m_bInSelectItem = FALSE;
+ UpdateOuterList(strText);
+ if (m_outerList.GetSize() > 0)
RepositionDropDown();
else
HideDropDown();
RepositionDropDown();
else
HideDropDown();
HWND CreateDropDown();
virtual ~CAutoComplete();
HWND CreateDropDown();
virtual ~CAutoComplete();
- BOOL CanAutoSuggest();
- BOOL CanAutoAppend();
- BOOL UseTab();
- BOOL IsComboBoxDropped();
- BOOL FilterPrefixes();
- INT GetItemCount();
- CStringW GetItemText(INT iItem);
+ BOOL CanAutoSuggest() const;
+ BOOL CanAutoAppend() const;
+ BOOL UseTab() const;
+ BOOL IsComboBoxDropped() const;
+ BOOL FilterPrefixes() const;
+ INT GetItemCount() const;
+ 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 ShowDropDown();
VOID SetEditSel(INT ich0, INT ich1);
VOID ShowDropDown();
HWND m_hwndEdit; // the textbox
WNDPROC m_fnOldEditProc; // old textbox procedure
EDITWORDBREAKPROCW m_fnOldWordBreakProc;
HWND m_hwndEdit; // the textbox
WNDPROC m_fnOldEditProc; // old textbox procedure
EDITWORDBREAKPROCW m_fnOldWordBreakProc;
+ BOOL m_bPartialList; // is the list partial?
+ DWORD m_dwTick; // to check timeout
// The following variables are non-POD:
CStringW m_strText; // internal text (used in selecting item and reverting text)
CStringW m_strStemText; // dirname + '\\'
// The following variables are non-POD:
CStringW m_strText; // internal text (used in selecting item and reverting text)
CStringW m_strStemText; // dirname + '\\'
CSimpleArray<CStringW> m_outerList; // owner data for virtual listview
// protected methods
VOID UpdateDropDownState();
CSimpleArray<CStringW> m_outerList; // owner data for virtual listview
// protected methods
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();
- INT UpdateInnerList();
- INT UpdateOuterList();
+ VOID ReLoadInnerList(const CStringW& strText);
+ VOID UpdateInnerList(const CStringW& strText);
+ 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
BEGIN_MSG_MAP(CAutoComplete)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
// message map
BEGIN_MSG_MAP(CAutoComplete)
MESSAGE_HANDLER(WM_CREATE, OnCreate)