Click here to Skip to main content
15,888,590 members
Articles / Desktop Programming / MFC

VCOMBOBX in MFC - A Virtual Combo Box Control

Rate me:
Please Sign up or sign in to vote.
4.91/5 (33 votes)
4 Jan 2011CPOL8 min read 71.8K   11.3K   108  
A MFC based virtual combo box
/* 
 * Kenny Liu
 * http://www.codeproject.com/Members/yonken
 *
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
 *
 * Permission is hereby granted to use or copy this program
 * for any purpose,  provided the above notices are retained on all copies.
 * Permission to modify the code and to distribute modified code is granted,
 * provided the above notices are retained, and a notice that the code was
 * modified is included with the above copyright notice.
 */

#include "StdAfx.h"
#include "ObjInfoControl.h"

/*----------------------------------------------------------------------------*/
/* IIconMapper
/*----------------------------------------------------------------------------*/
IIconMapper::IIconMapper(int cx /*= 16*/, int cy /*= 16*/)
{
	UINT nImageListFlags = ILC_COLOR32|ILC_MASK;
	m_ImageList.Create(cx, cy, nImageListFlags, 0, 8);
}

IIconMapper::~IIconMapper()
{
	;
}

/*----------------------------------------------------------------------------*/
/* CAsciiProgressGenerator
/*----------------------------------------------------------------------------*/
CAsciiProgressGenerator::CAsciiProgressGenerator(DWORD dwMinTimeLimit /*= 600*/)
	: m_nProgress(0)
	, m_dwMinTimeLimit(dwMinTimeLimit)
{
	m_dwCurrentTime = GetTickCount();
}

CString CAsciiProgressGenerator::GetProressAscii()
{
	const TCHAR* szProgress[] = {
		_T("--"), _T("\\"), _T("|"), _T("/")
	};
	UINT nProgress = m_nProgress;
	DWORD dwCurrentTime = GetTickCount();
	if ( GetTickCount() - m_dwCurrentTime > m_dwMinTimeLimit )
	{
		++m_nProgress;
		m_dwCurrentTime = dwCurrentTime;
	}
	return szProgress[nProgress % 4];
}

/*----------------------------------------------------------------------------*/
/* CObjInfoListCtrl
/*----------------------------------------------------------------------------*/

#ifndef LVS_EX_INFOTIP
	#define LVS_EX_INFOTIP          0x00000400 // listview does InfoTips for you
#endif // LVS_EX_INFOTIP

IMPLEMENT_DYNCREATE(CObjInfoListCtrl, CObjInfoListCtrlBase)

CObjInfoListCtrl::CObjInfoListCtrl()
	: m_pObjInfoMatcher(NULL)
	, m_pIconMapper(NULL)
#ifdef UPDATE_OBJINFO_VIA_TIMER
	, m_nTimer(0)
	, m_bUpdateListByTimer(TRUE)
#endif // UPDATE_OBJINFO_VIA_TIMER
{
	
}

CObjInfoListCtrl::~CObjInfoListCtrl()
{
	;
}

BEGIN_MESSAGE_MAP(CObjInfoListCtrl, CObjInfoListCtrlBase)
	ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnGetObjListDispInfo)
	ON_NOTIFY_REFLECT(LVN_ODFINDITEM, OnOdFindObjListItem)
#ifdef UPDATE_OBJINFO_VIA_TIMER
	ON_WM_TIMER()
#endif // UPDATE_OBJINFO_VIA_TIMER
	ON_MESSAGE(WM_OBJINFO_THREADEVENT, OnObjInfoThreadEvent)
	ON_WM_KEYDOWN()
END_MESSAGE_MAP()

void CObjInfoListCtrl::SetObjInfoMatcher( CObjInfoMatcher* pMatcher, BOOL bSubscribeThreadEvent /*= TRUE*/ )
{
	m_pObjInfoMatcher = pMatcher;
	if (m_pObjInfoMatcher && bSubscribeThreadEvent)
	{
		// to bind the thread event.
		Subscribe(m_pObjInfoMatcher->GetObjInfoHolder());
	}
}

void CObjInfoListCtrl::OnGetObjListDispInfo( NMHDR* pNMHDR, LRESULT* pResult )
{
	LV_DISPINFO* pDispInfo = reinterpret_cast<LV_DISPINFO*>(pNMHDR);
	ASSERT(pDispInfo);
	
	if ( !GetObjInfoMatcher() )
	{
		ASSERT(0);	// how come this happen?
		return;
	}
	
	LVITEM& item = pDispInfo->item;
	
	if ( item.mask & LVIF_TEXT )
	{
		//_tcsncpy(item.pszText, const_cast<LPTSTR>(GetObjInfoMatcher()->GetObjInfoTextInMatches(item.iItem, item.iSubItem)), item.cchTextMax);
		item.pszText = const_cast<LPTSTR>(GetObjInfoMatcher()->GetObjInfoTextInMatches(item.iItem, item.iSubItem));
	}
	
	if ( item.mask & LVIF_IMAGE )
	{
		item.iImage = GetItemIconIndex(item.iItem);
	}
	
	*pResult = 0;
}

void CObjInfoListCtrl::OnOdFindObjListItem( NMHDR* pNMHDR, LRESULT* pResult )
{
	LPNMLVFINDITEM pFindItem = reinterpret_cast<LPNMLVFINDITEM>(pNMHDR);
	
	LVFINDINFO& lvfi = pFindItem->lvfi;
	
	ASSERT( !(lvfi.flags & LVFI_PARAM) );

	*pResult = -1;	// no match

	if ( !GetObjInfoMatcher() )
	{
		ASSERT(0);	// how come this happen?
		return;
	}
	
	if ( lvfi.flags & LVFI_STRING && GetFindObjInfoListColumnIndex() >= 0 )
	{
		const int nSearchStrLen = _tcslen(lvfi.psz);
		
		int nStartPos = pFindItem->iStart;
		if ( nStartPos >= GetItemCount() )
		{
			nStartPos = 0;
		}
		
		int nCurrentPos = nStartPos;
		do 
		{
			LPCTSTR lpcszItem = const_cast<LPCTSTR>(GetObjInfoMatcher()->GetObjInfoTextInMatches(nCurrentPos, GetFindObjInfoListColumnIndex()));
			if ( _tcsnicmp(lvfi.psz, lpcszItem, nSearchStrLen) == 0 )
			{
				*pResult = nCurrentPos;
				return;
			}
			++nCurrentPos;
			if ( nCurrentPos >= GetItemCount() )
				nCurrentPos = 0;
		} while (nCurrentPos != nStartPos);
	}
}

void CObjInfoListCtrl::OnInitList()
{
	CObjInfoListCtrlBase::OnInitList();
	ASSERT( GetStyle() & LVS_OWNERDATA );	// must be a virtual list-ctrl!

	ASSERT(GetObjInfoMatcher() != NULL);	// how come this happen?!
	ASSERT(m_arrColumnLabels.GetSize() == GetObjInfoHolder()->GetObjInfoColumnCount());

	for (int nCol = 0; nCol < m_arrColumnLabels.GetSize(); ++nCol)
	{
		InsertColumn(nCol, m_arrColumnLabels[nCol]);
	}

	if ( GetIconMapper() )
	{
		SetImageList( &GetIconMapper()->GetImageList(), LVSIL_SMALL );
	}
}

void CObjInfoListCtrl::OnDestroyList()
{
	//TRACE("======= CObjInfoListCtrl::OnDestroyList()\n");
	if (m_pObjInfoMatcher )
	{
		UnSubscribe( m_pObjInfoMatcher->GetObjInfoHolder());
	}
#ifdef UPDATE_OBJINFO_VIA_TIMER
	if (m_nTimer)
	{
		KillTimer(m_nTimer);
		m_nTimer = 0;
	}
#endif // UPDATE_OBJINFO_VIA_TIMER
	CObjInfoListCtrlBase::OnDestroyList();
}

void CObjInfoListCtrl::AutoResizeColumn()
{
	if ( GetObjInfoMatcher() )
	{
		for (UINT nCol = 0; nCol < GetObjInfoHolder()->GetObjInfoColumnCount(); ++nCol)
		{
			SetColumnWidth(nCol, LVSCW_AUTOSIZE);
		}
	}
}

BOOL CObjInfoListCtrl::LoadColumnLabels( UINT nResID )
{
	return GetStringResourceArray(nResID, m_arrColumnLabels);
}

#ifdef UPDATE_OBJINFO_VIA_TIMER
void CObjInfoListCtrl::OnTimer( UINT nIDEvent )
{
	CObjInfoListCtrlBase::OnTimer(nIDEvent);
	switch (nIDEvent)
	{
	case UPDATE_INFOLIST_TIMER_ID:
		ASSERT(GetObjInfoHolder());
		if ( !GetObjInfoHolder()->IsTaskStopped() )
		{
			//if ( GetObjInfoMatcher()->GetMatchObjInfoCount() > GetItemCount() )
				OnUpdateObjInfoList();
		}
		break;
	default:
		break;
	}
}
#endif // UPDATE_OBJINFO_VIA_TIMER

BOOL CObjInfoListCtrl::RefreshObjInfoList()
{
	if (!GetObjInfoHolder())
	{
		ASSERT(0);
		return FALSE;
	}

	SetItemCountEx(0);

#ifdef RETRIEVE_OBJINFO_IN_NEWTHREAD
	BOOL bRet = GetObjInfoMatcher()->RunInThread();
#else
	OnObjInfoThreadEvent(CThreadedObj::EVENT_ONSTART, 0);
	BOOL bRet = GetObjInfoMatcher()->RetrieveObjInfo();
	OnObjInfoThreadEvent(CThreadedObj::EVENT_ONFINISH, 0);
#endif // RETRIEVE_OBJINFO_IN_NEWTHREAD

	return bRet;
}

void CObjInfoListCtrl::OnUpdateObjInfoList(bool bAutoResizeColumns /*= true*/)
{
	LockSetRedraw(FALSE);
	GetParent()->SendMessage(WM_OBJINFOLIST_UPDATE, (WPARAM)m_hWnd);

	size_t nObjInfoCount = GetObjInfoMatcher()->GetMatchObjInfoCount();
	SetItemCountEx(nObjInfoCount, LVSICF_NOSCROLL);
	
	if ( GetObjInfoHolder() )
	{
		int nSortInfoType = GetObjInfoHolder()->GetSortColumn();
		if ( nSortInfoType >= CMultiColInfoHolder::INFOCOL_FIRST )
		{
			GetObjInfoHolder()->Sort(nSortInfoType, GetObjInfoHolder()->IsSortAscending());
		}
	}
	
	if (bAutoResizeColumns)
	{
		AutoResizeColumn();
	}
	//Invalidate();

	LockSetRedraw(TRUE);
}

BOOL CObjInfoListCtrl::OnSort( int nCol )
{
	if (GetObjInfoHolder())
	{
		LockSetRedraw(FALSE);
		SetItemState(-1, 0, LVIS_SELECTED|LVIS_FOCUSED);
		
		GetObjInfoHolder()->SwitchSort(nCol);
		
		OnUpdateObjInfoList(false);
		//Invalidate();
		
		LockSetRedraw(TRUE);
		SetItemState(0, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
		return TRUE;
	}
	return FALSE;
}

bool CObjInfoListCtrl::IsSortable() const
{
	if (GetObjInfoMatcher())
	{
		return GetObjInfoMatcher()->IsSortable();
	}
	return false;
}

void CObjInfoListCtrl::OnNotify( ISubject* pSubject, WPARAM wParam /*= 0*/, LPARAM lParam /*= 0*/ )
{
	ASSERT(pSubject);
	if ( NULL == m_hWnd )
		return;
	if ( CThreadedObj::EVENT_ONSTART == wParam )
	{
		SetItemCountEx(0);
		GetObjInfoMatcher()->Reset();
	}
	// Use PostMessage to avoid deadlock
	PostMessage(WM_OBJINFO_THREADEVENT, wParam, lParam);
}

LRESULT CObjInfoListCtrl::OnObjInfoThreadEvent( WPARAM wParam, LPARAM )
{
	ASSERT( IsWindow(m_hWnd) );
	switch (wParam)
	{
	case CThreadedObj::EVENT_ONSTART:
#ifdef UPDATE_OBJINFO_VIA_TIMER
		if (m_bUpdateListByTimer && 0 == m_nTimer)
		{
			m_nTimer = SetTimer(UPDATE_INFOLIST_TIMER_ID, UPDATE_INFOLIST_TIMER_ELAPSE, NULL);
		}
#endif // UPDATE_OBJINFO_VIA_TIMER
		break;
	case CThreadedObj::EVENT_ONFINISH:
	case CThreadedObj::EVENT_ONFORCESTOP:
		OnUpdateObjInfoList();
#ifdef UPDATE_OBJINFO_VIA_TIMER
		if (m_nTimer > 0)
		{
			KillTimer(m_nTimer);
			m_nTimer = 0;
		}
#endif // UPDATE_OBJINFO_VIA_TIMER
		break;
	default:
		return 0;	// no need to notify the parent for the other events
	}
	// Forward this message to the parent, lParam is not used!
	GetParent()->PostMessage(WM_OBJINFO_THREADEVENT, wParam, (LPARAM)m_hWnd);
	return 1;
}

int CObjInfoListCtrl::GetItemIconIndex( int nItem )
{
	if ( GetIconMapper() )
	{
		ASSERT(GetObjInfoHolder());
		ObjInfoHolderAccessLock(GetObjInfoHolder());
		return GetIconMapper()->GetIconIndex(GetObjInfoMatcher(), (WPARAM)nItem);
	}
	return -1;	// not found!
}

void CObjInfoListCtrl::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags )
{
	if ( VK_F5 == nChar )
	{
		RefreshObjInfoList();
		return;
	}
	CObjInfoListCtrlBase::OnKeyDown(nChar, nRepCnt, nFlags);
}

/*----------------------------------------------------------------------------*/
/* CFileIconMapper
/*----------------------------------------------------------------------------*/
CFileIconMapper::CFileIconMapper(int cx /*= 16*/, int cy /*= 16*/)
	: IIconMapper(cx, cy)
{
}

CFileIconMapper::~CFileIconMapper()
{
	
}

int CFileIconMapper::GetFileIconIndex( const CFileInfoEnumerator::FileInfo* pFileInfo )
{
	LockableObjAccessLock(this);
	if ( pFileInfo )
	{
		LPCTSTR lpcszFileExt = const_cast<TCHAR*>(pFileInfo->sFileInfo[CFileInfoEnumerator::FIT_FILEEXT].c_str());
		LPCTSTR lpcszFilePath = const_cast<TCHAR*>(pFileInfo->sFileInfo[CFileInfoEnumerator::FIT_FILEPATH].c_str());
		return GetFileIconIndex(lpcszFilePath, lpcszFileExt);
	}
	return -1;
}

int CFileIconMapper::GetFileIconIndex( LPCTSTR lpcszFilePath, LPCTSTR lpcszFileExt )
{
	LPCTSTR lpcszMapKey = _tcsicmp(lpcszFileExt, _T("exe")) ? lpcszFileExt : lpcszFilePath;
	
	// lazy creation
	FileIconMap::const_iterator iter = m_FileIconIndexMap.find(lpcszMapKey);
	if ( iter != m_FileIconIndexMap.end() )
	{
		return iter->second;
	}
	else
	{
		HICON hIcon = GetAssociatedFileIcon(lpcszFilePath);
		if ( hIcon )
		{
			int nImgIndex = m_ImageList.Add(hIcon);
			m_FileIconIndexMap[lpcszMapKey] = nImgIndex;
			return nImgIndex;
		}
	}
	return -1;
}

int CFileIconMapper::GetFileIconIndex( LPCTSTR lpcszFilePath )
{
	TCHAR drive[_MAX_DRIVE];
	TCHAR dir[_MAX_DIR];
	TCHAR fname[_MAX_FNAME];
	TCHAR ext[_MAX_EXT];
#ifdef MSVC_NEW_VER
	_tsplitpath_s(lpcszFilePath, drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME, ext, _MAX_EXT);
#else
	_tsplitpath(lpcszFilePath, drive, dir, fname, ext);
#endif // MSVC_NEW_VER
	return GetFileIconIndex(lpcszFilePath, ext);
}

int CFileIconMapper::GetIconIndex( CObjInfoMatcher* pMatcher, WPARAM wParam, LPARAM lParam /*= 0*/ )
{
	ASSERT(pMatcher);
	int nItem = (int)wParam;
	CFileInfoMatcher* pFileInfoMatcher = static_cast<CFileInfoMatcher*>(pMatcher);
	CFileInfoHolder* pFileInfoHolder = static_cast<CFileInfoHolder*>(pMatcher->GetObjInfoHolder());
	
	int nObjInfoIndex = pFileInfoMatcher->GetObjInfoIndexInMatches(nItem);
	
	return GetFileIconIndex( pFileInfoHolder->GetFileInfo(nObjInfoIndex) );
}

/*----------------------------------------------------------------------------*/
/* CFileInfoListCtrl
/*----------------------------------------------------------------------------*/

IMPLEMENT_DYNCREATE(CFileInfoListCtrl, CFileInfoListCtrlBase)

CFileInfoListCtrl::CFileInfoListCtrl()
{
}

CFileInfoListCtrl::~CFileInfoListCtrl()
{
	
}

BEGIN_MESSAGE_MAP(CFileInfoListCtrl, CFileInfoListCtrlBase)
	ON_MESSAGE(WM_OBJINFO_THREADEVENT, OnObjInfoThreadEvent)
END_MESSAGE_MAP()

IIconMapper* CFileInfoListCtrl::GetIconMapper() const
{
	if ( !CFileInfoListCtrlBase::GetIconMapper() )
	{
		static CFileIconMapper s_FileIconMapper;
		return &s_FileIconMapper;
	}
	return CFileInfoListCtrlBase::GetIconMapper();
}

LRESULT CFileInfoListCtrl::OnObjInfoThreadEvent( WPARAM wParam, LPARAM lParam )
{
	LRESULT result = CFileInfoListCtrlBase::OnObjInfoThreadEvent(wParam, lParam);
	if ( wParam == CMultiColInfoHolder::EVENT_SORT_END )
	{
		Invalidate();
		UpdateWindow();
	}
	return result;
}

/*----------------------------------------------------------------------------*/
/* CObjInfoListBox
/*----------------------------------------------------------------------------*/
IMPLEMENT_DYNCREATE(CObjInfoListBox, CObjInfoListBoxBase)

CObjInfoListBox::CObjInfoListBox()
	: m_pObjInfoMatcher(NULL)
	, m_pIconMapper(NULL)
#ifdef UPDATE_OBJINFO_VIA_TIMER
	, m_nTimer(0)
	, m_bUpdateListByTimer(TRUE)
#endif // UPDATE_OBJINFO_VIA_TIMER
{
	
}

CObjInfoListBox::~CObjInfoListBox()
{
	
}

BEGIN_MESSAGE_MAP(CObjInfoListBox, CObjInfoListBoxBase)
	ON_MESSAGE(WM_OBJINFO_THREADEVENT, OnObjInfoThreadEvent)
	ON_WM_KEYDOWN()
END_MESSAGE_MAP()

BOOL CObjInfoListBox::Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID )
{
	// Specifies a no-data list box. 
	// Specify this style when the count of items in the list box will exceed one thousand. 
	// A no-data list box must also have the LBS_OWNERDRAWFIXED style, but must not have the LBS_SORT or LBS_HASSTRINGS style. 
	// see http://msdn.microsoft.com/en-us/library/bb775149(VS.85).aspx
	DWORD dwMustHaveStyle		= LBS_NODATA | LBS_OWNERDRAWFIXED;
	DWORD dwMustNotHaveStyle	= LBS_HASSTRINGS | LBS_SORT;
	ASSERT( (dwStyle & dwMustHaveStyle) == dwMustHaveStyle && (dwStyle & dwMustNotHaveStyle) == 0 );

	dwStyle &= ~dwMustNotHaveStyle;
	dwStyle |= dwMustHaveStyle;
	
	BOOL bRet = CObjInfoListBoxBase::Create(dwStyle, rect, pParentWnd, nID);

	ASSERT(GetObjInfoMatcher());
	
	int nItemCount = GetObjInfoMatcher()->GetObjInfoCount();
	SetItemCount(nItemCount);
	//SetDroppedVisibleItemCount(nItemCount > MAX_VISIBLE_COMBOLISTITEM_COUNT ? MAX_VISIBLE_COMBOLISTITEM_COUNT : nItemCount);
	
	return bRet;
}

BOOL CObjInfoListBox::CreateFromCtrl( CWnd* pParent, int nID, DWORD dwStyleAdd /*= 0*/ )
{
	if (!pParent || !pParent->GetSafeHwnd())
		return FALSE;
	CWnd *pCtrl = pParent->GetDlgItem(nID);
	if (!pCtrl)
		return FALSE;
	
	CFont* pFont = pCtrl->GetFont();
	LOGFONT lf = {0};
	if (pFont)
	{
		pFont->GetLogFont(&lf);
	}
	
	UINT style = ::GetWindowLong(pCtrl->GetSafeHwnd(), GWL_STYLE);
	CRect controlRect;
	pCtrl->GetWindowRect(controlRect);
	pParent->ScreenToClient(controlRect);
	pCtrl->DestroyWindow();
	
	BOOL bRet = Create(style | dwStyleAdd, controlRect, pParent, nID);
	
	m_font.CreateFontIndirect(&lf);
	SetFont(&m_font);
	
	return bRet;
}

void CObjInfoListBox::SetObjInfoMatcher( CObjInfoMatcher* pMatcher, BOOL bSubscribeThreadEvent /*= TRUE*/ )
{
	m_pObjInfoMatcher = pMatcher;
	if (m_pObjInfoMatcher && bSubscribeThreadEvent)
	{
		// to bind the thread event.
		Subscribe( m_pObjInfoMatcher->GetObjInfoHolder() );
	}
}

CString CObjInfoListBox::GetItemText( UINT nItem )
{
	if ( GetObjInfoMatcher() )
	{
		WPARAM wParam = GetObjInfoTextInMatchWPARAM(nItem);
		LPARAM lParam = GetObjInfoTextInMatchLPARAM(nItem);
		return GetObjInfoMatcher()->GetObjInfoTextInMatches(wParam, lParam);
	}
	else if ( GetBuddyComboBox() )
	{
		CObjInfoComboBox* pObjInfoComboBox = STATIC_DOWNCAST(CObjInfoComboBox, GetBuddyComboBox());
		return pObjInfoComboBox->GetItemText(nItem);
	}
	return CObjInfoListBoxBase::GetItemText(nItem);
}

void CObjInfoListBox::OnInitListBox()
{
	CObjInfoListBoxBase::OnInitListBox();
	if ( GetIconMapper() )
	{
		SetImageList( &GetIconMapper()->GetImageList() );
	}
}

BOOL CObjInfoListBox::RefreshObjInfoList()
{
	if (!GetObjInfoHolder())
	{
		ASSERT(0);
		return FALSE;
	}

	SetItemCount(0);
	
#ifdef RETRIEVE_OBJINFO_IN_NEWTHREAD
	BOOL bRet = GetObjInfoMatcher()->RunInThread();
#else
	OnObjInfoThreadEvent(CThreadedObj::EVENT_ONSTART, 0);
	BOOL bRet = GetObjInfoMatcher()->RetrieveObjInfo();
	OnObjInfoThreadEvent(CThreadedObj::EVENT_ONFINISH, 0);
#endif // RETRIEVE_OBJINFO_IN_NEWTHREAD
	
	return bRet;
}

void CObjInfoListBox::OnUpdateObjInfoList()
{
	LockSetRedraw(FALSE);
	GetParent()->SendMessage(WM_OBJINFOLIST_UPDATE, (WPARAM)m_hWnd);
	
	SetItemCount(GetObjInfoMatcher()->GetMatchObjInfoCount());
	
	if ( GetObjInfoHolder() )
	{
		int nSortInfoType = GetObjInfoHolder()->GetSortColumn();
		if ( nSortInfoType >= CMultiColInfoHolder::INFOCOL_FIRST )
		{
			GetObjInfoHolder()->Sort(nSortInfoType, GetObjInfoHolder()->IsSortAscending());
		}
	}
	
	//Invalidate();
	
	LockSetRedraw(TRUE);
}

#ifdef UPDATE_OBJINFO_VIA_TIMER
void CObjInfoListBox::OnTimer( UINT nIDEvent )
{
	CObjInfoListBoxBase::OnTimer(nIDEvent);
	switch (nIDEvent)
	{
	case UPDATE_INFOLIST_TIMER_ID:
		ASSERT(GetObjInfoHolder());
		if ( !GetObjInfoHolder()->IsTaskStopped() )
		{
			OnUpdateObjInfoList();
		}
		break;
	default:
		break;
	}
}
#endif // UPDATE_OBJINFO_VIA_TIMER

void CObjInfoListBox::OnNotify( ISubject* pSubject, WPARAM wParam /*= 0*/, LPARAM lParam /*= 0*/ )
{
	ASSERT(pSubject);
	if ( NULL == m_hWnd )
		return;
	if ( CThreadedObj::EVENT_ONSTART == wParam )
	{
		SetItemCount(0);
		GetObjInfoMatcher()->Reset();
	}
	// Use PostMessage to avoid deadlock
	PostMessage(WM_OBJINFO_THREADEVENT, wParam, lParam);
}

LRESULT CObjInfoListBox::OnObjInfoThreadEvent( WPARAM wParam, LPARAM lParam )
{
	ASSERT( IsWindow(m_hWnd) );
	switch (wParam)
	{
	case CThreadedObj::EVENT_ONSTART:
#ifdef UPDATE_OBJINFO_VIA_TIMER
		if (m_bUpdateListByTimer && 0 == m_nTimer)
		{
			m_nTimer = SetTimer(UPDATE_INFOLIST_TIMER_ID, UPDATE_INFOLIST_TIMER_ELAPSE, NULL);
		}
#endif // UPDATE_OBJINFO_VIA_TIMER
		break;
	case CThreadedObj::EVENT_ONFINISH:
	case CThreadedObj::EVENT_ONFORCESTOP:
		OnUpdateObjInfoList();
		AutoFitContentHScroll();
#ifdef UPDATE_OBJINFO_VIA_TIMER
		if (m_nTimer > 0)
		{
			KillTimer(m_nTimer);
			m_nTimer = 0;
		}
#endif // UPDATE_OBJINFO_VIA_TIMER
		break;
	default:
		return 0;	// no need to notify the parent for the other events
	}
	// Forward this message to the parent, lParam is not used!
	GetParent()->PostMessage(WM_OBJINFO_THREADEVENT, wParam, (LPARAM)m_hWnd);
	return 1;
}

void CObjInfoListBox::AutoFitContentHScroll()
{
	if ( NULL == m_hWnd )
		return;
	UINT nItemCount = GetItemCount();
	// Find the longest string in the list box.
	CString str;
	CSize   sz;
	int     dx = 0;
	HDC hDC = ::GetDC(m_hWnd);	// CWnd::GetDC() has memory leak bug: http://support.microsoft.com/kb/819635/
	for (UINT nItem = 0; nItem < nItemCount; ++nItem)
	{
		str = GetItemText(nItem);
		::GetTextExtentPoint32(hDC, str, str.GetLength(), &sz);
		
		if (sz.cx > dx)
			dx = sz.cx;
	}
	::ReleaseDC(m_hWnd, hDC);

	if ( GetImageList() )
	{
		int cx, cy;
		ImageList_GetIconSize(GetImageList()->m_hImageList, &cx, &cy);
		dx += cx + 2;
	}
	// Set the horizontal extent so every character of all strings 
	// can be scrolled to.
	SetHorizontalExtent(dx);
}

void CObjInfoListBox::OnDestroyListBox()
{
	//TRACE("======= CObjInfoListBox::OnDestroyListBox()\n");
	if (m_pObjInfoMatcher )
	{
		UnSubscribe( m_pObjInfoMatcher->GetObjInfoHolder());
	}
#ifdef UPDATE_OBJINFO_VIA_TIMER
	if (m_nTimer)
	{
		KillTimer(m_nTimer);
		m_nTimer = 0;
	}
#endif // UPDATE_OBJINFO_VIA_TIMER
	CObjInfoListBoxBase::OnDestroyListBox();
}

int CObjInfoListBox::GetItemIconIndex( UINT nItem )
{
	if ( GetIconMapper() )
	{
		ASSERT(GetObjInfoHolder());
		ObjInfoHolderAccessLock(GetObjInfoHolder());
		return GetIconMapper()->GetIconIndex(GetObjInfoMatcher(), (WPARAM)nItem);
	}
	return CObjInfoListBoxBase::GetItemIconIndex(nItem);
}

void CObjInfoListBox::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags )
{
	if ( VK_F5 == nChar )
	{
		RefreshObjInfoList();
		return;
	}
	CObjInfoListBoxBase::OnKeyDown(nChar, nRepCnt, nFlags);
}

/*----------------------------------------------------------------------------*/
/* CFileInfoListBox
/*----------------------------------------------------------------------------*/
IMPLEMENT_DYNCREATE(CFileInfoListBox, CFileInfoListBoxBase)

CFileInfoListBox::CFileInfoListBox()
{
}

CFileInfoListBox::~CFileInfoListBox()
{
	
}

BEGIN_MESSAGE_MAP(CFileInfoListBox, CFileInfoListBoxBase)
	ON_MESSAGE(WM_OBJINFO_THREADEVENT, OnObjInfoThreadEvent)
END_MESSAGE_MAP()

IIconMapper* CFileInfoListBox::GetIconMapper() const
{
	if ( !CFileInfoListBoxBase::GetIconMapper() )
	{
		static CFileIconMapper s_FileIconMapper;
		return &s_FileIconMapper;
	}
	return CFileInfoListBoxBase::GetIconMapper();
}

LRESULT CFileInfoListBox::OnObjInfoThreadEvent( WPARAM wParam, LPARAM lParam )
{
	LRESULT result = CFileInfoListBoxBase::OnObjInfoThreadEvent(wParam, lParam);
	if ( wParam == CMultiColInfoHolder::EVENT_SORT_END )
	{
		Invalidate();
		UpdateWindow();
	}
	return result;
}

/*----------------------------------------------------------------------------*/
/* CObjInfoComboBox
/*----------------------------------------------------------------------------*/
IMPLEMENT_DYNCREATE(CObjInfoComboBox, CObjInfoComboBoxBase)

CObjInfoComboBox::CObjInfoComboBox()
	: m_pObjInfoMatcher(NULL)
	, m_pIconMapper(NULL)
	, m_bFilterListOnDropDown(TRUE)
	, m_nTimer(0)
	, m_bUpdateListByTimer(TRUE)
{
	EnableAutoComplete(TRUE);
}

CObjInfoComboBox::~CObjInfoComboBox()
{
	
}

BEGIN_MESSAGE_MAP(CObjInfoComboBox, CObjInfoComboBoxBase)
#ifdef DERIVE_FROM_VCOMBO
	ON_CONTROL_REFLECT(VCBN_DROPDOWN, OnDropDown)
	ON_CONTROL_REFLECT(VCBN_SELCHANGE, OnSelChange)
	ON_CONTROL_REFLECT(VCBN_SELENDOK, OnSelEndOK)
	//ON_MESSAGE(VCBM_SETCURSEL, OnSetCurSel)
	ON_CONTROL_REFLECT(VCBN_DISPCHANGE, OnEditDispChange)
#else
	ON_CONTROL_REFLECT(CBN_EDITUPDATE, OnEditUpdate)
	ON_CONTROL_REFLECT(CBN_DROPDOWN, OnDropDown)
	ON_CONTROL_REFLECT(CBN_SELCHANGE, OnSelChange)
	ON_CONTROL_REFLECT(CBN_SELENDOK, OnSelEndOK)
	ON_MESSAGE(CB_SETCURSEL, OnSetCurSel)
#endif // DERIVE_FROM_VCOMBO

	ON_MESSAGE(WM_OBJINFO_THREADEVENT, OnObjInfoThreadEvent)

#ifdef UPDATE_OBJINFO_VIA_TIMER
	ON_WM_TIMER()
#endif // UPDATE_OBJINFO_VIA_TIMER

	ON_WM_KEYDOWN()
END_MESSAGE_MAP()

BOOL CObjInfoComboBox::Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID )
{
	BOOL bRet = CObjInfoComboBoxBase::Create(dwStyle, rect, pParentWnd, nID);

	ASSERT(GetObjInfoMatcher());
	
	int nItemCount = GetObjInfoMatcher()->GetObjInfoCount();
	SetItemCount(nItemCount);
	//SetDroppedVisibleItemCount(nItemCount > MAX_VISIBLE_COMBOLISTITEM_COUNT ? MAX_VISIBLE_COMBOLISTITEM_COUNT : nItemCount);
	
	return bRet;
}

void CObjInfoComboBox::SetObjInfoMatcher( CObjInfoMatcher* pMatcher, BOOL bSubscribeThreadEvent /*= TRUE*/ )
{
	m_pObjInfoMatcher = pMatcher;
	if (m_pObjInfoMatcher )
	{
		if ( bSubscribeThreadEvent )
		{
			// to bind the thread event.
			Subscribe( m_pObjInfoMatcher->GetObjInfoHolder());
		}
		if ( GetObjInfoListBox() )
		{
			// We don't want the listbox to have the thread event notify
			GetObjInfoListBox()->SetObjInfoMatcher(pMatcher, FALSE);
		}
	}
}

CString CObjInfoComboBox::GetItemText( UINT nItem )
{
	if ( GetObjInfoMatcher() )
	{
		WPARAM wParam = GetObjInfoTextInMatchWPARAM(nItem);
		LPARAM lParam = GetObjInfoTextInMatchLPARAM(nItem);
		return GetObjInfoMatcher()->GetObjInfoTextInMatches(wParam, lParam);
	}
	return CObjInfoComboBoxBase::GetItemText(nItem);
}

size_t CObjInfoComboBox::GetMatchItemCount( LPCTSTR lpcszPattern, PINT pSelItem /*= NULL*/ )
{
	if ( GetObjInfoMatcher() )
	{
		return GetObjInfoMatcher()->GetMatch(lpcszPattern, (WPARAM)pSelItem);
	}
	return 0;
}

void CObjInfoComboBox::OnNotify( ISubject* pSubject, WPARAM wParam /*= 0*/, LPARAM lParam /*= 0*/ )
{
	ASSERT(pSubject);
	if ( NULL == m_hWnd )
		return;
	if ( CThreadedObj::EVENT_PRESTART == wParam )
	{
		SetItemCount(0);
		GetObjInfoMatcher()->Reset();
	}
	// Use PostMessage to avoid deadlock
	PostMessage(WM_OBJINFO_THREADEVENT, wParam, lParam);
}

LRESULT CObjInfoComboBox::OnObjInfoThreadEvent( WPARAM wParam, LPARAM lParam )
{
	ASSERT( IsWindow(m_hWnd) );
	switch (wParam)
	{
	case CThreadedObj::EVENT_ONSTART:
#ifdef UPDATE_OBJINFO_VIA_TIMER
		if (m_bUpdateListByTimer && 0 == m_nTimer)
		{
			m_nTimer = SetTimer(UPDATE_INFOLIST_TIMER_ID, UPDATE_INFOLIST_TIMER_ELAPSE, NULL);
		}
#endif // UPDATE_OBJINFO_VIA_TIMER
		break;
	case CThreadedObj::EVENT_ONFINISH:
	case CThreadedObj::EVENT_ONFORCESTOP:
		OnUpdateObjInfoList();
#ifdef UPDATE_OBJINFO_VIA_TIMER
		if (m_nTimer > 0)
		{
			KillTimer(m_nTimer);
			m_nTimer = 0;
		}
#endif // UPDATE_OBJINFO_VIA_TIMER
		break;
	default:
		return 0;	// no need to notify the parent for the other events
	}
	// Forward this message to the parent, lParam is not used!
	GetParent()->PostMessage(WM_OBJINFO_THREADEVENT, wParam, (LPARAM)m_hWnd);
	return 1;
}

BOOL CObjInfoComboBox::OnInitCombo()
{
	BOOL bRet = CObjInfoComboBoxBase::OnInitCombo();
	ASSERT(GetObjInfoListBox());	// you have to subclass the listbox!
	GetObjInfoListBox()->SetBuddyComboBox(this);

	if ( GetIconMapper() )
	{
		//SetImageList( &GetIconMapper()->GetImageList() );
		GetObjInfoListBox()->SetIconMapper( GetIconMapper() );
		GetObjInfoListBox()->SetImageList( &GetIconMapper()->GetImageList() );
	}
	return bRet;
}

void CObjInfoComboBox::OnDestroyCombo()
{
	//TRACE("======= CObjInfoComboBox::OnDestroyCombo()\n");
	if (m_pObjInfoMatcher )
	{
		UnSubscribe( m_pObjInfoMatcher->GetObjInfoHolder());
	}
#ifdef UPDATE_OBJINFO_VIA_TIMER
	if (m_nTimer)
	{
		KillTimer(m_nTimer);
		m_nTimer = 0;
	}
#endif // UPDATE_OBJINFO_VIA_TIMER
	CObjInfoComboBoxBase::OnDestroyCombo();
}

void CObjInfoComboBox::OnDropDown()
{
	if (m_bAutoComplete)
	{
		// We have to do this, otherwise the cursor will somehow disappear
		SetCursor(LoadCursor(NULL, IDC_ARROW));

		if ( m_bFilterListOnDropDown )
		{
			int nCurSel = GetCurSel();
			BOOL bHasSel = nCurSel >= 0;
			if ( !bHasSel )
				nCurSel = 0;

			int nMatchCount = 0;
			int nSel = nCurSel;
			CString strPattern;
			if ( !bHasSel )
			{
				GetWindowText(strPattern);
				nMatchCount = GetMatchItemCount(strPattern, &nSel);
				nCurSel = nSel;
			}
			if ( nMatchCount <= 0 )
			{
				nMatchCount = GetMatchItemCount(NULL, &nSel);
				SetItemCount(nMatchCount);
			}
			//nCurSel = nSel;

			SetDroppedVisibleItemCount(nMatchCount > MAX_VISIBLE_COMBOLISTITEM_COUNT ? MAX_VISIBLE_COMBOLISTITEM_COUNT : nMatchCount);
			SetTopIndex(nCurSel);

			if ( bHasSel )
			{
// 				GetComboListBox().SetCurSel(nCurSel);
// 				SetTextOfItem(nCurSel);
				SetCurSel(nCurSel);
			}
		}
	}
}

LRESULT CObjInfoComboBox::OnSetCurSel( WPARAM nIndex, LPARAM )
{
#ifdef DERIVE_FROM_VCOMBO
	LRESULT result = CObjInfoComboBoxBase::OnSetCurSel(nIndex, 0);
#else
	LRESULT result = Default();
#endif // DERIVE_FROM_VCOMBO
	//TRACE1("======= CObjInfoComboBox::OnSetCurSel(%d)\n", nIndex);
	int nStartChar, nEndChar;
	GetComboEdit().GetSel(nStartChar, nEndChar);

	if (m_bAutoComplete)
	{
#if defined(USE_HOOK_CHANGE_LISTBOX_STYLE) && !defined(DERIVE_FROM_VCOMBO)
		GetComboEdit().SetSel(nStartChar, nEndChar);
#else
		GetComboEdit().SetSel(nEndChar, nEndChar);
#endif // USE_HOOK_CHANGE_LISTBOX_STYLE
	}
	return result;
}


void CObjInfoComboBox::OnEditDispChange()
{
	CString strPattern;
	GetWindowText(strPattern);
	int nCurSel = GetCurSel();
	BOOL bHasSel = nCurSel >= 0;
	if ( !bHasSel )
		nCurSel = 0;
	int nMatchCount = GetMatchItemCount(strPattern, &nCurSel);

	SetItemCount(nMatchCount);
	SetDroppedVisibleItemCount(nMatchCount > MAX_VISIBLE_COMBOLISTITEM_COUNT ? MAX_VISIBLE_COMBOLISTITEM_COUNT : nMatchCount);

	if ( nMatchCount <= 0 )
	{
		return;
	}
	
	if (m_bAutoComplete)
	{
		if ( !GetComboListBox().IsWindowVisible() )
		{
			m_bFilterListOnDropDown = FALSE;
			ShowDropDown(TRUE);
			m_bFilterListOnDropDown = TRUE;
		}
		//GetComboListBox().SetCurSel(nCurSel);
		SetTopIndex(nCurSel);
		if ( bHasSel || !strPattern.IsEmpty() )
		{
			GetComboListBox().SetCurSel(nCurSel);
		}
	}
}

#ifndef DERIVE_FROM_VCOMBO
void CObjInfoComboBox::OnEditUpdate()
{
	OnEditDispChange();
}
#endif // DERIVE_FROM_VCOMBO

void CObjInfoComboBox::OnSelChange()
{
	//TRACE1("======= CObjInfoComboBox::OnSelChange(%d)\n", GetCurSel());
	Default();
#ifndef DERIVE_FROM_VCOMBO
	int nCurSel = GetCurSel();
	ASSERT(nCurSel >= 0);
	SetTextOfItem(nCurSel);
#endif // DERIVE_FROM_VCOMBO
}

void CObjInfoComboBox::OnSelEndOK()
{
	//TRACE("======= CObjInfoComboBox::OnSelEndOK()\n");
	Default();
#ifndef DERIVE_FROM_VCOMBO
	if (m_bAutoComplete)
	{
		int nCurSel = GetCurSel();
		if (nCurSel >= 0)
		{
			SetTextOfItem(nCurSel);
		}
	}
#endif // DERIVE_FROM_VCOMBO
}

int CObjInfoComboBox::GetItemIconIndex( UINT nItem )
{
	if ( GetIconMapper() )
	{
		ASSERT(GetObjInfoHolder());
		ObjInfoHolderAccessLock(GetObjInfoHolder());
		return GetIconMapper()->GetIconIndex(GetObjInfoMatcher(), (WPARAM)nItem);
	}
	return CObjInfoComboBoxBase::GetItemIconIndex(nItem);
}

BOOL CObjInfoComboBox::RefreshObjInfoList()
{
	if (!GetObjInfoHolder())
	{
		ASSERT(0);
		return FALSE;
	}

	SetItemCount(0);
	
#ifdef RETRIEVE_OBJINFO_IN_NEWTHREAD
	BOOL bRet = GetObjInfoMatcher()->RunInThread();
#else
	OnObjInfoThreadEvent(CThreadedObj::EVENT_ONSTART, 0);
	BOOL bRet = GetObjInfoMatcher()->RetrieveObjInfo();
	OnObjInfoThreadEvent(CThreadedObj::EVENT_ONFINISH, 0);
#endif // RETRIEVE_OBJINFO_IN_NEWTHREAD
	
	return bRet;
}

void CObjInfoComboBox::OnUpdateObjInfoList()
{
	LockSetRedraw(FALSE);
	GetParent()->SendMessage(WM_OBJINFOLIST_UPDATE, (WPARAM)m_hWnd);

	const size_t nObjInfoCount = GetObjInfoMatcher()->GetMatchObjInfoCount();
	SetItemCount(nObjInfoCount);
	if ( GetObjInfoListBox() )
	{
		GetObjInfoListBox()->AutoFitContentHScroll();
	}

	LockSetRedraw(TRUE);
}

void CObjInfoComboBox::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags )
{
	if ( VK_F5 == nChar )
	{
		RefreshObjInfoList();
		return;
	}
	CObjInfoComboBoxBase::OnKeyDown(nChar, nRepCnt, nFlags);
}

#ifdef UPDATE_OBJINFO_VIA_TIMER
void CObjInfoComboBox::OnTimer( UINT nIDEvent )
{
	CObjInfoComboBoxBase::OnTimer(nIDEvent);
	switch (nIDEvent)
	{
	case UPDATE_INFOLIST_TIMER_ID:
		ASSERT(GetObjInfoHolder());
		if ( !GetObjInfoHolder()->IsTaskStopped() )
		{
			//if ( GetObjInfoMatcher()->GetMatchObjInfoCount() > GetItemCount() )
			OnUpdateObjInfoList();
		}
		break;
	default:
		break;
	}
}
#endif // UPDATE_OBJINFO_VIA_TIMER

void CObjInfoComboBox::SetIconMapper( IIconMapper* pIconMapper )
{
	m_pIconMapper = pIconMapper;
	if ( GetObjInfoListBox() )
	{
		GetObjInfoListBox()->SetIconMapper(pIconMapper);
	}
}

/*----------------------------------------------------------------------------*/
/* CFileInfoComboBox
/*----------------------------------------------------------------------------*/
IMPLEMENT_DYNCREATE(CFileInfoComboBox, CFileInfoComboBoxBase)

CFileInfoComboBox::CFileInfoComboBox()
{
}

CFileInfoComboBox::~CFileInfoComboBox()
{
	
}

BEGIN_MESSAGE_MAP(CFileInfoComboBox, CFileInfoComboBoxBase)
	ON_MESSAGE(WM_OBJINFO_THREADEVENT, OnObjInfoThreadEvent)
END_MESSAGE_MAP()

IIconMapper* CFileInfoComboBox::GetIconMapper() const
{
	if ( !CFileInfoComboBoxBase::GetIconMapper() )
	{
		static CFileIconMapper s_FileIconMapper;
		return &s_FileIconMapper;
	}
	return CFileInfoComboBoxBase::GetIconMapper();
}

LRESULT CFileInfoComboBox::OnObjInfoThreadEvent( WPARAM wParam, LPARAM lParam )
{
	LRESULT result = CFileInfoComboBoxBase::OnObjInfoThreadEvent(wParam, lParam);
	if ( wParam == CMultiColInfoHolder::EVENT_SORT_END )
	{
		if ( GetComboListBox().IsWindowVisible() )
		{
			GetComboListBox().Invalidate();
			GetComboListBox().UpdateWindow();
		}
	}
	return result;
}

CObjInfoListBox* CFileInfoComboBox::GetObjInfoListBox()
{
	return &m_FileInfoListBox;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
China China
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions