Combobox autocomplete

If you have a cool piece of software to share, but you are not hosting it officially yet, please dump it in here. If you have code snippets that are useful, please donate!
Post Reply
arolson101
In need of some credit
In need of some credit
Posts: 2
Joined: Wed May 21, 2008 6:49 am

Combobox autocomplete

Post by arolson101 » Wed May 21, 2008 6:59 am

I wanted an autocompleting combobox but I couldn't find anything in wx that will do it. I came across an article on codeproject.com that implements IAutoComplete, and adapted it to work with a wxCombobox. This is Win32 only, of course.

Code: Select all

#include <atlbase.h>

class CCustomAutoComplete : public IEnumString
{
	// taken from http://www.codeproject.com/KB/wtl/customautocomplete_wtl.aspx

private:
	wxSortedArrayString m_strs;
	CComPtr<IAutoComplete> m_pac;

	ULONG m_nCurrentElement;
	ULONG m_nRefCount;
	BOOL m_fBound;

public:

	// Constructors/destructors
	CCustomAutoComplete()
	{
		InternalInit();
	}

	CCustomAutoComplete(const wxArrayString& p_sItemList)
	{
		InternalInit();
		SetList(p_sItemList);
	}

	~CCustomAutoComplete()
	{
		if (m_pac)
			m_pac.Release();
	}

public:

	// Implementation

	BOOL SetList(const wxArrayString& p_sItemList)
	{
		ATLASSERT(p_sItemList.size() != 0);
		Clear();
		m_strs = p_sItemList;
		return TRUE;
	}

	BOOL Bind(HWND p_hWndEdit, DWORD p_dwOptions = 0, LPCOLESTR p_lpszFormatString = NULL)
	{
		ATLASSERT(::IsWindow(p_hWndEdit));
		if ((m_fBound) || (m_pac))
			return FALSE;
		HRESULT hr = S_OK;
		hr = m_pac.CoCreateInstance(CLSID_AutoComplete);
		if (SUCCEEDED(hr))
		{
			if (p_dwOptions)
			{
				CComQIPtr<IAutoComplete2> pAC2(m_pac);
				ATLASSERT(pAC2);
				hr = pAC2->SetOptions(p_dwOptions);			// This never fails?
				pAC2.Release();
			}

			hr = m_pac->Init(p_hWndEdit, this, NULL, p_lpszFormatString);

			if (SUCCEEDED(hr))
			{
				m_fBound = TRUE;
				return TRUE;
			}
		}

		return FALSE;
	}

	VOID Unbind()
	{
		if (!m_fBound)
			return;
		ATLASSERT(m_pac);
		if (m_pac)
		{
			m_pac.Release();
			m_fBound = FALSE;
		}
	}

	BOOL Clear()
	{
		if (m_strs.size() != 0)
		{
			m_strs.Clear();

			return TRUE;
		}

		return FALSE;
	}

	BOOL Disable()
	{
		if ((!m_pac) || (!m_fBound))
			return FALSE;

		return SUCCEEDED(EnDisable(FALSE));
	}

	BOOL Enable(VOID)
	{
		if ((!m_pac) || (m_fBound))
			return FALSE;

		return SUCCEEDED(EnDisable(TRUE));
	}

public:

	//
	//	IUnknown implementation
	//
	STDMETHODIMP_(ULONG) AddRef()
	{
		return ::InterlockedIncrement(reinterpret_cast<LONG*>(&m_nRefCount));
	}

	STDMETHODIMP_(ULONG) Release()
	{
		ULONG nCount = 0;
		nCount = (ULONG) ::InterlockedDecrement(reinterpret_cast<LONG*>(&m_nRefCount));

		if (nCount == 0)
			delete this;

		return nCount;
	}

	STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject)
	{
		HRESULT hr = E_NOINTERFACE;
		if (ppvObject != NULL)
		{
			*ppvObject = NULL;

			if (IID_IUnknown == riid)
				*ppvObject = static_cast<IUnknown*>(this);

			if (IID_IEnumString == riid)
				*ppvObject = static_cast<IEnumString*>(this);

			if (*ppvObject != NULL)
			{
				hr = S_OK;
				((LPUNKNOWN)*ppvObject)->AddRef();
			}
		}
		else
		{
			hr = E_POINTER;
		}

		return hr;
	}

public:

	//
	//	IEnumString implementation
	//
	STDMETHODIMP Next(ULONG celt, LPOLESTR* rgelt, ULONG* pceltFetched)
	{
		ULONG i;
		HRESULT hr = S_FALSE;
		if (!celt)
			celt = 1;

		for (i = 0; i < celt; i++)
		{
			if (m_nCurrentElement == (ULONG)m_strs.size())
				break;

			rgelt[i] = (LPWSTR)::CoTaskMemAlloc((ULONG) sizeof(WCHAR) * (m_strs[m_nCurrentElement].length() + 1));
			lstrcpyW(rgelt[i], m_strs[m_nCurrentElement].wc_str(wxConvLibc));

			if (pceltFetched)
				*pceltFetched++;

			m_nCurrentElement++;
		}

		if (i == celt)
			hr = S_OK;

		return hr;
	}

	STDMETHODIMP Skip(ULONG celt)
	{
		m_nCurrentElement += celt;

		if (m_nCurrentElement > (ULONG)m_strs.size())
			m_nCurrentElement = 0;

		return S_OK;
	}

	STDMETHODIMP Reset(void)
	{
		m_nCurrentElement = 0;
		return S_OK;
	}

	STDMETHODIMP Clone(IEnumString** ppenum)
	{
		if (!ppenum)
			return E_POINTER;
		CCustomAutoComplete* pnew = new CCustomAutoComplete();
		pnew->AddRef();
		*ppenum = pnew;
		return S_OK;
	}

private:

	// Internal implementation
	void InternalInit()
	{
		m_nCurrentElement = 0;
		m_nRefCount = 1;
		m_fBound = FALSE;
	}

	HRESULT EnDisable(BOOL p_fEnable)
	{
		HRESULT hr = S_OK;
		ATLASSERT(m_pac);
		hr = m_pac->Enable(p_fEnable);
		if (SUCCEEDED(hr))
			m_fBound = p_fEnable;
		return hr;
	}
};
To use it, after you create your wxComboBox, call:

Code: Select all

	mAutocomplete = new CCustomAutoComplete();
	mAutocomplete->Bind((HWND)mInput->GetEditHWND(), /*ACO_UPDOWNKEYDROPSLIST | ACO_USETAB |*/ ACO_AUTOSUGGEST | ACO_AUTOAPPEND);

	wxArrayString strs;
	strs.push_back("autocomplete");
	strs.push_back("complete foo");
	strs.push_back("foo bar");
	mAutocomplete->SetList(strs);
Clean up by calling:

Code: Select all

	mAutocomplete->Unbind();
	mAutocomplete->Release();

arolson101
In need of some credit
In need of some credit
Posts: 2
Joined: Wed May 21, 2008 6:49 am

Post by arolson101 » Wed May 21, 2008 7:02 am

Oh, and this could probably be pretty easily adapted to a wxTextCtrl.

vdell
Moderator
Moderator
Posts: 536
Joined: Fri Jan 07, 2005 3:44 pm
Location: Finland
Contact:

Post by vdell » Fri May 23, 2008 5:03 pm

FYI, there is wxAutoComboBox and wxAutoTextCtrl.
Visual C++ 9.0 / Windows XP Pro SP3 / wxWidgets 2.9.0 (SVN) | Colligere

Troels
Experienced Solver
Experienced Solver
Posts: 79
Joined: Fri Jan 07, 2005 12:02 pm
Location: Denmark

Post by Troels » Fri May 23, 2008 9:59 pm


Post Reply