Click here to Skip to main content
15,896,606 members
Articles / Desktop Programming / MFC

Process, Module and Thread enumeration classes

Rate me:
Please Sign up or sign in to vote.
4.21/5 (9 votes)
15 May 2002Ms-PL3 min read 169.4K   5.4K   45  
3 simple classes for easy retrival of running processes, modules and threads
// EnhancedListCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "EnhancedListCtrl.h"
#include <afxdlgs.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define USE_FLAT

#if !defined(WM_OUTPUT_MSG)
#define WM_OUTPUT_MSG (WM_USER + 1005)
#endif

HHOOK	ghHook = NULL;
HWND	ghChild = NULL;

#define ID_LISTCTRL_SELECT_ALL	0x4141

/////////////////////////////////////////////////////////////////////////////
// CEnhancedListCtrl

CEnhancedListCtrl::CEnhancedListCtrl() : 
	m_bSilent(false), 
		m_bEnhanceHeader(false), 
		m_bComboItems(false),
		m_bAllowClick(false),
		m_bPermanentDropArrow(false),
		m_bFlat(false),
		m_bChildActive(false),
		m_nLastSortRow(-1), 
		m_bLastSortOrder(false), 
		m_ilHeader(NULL),
		m_csMessage(_T("<< No items to display >>")),
		m_pwndChild(NULL),
		m_ilRowHeight(NULL)
{
}

CEnhancedListCtrl::~CEnhancedListCtrl()
{
	if(m_ilHeader)
		delete m_ilHeader;

	if(m_ilRowHeight)
		delete m_ilRowHeight;

}


BEGIN_MESSAGE_MAP(CEnhancedListCtrl, CListCtrl)
	//{{AFX_MSG_MAP(CEnhancedListCtrl)
	ON_WM_PAINT()
	ON_WM_SETFOCUS()
	ON_WM_CREATE()
	ON_NOTIFY(HDN_CHECKCLICK, 0, OnItemdblclick)
	ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
	ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
	ON_WM_LBUTTONUP()
	ON_WM_LBUTTONDOWN()
	ON_WM_SYSKEYDOWN()
	ON_WM_KILLFOCUS()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CEnhancedListCtrl message handlers

void CEnhancedListCtrl::OnPaint() 
{
    if ( GetItemCount() == 0 && (m_bSilent == false))
    {
        CPaintDC dc( this );
        int nSavedDC = dc.SaveDC();
        
        CRect rc;
        GetWindowRect( &rc );
        ScreenToClient( &rc );
        
        CHeaderCtrl* pHC;
        pHC = GetHeaderCtrl();
        if (pHC != NULL)
        {
            CRect rcH;
            pHC->GetItemRect( 0, &rcH );
            rc.top += rcH.bottom;
        }
        rc.top += 10;

		dc.SetBkMode(OPAQUE);

        if(IsWindowEnabled())
		{
			dc.SetTextColor(::GetSysColor(COLOR_GRAYTEXT));
			dc.SetBkColor(::GetSysColor(COLOR_WINDOW));
		}
		else
		{
			dc.SetTextColor(::GetSysColor(COLOR_3DSHADOW));
			dc.SetBkColor(::GetSysColor(COLOR_BTNFACE));
		}
		
        dc.SelectStockObject( ANSI_VAR_FONT );
        dc.DrawText( m_csMessage, -1, 
                     rc, 
                     DT_CENTER | DT_WORDBREAK | DT_NOPREFIX | DT_NOCLIP );
        
        dc.RestoreDC(nSavedDC);
    }
    else
    {
        Default();
    }
}

void CEnhancedListCtrl::SetFlat(BOOL bFlat)
{
	m_bFlat = bFlat;
	if(IsWindow(m_hWnd))
	{
		Invalidate();
		UpdateWindow();
	}
}

//
// default focus on first item to gove feedback to the user.
//
void CEnhancedListCtrl::OnSetFocus(CWnd* pOldWnd) 
{
	CListCtrl::OnSetFocus(pOldWnd);
	
	if(pOldWnd != this)
	{
		if((GetSelectedCount() == 0) && (GetItemCount() > 0))
		{
			EnsureVisible(0, TRUE);
			SetSelectionMark(0);
			SetItemState(0, LVIS_FOCUSED, LVIS_FOCUSED);
		}
	}
}

//
// handle kill focus to check if we gove focus to a child (combo)
// if so, we just ignore it and let the status of the items unchanged
//

void CEnhancedListCtrl::OnKillFocus(CWnd* pNewWnd) 
{
	if(pNewWnd && pNewWnd->GetParent() != this)
		CListCtrl::OnKillFocus(pNewWnd);
}

void CEnhancedListCtrl::IndicateSortOrder(int nRow, BOOL bSortAscending)
{
	if(m_ilHeader)
	{
		HD_ITEM			Item;
		CHeaderCtrl*	HdrCtrl= GetHeaderCtrl();
		
		Item.mask = HDI_IMAGE | HDI_FORMAT;
		if(m_nLastSortRow != -1)
		{
			HdrCtrl->GetItem(m_nLastSortRow, &Item);
			Item.iImage = -1;	// remove it
			HdrCtrl->SetItem(m_nLastSortRow, &Item);
		}
		
		if( nRow != -1 )
		{
			HdrCtrl->GetItem(nRow, &Item);
			Item.iImage = (bSortAscending ? 0 : 1);
			Item.fmt |= (HDF_IMAGE | HDF_BITMAP_ON_RIGHT);
			HdrCtrl->SetItem(nRow, &Item);
		}
		
		m_nLastSortRow = nRow;
		m_bLastSortOrder = bSortAscending;
	}
}

void CEnhancedListCtrl::SetSortable()
{
	HICON h1 = AfxGetApp()->LoadIcon(100);	// add this resource to your app: icon pointing up
	HICON h2 = AfxGetApp()->LoadIcon(101);	// add this resource to your app: icon pointing down

	if(h1 && h2)
	{
		m_ilHeader = new CImageList();
		m_ilHeader->Create(16, 16, ILC_MASK, 2 , 2 );
		m_ilHeader->Add(h1);
		m_ilHeader->Add(h2);
		CHeaderCtrl	*HdrCtrl= GetHeaderCtrl();
		HdrCtrl->SetImageList( m_ilHeader );
	}
	else
	{
		OutputDebugString(_T("Warning: Up/Down icons for sort indicator not found. Check icon resource 100/101.\r\n"));
	}
}

int CEnhancedListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CListCtrl::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	// TODO: Add your specialized creation code here
	if((m_bEnhanceHeader && !IsWindow(m_cHeader.m_hWnd)))
	{
		m_cHeader.SubclassDlgItem(0, this);
	}
	return 0;
}

CHeaderCtrl* CEnhancedListCtrl::GetHeaderCtrl()	
{
	if(m_bEnhanceHeader)
		return (CHeaderCtrl*)&m_cHeader; 
	else
		return CListCtrl::GetHeaderCtrl(); 
}

static CImageList dummyImageList;
void CEnhancedListCtrl::PreSubclassWindow() 
{
	// TODO: Add your specialized code here and/or call the base class
	if((m_bEnhanceHeader && !IsWindow(m_cHeader.m_hWnd)))
	{
		m_cHeader.SubclassDlgItem(0, this);
	}
	if(m_bComboItems)
	{
		if(CListCtrl::GetImageList(LVSIL_SMALL) == NULL)
		{
			dummyImageList.DeleteImageList();
			dummyImageList.Create( 1, 16, ILC_COLOR4, 10, 10 ); 
			CListCtrl::SetImageList( &dummyImageList, LVSIL_SMALL ); 
		}
	}
	CListCtrl::PreSubclassWindow();
}

int CEnhancedListCtrl::InsertColumn (int nCol, const LVCOLUMN* pColumn)
{
	int nRetCol = CListCtrl::InsertColumn(nCol, pColumn);

	if(m_bEnhanceHeader && (-1 != nRetCol))
	{
		HDITEM hdi = {0};
		hdi.mask = HDI_FORMAT|HDI_LPARAM;
		m_cHeader.GetItem(nRetCol, &hdi);
		hdi.fmt |= HDF_OWNERDRAW;
		if(pColumn->fmt & LVCFMT_NOSORT)
			hdi.fmt |= HDF_NOSORT;
		hdi.lParam = NULL;
		m_cHeader.SetItem(nRetCol, &hdi);
	}
	return nRetCol;
}

int CEnhancedListCtrl::InsertColumn (int nCol, LPCTSTR lpszColumnHeading, int nFormat, int nWidth, int nSubItem)
{
	int nRetCol = CListCtrl::InsertColumn(nCol, lpszColumnHeading, nFormat, nWidth, nSubItem);
	
	if(m_bEnhanceHeader && (-1 != nRetCol))
	{
		HDITEM hdi = {0};
		hdi.mask = HDI_FORMAT|HDI_LPARAM;
		m_cHeader.GetItem(nRetCol, &hdi);
		hdi.fmt |= HDF_OWNERDRAW;
		if(nFormat & LVCFMT_NOSORT)
			hdi.fmt |= HDF_NOSORT;
		hdi.lParam = NULL;
		m_cHeader.SetItem(nRetCol, &hdi);
	}
	return nRetCol;
}

BOOL CEnhancedListCtrl::SetColumnCheck(int nCol, int nCheck)
{
	if(m_bEnhanceHeader)
	{
		return m_cHeader.SetCheck(nCol, nCheck);
	}
	return FALSE;
}

int CEnhancedListCtrl::GetColumnCheck(int nCol)
{
	if(m_bEnhanceHeader)
	{
		return m_cHeader.GetCheck(nCol);
	}
	return FALSE;
}

BOOL CEnhancedListCtrl::SetColumnData(int nCol, int nData)
{
	if(m_bEnhanceHeader)
	{
		return m_cHeader.SetItemData(nCol, nData);
	}
	
	HDITEM hdi = {0};
	hdi.mask = HDI_LPARAM;
	GetHeaderCtrl()->GetItem(nCol, &hdi);
	hdi.lParam = nData;
	
	return GetHeaderCtrl()->SetItem(nCol, &hdi);
}

int CEnhancedListCtrl::GetColumnData(int nCol)
{
	if(m_bEnhanceHeader)
	{
		return m_cHeader.GetItemData(nCol);
	}
	
	HDITEM hdi = {0};
	hdi.mask = HDI_LPARAM;
	GetHeaderCtrl()->GetItem(nCol, &hdi);

	return hdi.lParam;
}

void CEnhancedListCtrl::OnItemdblclick(NMHDR* pNMHDR, LRESULT* pResult) 
{
	HD_NOTIFY *phdn = (HD_NOTIFY *) pNMHDR;
	
	phdn->hdr.hwndFrom = this->m_hWnd;
	GetParent()->SendMessage(WM_NOTIFY, GetDlgCtrlID(), (LPARAM)pNMHDR);
	*pResult = 0;
}


BOOL CEnhancedListCtrl::PrepareControlSpace(const int nItem, const int nSubItem, CRect& rect, BOOL bList)
{
	int offset = 0;
	//
	// Make sure that the item is visible
	//
	if(!EnsureVisible(nItem, TRUE))
	{ 
		return FALSE;	
	}
	
	GetSubItemRect(nItem, bList ? 0 : nSubItem, LVIR_BOUNDS, rect);

	if(bList) rect.DeflateRect(rect.Height(), 0, 0, 0);

	// Now scroll if we need to expose the column
	CRect rcClient;
	GetClientRect(rcClient);
	if( offset + rect.left < 0 || offset + rect.left > rcClient.right )
	{
		CSize size(offset + rect.left,0);		
		Scroll(size);
		rect.left -= size.cx;
	}
	
	if(bList)
	{
		rect.left += offset;	
		rect.right = rect.left;

		for(int i = 0; i < nSubItem; i++)
			rect.right += GetColumnWidth(i);

		rect.right += GetColumnWidth(nSubItem) + 1;
	}
	else
	{
		rect.left += offset;	
		rect.right = rect.left + GetColumnWidth(nSubItem) + 1;
	}
	
	if(rect.right > rcClient.right) 
		rect.right = rcClient.right;
	
	return TRUE;
}


void CEnhancedListCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
{
	if(m_bAllowClick)
	{
		CListCtrl::OnLButtonUp(nFlags, point);
	}
	else
	{
		NMLISTVIEW lvn;
		lvn.hdr.code = NM_CLICK;
		lvn.hdr.hwndFrom = this->m_hWnd;
		lvn.hdr.idFrom = GetDlgCtrlID();
		
		lvn.iItem = m_nCurrentItem;
		lvn.iSubItem = m_nCurrentSubItem;
		lvn.uNewState = NULL;
		lvn.uOldState = NULL;
		lvn.uChanged = NULL;
		lvn.ptAction.x = 0;
		lvn.ptAction.y = 0;
		lvn.lParam = NULL;
		
		SendMessage(WM_NOTIFY, 0, (LPARAM)&lvn);
			
		m_bAllowClick = true;
	}
}

void CEnhancedListCtrl::OnLButtonDown(UINT nFlags, CPoint point) 
{
	m_bAllowClick = TRUE;
	{
		LVHITTESTINFO hti = {0};
		GetCursorPos(&hti.pt);
		ScreenToClient(&hti.pt);
		//
		// find where the click hit the item or subitem
		//
		
		CListCtrl::SubItemHitTest(&hti);
		
		if((hti.flags & LVHT_ONITEM) == 0)
			return;
		
		int nState = GetItemState(hti.iItem, 0x0f);
		if(nState & LVIS_SELECTED)
		{	
			
			if((hti.iSubItem == 0) && (hti.flags & LVHT_ONITEMICON) && !(hti.flags & LVHT_ONITEMLABEL))
			{
			}
			else
			{
				CRect rc;
				GetSubItemRect(hti.iItem, hti.iSubItem, LVIR_BOUNDS, rc);
				rc.left = rc.right - rc.Height();
				
				if(rc.PtInRect(hti.pt))
				{
					//
					// it hit a subitem, then do what the subitem requires
					//
					
					NMLISTVIEW lvn;
					lvn.hdr.code = LVN_QUERYCOMBO;
					lvn.hdr.hwndFrom = this->m_hWnd;
					lvn.hdr.idFrom = GetDlgCtrlID();
					
					lvn.iItem = hti.iItem;
					lvn.iSubItem = hti.iSubItem;
					lvn.uNewState = NULL;
					lvn.uOldState = NULL;
					lvn.uChanged = NULL;
					lvn.ptAction.x = 0;
					lvn.ptAction.y = 0;
					lvn.lParam = NULL;
					
					if(0 != GetParent()->SendMessage(WM_NOTIFY, 0, (LPARAM)&lvn))
					{
						m_bAllowClick = false;
						m_nCurrentItem = hti.iItem;
						m_nCurrentSubItem = hti.iSubItem;
					}
				}
			}
		}
	}
	if(m_bAllowClick)
		CListCtrl::OnLButtonDown(nFlags, point);
}

void CEnhancedListCtrl::OnClick(NMHDR* pNMHDR, LRESULT* pResult) 
{
	// TODO: Add your control notification handler code here
	*pResult = 0;

	if(!m_bComboItems)
		return;

	if( GetFocus() != this) 
		SetFocus();
	
	//
	// do we have a valid item ??
	//
	
	NMLISTVIEW* pNMListView = (NMLISTVIEW*)pNMHDR;
	if(pNMListView->iItem == -1)
		return;
	
	{
		LVHITTESTINFO hti = {0};
		GetCursorPos(&hti.pt);
		ScreenToClient(&hti.pt);
		//
		// find where the click hit the item or subitem
		//
		
		CListCtrl::SubItemHitTest(&hti);
		
		if((hti.flags & LVHT_ONITEM) == 0)
			return;
		
		if((hti.iSubItem == 0) && (hti.flags & LVHT_ONITEMICON) && !(hti.flags & LVHT_ONITEMLABEL))
		{
		}
		else
		{
			CRect rc;
			GetSubItemRect(hti.iItem, hti.iSubItem, LVIR_BOUNDS, rc);
			rc.left = rc.right - rc.Height();

			if(rc.PtInRect(hti.pt))
			{
				//
				// it hit a subitem, then do what the subitem requires
				//
				
				NMLISTVIEW lvn;
				lvn.hdr.code = LVN_QUERYCOMBO;
				lvn.hdr.hwndFrom = this->m_hWnd;
				lvn.hdr.idFrom = GetDlgCtrlID();
				
				lvn.iItem = hti.iItem;
				lvn.iSubItem = hti.iSubItem;
				lvn.uNewState = NULL;
				lvn.uOldState = NULL;
				lvn.uChanged = NULL;
				lvn.ptAction.x = 0;
				lvn.ptAction.y = 0;
				lvn.lParam = NULL;
				
				switch(GetParent()->SendMessage(WM_NOTIFY, 0, (LPARAM)&lvn))
				{
					case LV_NONE:
						// do nothing
						break;
					case LV_LISTCTRL:
						DoListCtrl(hti.iItem, hti.iSubItem);
						break;
					default:
						DoCombo(hti.iItem, hti.iSubItem);
						break;
				}
			}
		}
	}
}

CEnhancedListCtrl::OLComboCtrl* CEnhancedListCtrl::DoCombo(int nItem, int nSubItem)
{
	
	CString strFind = GetItemText(nItem, nSubItem);
	
	CRect rect;
	
	if(!PrepareControlSpace(nItem, nSubItem, rect))
		return NULL;
	
	rect.top--;
	//basic code end
	
	rect.bottom += 6 * rect.Height();//dropdown area
	
	DWORD dwStyle =  WS_CHILD | WS_VISIBLE | WS_VSCROLL | CBS_DROPDOWNLIST;
	OLComboCtrl *pCtrl = new OLComboCtrl(nItem, nSubItem);
	pCtrl->Create(dwStyle, rect, this, 1);
	pCtrl->ModifyStyleEx(0,WS_EX_STATICEDGE);//can we tell at all
	
	NMLISTVIEW lvn;
	lvn.hdr.code = LVN_COMBODROPDOWN;
	lvn.hdr.hwndFrom = this->m_hWnd;
	lvn.hdr.idFrom = GetDlgCtrlID();
	
	lvn.iItem = nItem;
	lvn.iSubItem = nSubItem;
	lvn.uNewState = NULL;
    lvn.uOldState = NULL;
    lvn.uChanged = NULL;
    lvn.ptAction.x = 0;
    lvn.ptAction.y = 0;
	lvn.lParam = (LPARAM)pCtrl->GetSafeHwnd();
				
	GetParent()->SendMessage(WM_NOTIFY, 0, (LPARAM)&lvn);

	pCtrl->ShowDropDown();
	pCtrl->SelectString(-1, strFind.GetBuffer(1));
	// The returned pointer should not be saved
	return pCtrl;
}

LRESULT CALLBACK ListCtrlMonitor(int code,       // hook code
								 WPARAM wParam,  // removal option
								 LPARAM lParam)   // message
{
	MOUSEHOOKSTRUCT* pmsg = (MOUSEHOOKSTRUCT*) lParam;

	switch(wParam)
	{
		case WM_LBUTTONDOWN:
		case WM_RBUTTONDOWN:
		case WM_MBUTTONDOWN:

			if(pmsg->hwnd != ghChild && ::GetParent(pmsg->hwnd) != ghChild)
			{
				::SendMessage(ghChild, WM_CANCELMODE, 0, 0);
			}
			break;
	}
	
	return CallNextHookEx(ghHook, code, wParam, lParam);
}


CEnhancedListCtrl::OLListCtrl* CEnhancedListCtrl::DoListCtrl(int nItem, int nSubItem)
{
	
	CString strFind = GetItemText(nItem, nSubItem);
	
	CRect rect;
	
	if(!PrepareControlSpace(nItem, nSubItem, rect, FALSE))
		return NULL;
	
	rect.OffsetRect(0, rect.Height());
	rect.bottom += 8 * rect.Height();//dropdown area
	
	CRect rcThis;
	GetClientRect(&rcThis);

	CRect calc;

	calc.UnionRect(rcThis, rect);	// should return rcThis, then the list is completly inside
	if(calc != rcThis)
	{
		Scroll(CSize(0, -(rcThis.Height() - calc.Height())));
		rect.OffsetRect(0, rcThis.Height() - calc.Height());
	}
	
	// ClientToScreen(&rect);
	DWORD dwStyle =  WS_CHILD | WS_VISIBLE | WS_BORDER | WS_HSCROLL|WS_VSCROLL | (LVS_ALIGNLEFT|LVS_NOSORTHEADER|LVS_REPORT|LVS_SHOWSELALWAYS|LVS_SINGLESEL);

	// setup the hook for mouse messages
	ghHook = ::SetWindowsHookEx(WH_MOUSE, ListCtrlMonitor, AfxGetInstanceHandle(), NULL);
	
	// create the control
	OLListCtrl *pCtrl = new OLListCtrl(nItem, nSubItem);
	pCtrl->Create(dwStyle, rect, this, 1);
	// pCtrl->CreateEx(WS_EX_TOPMOST|WS_EX_CLIENTEDGE|WS_EX_TOOLWINDOW, WC_LISTVIEW, _T("DROPLIST"), dwStyle, rect, GetDesktopWindow(), 1);
	ghChild = pCtrl->m_hWnd;
	
	pCtrl->SetExtendedStyle(LVS_EX_FULLROWSELECT|LVS_EX_TRACKSELECT);
	pCtrl->SetHoverTime(1);
	
	NMLISTVIEW lvn;
	lvn.hdr.code = LVN_COMBODROPDOWN;
	lvn.hdr.hwndFrom = this->m_hWnd;
	lvn.hdr.idFrom = GetDlgCtrlID();
	
	lvn.iItem = nItem;
	lvn.iSubItem = nSubItem;
	lvn.uNewState = NULL;
    lvn.uOldState = NULL;
    lvn.uChanged = NULL;
    lvn.ptAction.x = 0;
    lvn.ptAction.y = 0;
	lvn.lParam = (LPARAM)pCtrl->GetSafeHwnd();
				
	GetParent()->SendMessage(WM_NOTIFY, 0, (LPARAM)&lvn);
	

	{	
		CWinApp* pApp = AfxGetApp();
		MSG msg;
		
		
		while(::IsWindow(pCtrl->m_hWnd))
		{
			if(PeekMessage(&msg, NULL, NULL, NULL, FALSE))
				pApp->PumpMessage();
			/*
			if (!::GetMessage(&msg, NULL, NULL, NULL))
			{
				break;
			}
			//if(msg.hwnd != NULL)
			//	msg.hwnd = pCtrl->m_hWnd;

			if(msg.hwnd == pCtrl->m_hWnd || ::GetParent(msg.hwnd) == pCtrl->m_hWnd)
			{
				::TranslateMessage(&msg);
				::DispatchMessage(&msg);
			}
			*/
		}			
	}
	UnhookWindowsHookEx(ghHook);
	ghChild = NULL;
	ghHook = NULL;
	
	// The returned pointer should not be saved
	return pCtrl;
}

void CEnhancedListCtrl::OnCustomDraw( NMHDR* pNMHDR, LRESULT* pResult )
{
	NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
	// Take the default processing unless we set this to something else below.
    *pResult = CDRF_DODEFAULT;

	if(!m_bComboItems)
		return;

    // First thing - check the draw stage. If it's the control's prepaint
    // stage, then tell Windows we want messages for every item.
	
    if(CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage)
	{
        *pResult = CDRF_NOTIFYITEMDRAW|CDRF_NOTIFYSUBITEMDRAW;
	}
    else if((CDDS_ITEMPREPAINT/*|CDDS_SUBITEM*/) == pLVCD->nmcd.dwDrawStage)
	{
        // This is the prepaint stage for an item. Here's where we set the
        // item's text color. Our return value will tell Windows to draw the
        // item itself.
		
		//int nState = GetItemState(pLVCD->nmcd.dwItemSpec, 0x0f);
		
        // Tell Windows to paint the control itself.
        *pResult = /*CDRF_NEWFONT|CDRF_DODEFAULT|*/CDRF_NOTIFYPOSTPAINT|CDRF_NOTIFYSUBITEMDRAW;
        //*pResult = CDRF_NEWFONT|CDRF_DODEFAULT|CDRF_NOTIFYPOSTPAINT;
	}
	else if((CDDS_POSTPAINT|CDDS_SUBITEM) & pLVCD->nmcd.dwDrawStage)
	{
		*pResult = CDRF_DODEFAULT;
		// if(pLVCD->iSubItem != 0)
		{
			//
			// get the rect of the subitem
			//
			
			int nState = GetItemState(pLVCD->nmcd.dwItemSpec, 0x0f);
			if(m_bPermanentDropArrow || (nState & LVIS_SELECTED))
			{
				CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
				CRect rc;
				GetSubItemRect(pLVCD->nmcd.dwItemSpec, pLVCD->iSubItem, LVIR_BOUNDS, rc);
				
				
				NMLISTVIEW lvn;
				lvn.hdr.code = LVN_QUERYCOMBO;
				lvn.hdr.hwndFrom = this->m_hWnd;
				lvn.hdr.idFrom = GetDlgCtrlID();
				
				lvn.iItem = pLVCD->nmcd.dwItemSpec;
				lvn.iSubItem = pLVCD->iSubItem;
				lvn.uNewState = NULL;
				lvn.uOldState = NULL;
				lvn.uChanged = NULL;
				lvn.ptAction.x = 0;
				lvn.ptAction.y = 0;
				lvn.lParam = NULL;
				
				if(0 != GetParent()->SendMessage(WM_NOTIFY, 0, (LPARAM)&lvn))
				{
					rc.left = rc.right - rc.Height();
					pDC->DrawFrameControl(&rc, DFC_SCROLL, m_bFlat ? DFCS_SCROLLCOMBOBOX|DFCS_FLAT : DFCS_SCROLLCOMBOBOX);
					pLVCD->nmcd.rc = rc;
					*pResult = CDRF_NOTIFYPOSTPAINT; //CDRF_SKIPDEFAULT;
				}
			}
		}
	}
}
/////////////////////////////////////////////////////////////////////////////
// OLComboCtrl

CEnhancedListCtrl::OLComboCtrl::OLComboCtrl( int nItem, int nSubItem)
{
	m_nItem		= nItem;
	m_nSubItem  = nSubItem;
	m_bVK_ESCAPE = FALSE;
}

CEnhancedListCtrl::OLComboCtrl::~OLComboCtrl()
{
}

typedef CEnhancedListCtrl::OLComboCtrl	CEnhancedListCtrlComboItem;

BEGIN_MESSAGE_MAP(CEnhancedListCtrlComboItem, CComboBox)
	//{{AFX_MSG_MAP(OLComboCtrl)
	ON_WM_NCDESTROY()
	ON_WM_CHAR()
	ON_WM_KILLFOCUS()
	ON_CONTROL_REFLECT(CBN_CLOSEUP, OnCloseup)
	ON_WM_CREATE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// OLComboCtrl message handlers

BOOL CEnhancedListCtrl::OLComboCtrl::PreTranslateMessage(MSG* pMsg) 
{
	// TODO: Add your specialized code here and/or call the base class
	if( pMsg->message == WM_KEYDOWN )	
	{		
		if(pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)	
		{
			::TranslateMessage(pMsg);
			::DispatchMessage(pMsg);			
			return 1;
		}	
	}	
	
	return CComboBox::PreTranslateMessage(pMsg);
}

void CEnhancedListCtrl::OLComboCtrl::OnNcDestroy() 
{
	CComboBox::OnNcDestroy();
	delete this;	
}

void CEnhancedListCtrl::OLComboCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// TODO: Add your message handler code here and/or call default
	if(nChar == VK_ESCAPE || nChar == VK_RETURN)	
	{		
		if( nChar == VK_ESCAPE)
			m_bVK_ESCAPE = 1;
		GetParent()->SetFocus();		
		return;	
	}	
	
	CComboBox::OnChar(nChar, nRepCnt, nFlags);
}

void CEnhancedListCtrl::OLComboCtrl::OnKillFocus(CWnd* pNewWnd) 
{
	int nIndex = GetCurSel();

	CComboBox::OnKillFocus(pNewWnd);

	CString str;	
	GetWindowText(str);

	// Send Notification to parent of ListView ctrl	
	LV_DISPINFO lvDispinfo;
	lvDispinfo.hdr.hwndFrom = GetParent()->m_hWnd;
	lvDispinfo.hdr.idFrom = GetDlgCtrlID(); //that's us
	lvDispinfo.hdr.code = LVN_ENDLABELEDIT;
	lvDispinfo.item.mask = LVIF_TEXT | LVIF_PARAM;	
	lvDispinfo.item.iItem = m_nItem;
	lvDispinfo.item.iSubItem = m_nSubItem;
	lvDispinfo.item.pszText = m_bVK_ESCAPE ? NULL : LPTSTR((LPCTSTR)str);
	lvDispinfo.item.cchTextMax = str.GetLength();
	lvDispinfo.item.lParam = GetItemData(GetCurSel());
	if(nIndex!=CB_ERR)
		GetParent()->GetParent()->SendMessage(WM_NOTIFY, GetParent()->GetDlgCtrlID(), (LPARAM)&lvDispinfo);
	PostMessage(WM_CLOSE);
}

void CEnhancedListCtrl::OLComboCtrl::OnCloseup() 
{
	GetParent()->SetFocus();	
}

int CEnhancedListCtrl::OLComboCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CComboBox::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	CFont* font = GetParent()->GetFont();	
	SetFont(font);
	SetFocus();	
	
	return 0;
}

/////////////////////////////////////////////////////////////////////////////
// OLComboCtrl

CEnhancedListCtrl::OLListCtrl::OLListCtrl( int nItem, int nSubItem) : m_pParentWnd(NULL)
{
	m_nItem		= nItem;
	m_nSubItem  = nSubItem;
	m_bVK_ESCAPE = FALSE;
}

CEnhancedListCtrl::OLListCtrl::~OLListCtrl()
{
}

typedef CEnhancedListCtrl::OLListCtrl	CEnhancedListCtrlListCtrlItem;

#pragma warning(disable:4310)	// cast truncates constant value 

BEGIN_MESSAGE_MAP(CEnhancedListCtrlListCtrlItem, CListCtrl)
	//{{AFX_MSG_MAP(OLListCtrl)
	ON_WM_NCDESTROY()
	ON_WM_NCPAINT()
	ON_WM_NCHITTEST()
	ON_WM_CHAR()
	ON_WM_KILLFOCUS()
	ON_WM_SETFOCUS()
	// ON_CONTROL_REFLECT(LVN_ITEMACTIVATE, OnCloseup)
	// ON_CONTROL_REFLECT(LVN_ITEMCHANGED, OnItemChange)
	ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
	ON_WM_LBUTTONDOWN()
	ON_WM_RBUTTONDOWN()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_RBUTTONDBLCLK()
	ON_WM_NCLBUTTONDOWN()
	ON_WM_CREATE()
	ON_WM_CANCELMODE()
	ON_WM_PAINT()
	ON_WM_SIZE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// OLListCtrl message handlers

BOOL CEnhancedListCtrl::OLListCtrl::PreTranslateMessage(MSG* pMsg) 
{
	// TODO: Add your specialized code here and/or call the base class
	if( pMsg->message == WM_KEYDOWN )	
	{		
		if(pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)	
		{
			::TranslateMessage(pMsg);
			::DispatchMessage(pMsg);			
			return 1;
		}	
	}	
	
	return CListCtrl::PreTranslateMessage(pMsg);
}

void CEnhancedListCtrl::OLListCtrl::OnNcDestroy() 
{
	CListCtrl::OnNcDestroy();
	delete this;	
}

void CEnhancedListCtrl::OLListCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// TODO: Add your message handler code here and/or call default
	if(nChar == VK_ESCAPE || nChar == VK_RETURN)	
	{		
		if( nChar == VK_ESCAPE)
			m_bVK_ESCAPE = 1;
		GetParent()->SetFocus();		
		return;	
	}	
	
	CListCtrl::OnChar(nChar, nRepCnt, nFlags);
}

void CEnhancedListCtrl::OLListCtrl::OnKillFocus(CWnd* pNewWnd) 
{
	POSITION pos = CListCtrl::GetFirstSelectedItemPosition();
	int item = -1;
	if(pos)
		item = CListCtrl::GetNextSelectedItem(pos);

	CListCtrl::OnKillFocus(pNewWnd);
	if(pNewWnd && pNewWnd->GetParent()->m_hWnd == this->m_hWnd)	// its the header control
	{
		return;
	}
	// ReleaseCapture();
	
	if(!m_bVK_ESCAPE)
	{
		CString str = GetItemText(item, 0);
		
		// Send Notification to parent of ListView ctrl	
		LV_DISPINFO lvDispinfo;
		lvDispinfo.hdr.hwndFrom = GetParent()->m_hWnd;
		lvDispinfo.hdr.idFrom = GetDlgCtrlID(); //that's us
		lvDispinfo.hdr.code = LVN_ENDLABELEDIT;
		lvDispinfo.item.mask = LVIF_TEXT | LVIF_PARAM;	
		lvDispinfo.item.iItem = m_nItem;
		lvDispinfo.item.iSubItem = m_nSubItem;
		lvDispinfo.item.pszText = m_bVK_ESCAPE ? NULL : LPTSTR((LPCTSTR)str);
		lvDispinfo.item.cchTextMax = str.GetLength();
		if(item != -1)
		{
			lvDispinfo.item.lParam = CListCtrl::GetItemData(item);
			GetParent()->GetParent()->SendMessage(WM_NOTIFY, GetParent()->GetDlgCtrlID(), (LPARAM)&lvDispinfo);
		}
	}
	PostMessage(WM_CLOSE);
}

void CEnhancedListCtrl::OLListCtrl::OnCloseup() 
{
	GetParent()->SetFocus();	
}

void CEnhancedListCtrl::OLListCtrl::OnItemChange(NMHDR* pNMHDR, LRESULT* pResult)
{
	NMLISTVIEW *phdn = (NMLISTVIEW *) pNMHDR;

	long l = GetItemData(phdn->iItem);
	if(!(phdn->uOldState & LVIS_SELECTED) && (phdn->uNewState & LVIS_SELECTED))
	{
		int i = phdn->iItem;

		while(GetItemData(i) == l)
		{
			SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
			i--;
		}
		i = phdn->iItem;
		while(GetItemData(i) == l)
		{
			SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
			i++;
		}
	}
	else if((phdn->uOldState & LVIS_SELECTED) && !(phdn->uNewState & LVIS_SELECTED))
	{
		int i = phdn->iItem;
		
		while(GetItemData(i) == l)
		{
			SetItemState(i, 0, LVIS_SELECTED);
			i--;
		}
		i = phdn->iItem;
		while(GetItemData(i) == l)
		{
			SetItemState(i, 0, LVIS_SELECTED);
			i++;
		}
	}

	*pResult = 0;
}

void CEnhancedListCtrl::OLListCtrl::OnLButtonDown(UINT nFlags, CPoint point) 
{
	CListCtrl::OnLButtonDown(nFlags, point);
	if(HitTest(point) == -1)
		m_bVK_ESCAPE = TRUE;
	
	GetParent()->SetFocus();	
}

void CEnhancedListCtrl::OLListCtrl::OnCancelMode() 
{
	// ReleaseCapture();
	m_bVK_ESCAPE = TRUE;
	PostMessage(WM_CLOSE);
}

void CEnhancedListCtrl::OLListCtrl::OnNcLButtonDown(UINT nHitTest, CPoint point) 
{
	if(nHitTest == HTNOWHERE)
	{
		// ReleaseCapture();
		m_bVK_ESCAPE = TRUE;
		PostMessage(WM_CLOSE);
	}
	CListCtrl::OnNcLButtonDown(nHitTest, point);
}

int CEnhancedListCtrl::OLListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CListCtrl::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	CFont* font = GetParent()->GetFont();	
	SetFont(font);
	SetFocus();
	// SetCapture();
	
	return 0;
}

void CEnhancedListCtrl::OLListCtrl::OnSetFocus(CWnd* wnd) 
{
	CListCtrl::OnSetFocus(wnd);
}

void CEnhancedListCtrl::OLListCtrl::OnPaint() 
{
	CListCtrl::OnPaint();
}

void CEnhancedListCtrl::OLListCtrl::OnNcPaint() 
{
	CListCtrl::OnNcPaint();
	/*
	CWindowDC dc(this);
	CRect rc;
	GetWindowRect(&rc);
	ScreenToClient(&rc);

	rc.top = rc.bottom - GetSystemMetrics(SM_CYHSCROLL);
	rc.left = rc.right - GetSystemMetrics(SM_CXVSCROLL);

	InvalidateRect(rc);
	DrawFrameControl(dc, &rc, DFC_SCROLL,DFCS_SCROLLSIZEGRIP);
	*/
}

UINT CEnhancedListCtrl::OLListCtrl::OnNcHitTest(CPoint point)
{
	CRect rc;
	GetWindowRect(&rc);

	if(rc.PtInRect(point))
	{
		rc.top = rc.bottom - GetSystemMetrics(SM_CYHSCROLL);
		rc.left = rc.right - GetSystemMetrics(SM_CXVSCROLL);
		if(rc.PtInRect(point))
		{
			return HTBOTTOMRIGHT;
		}
		return CListCtrl::OnNcHitTest(point);
	}
	
	return HTNOWHERE;
}

void CEnhancedListCtrl::OLListCtrl::OnSize(UINT nType, int cx, int cy)
{
	CListCtrl::OnSize(nType, cx, cy);
}

void CEnhancedListCtrl::OLListCtrl::OnCustomDraw( NMHDR* pNMHDR, LRESULT* pResult )
{
	NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>( pNMHDR );
	// Take the default processing unless we set this to something else below.
    *pResult = CDRF_DODEFAULT;

    // First thing - check the draw stage. If it's the control's prepaint
    // stage, then tell Windows we want messages for every item.
	
    if(CDDS_PREPAINT == pLVCD->nmcd.dwDrawStage)
	{
        *pResult = CDRF_NOTIFYITEMDRAW|CDRF_NOTIFYSUBITEMDRAW;
	}
    else if((CDDS_ITEMPREPAINT/*|CDDS_SUBITEM*/) == pLVCD->nmcd.dwDrawStage)
	{
        // This is the prepaint stage for an item. Here's where we set the
        // item's text color. Our return value will tell Windows to draw the
        // item itself.
		
		//int nState = GetItemState(pLVCD->nmcd.dwItemSpec, 0x0f);
		
        // Tell Windows to paint the control itself.
        *pResult = /*CDRF_NEWFONT|CDRF_DODEFAULT|*/CDRF_NOTIFYPOSTPAINT|CDRF_NOTIFYSUBITEMDRAW;
        //*pResult = CDRF_NEWFONT|CDRF_DODEFAULT|CDRF_NOTIFYPOSTPAINT;
	}
	else if((CDDS_POSTPAINT|CDDS_SUBITEM) & pLVCD->nmcd.dwDrawStage)
	{
		*pResult = CDRF_DODEFAULT;
		// if is separation element
		if(m_separator.find(pLVCD->nmcd.dwItemSpec) != m_separator.end())
		{
			CDC* pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
			CRect rc;
			GetItemRect(pLVCD->nmcd.dwItemSpec, rc, LVIR_BOUNDS);

			CPen p;
			p.CreatePen(PS_DOT, 1, GetSysColor(COLOR_3DSHADOW));
			CPen* pold = pDC->SelectObject(&p);
			pDC->MoveTo(rc.left, rc.top);
			pDC->LineTo(rc.right, rc.top);
			pDC->SelectObject(pold);
		}
	}
}

/////////////////////////////////////////////////////////////////////////////
// CEnhancedHeaderCtrl

CEnhancedHeaderCtrl::CEnhancedHeaderCtrl() : m_nLastClickCol(-1)
{
	m_cxCheck = GetSystemMetrics(SM_CXMENUCHECK);	// width of the checkbox
	m_cxSpacing = 2;								// spacing between checkbox/text/bitmap
	m_cxOffsetLeft = 6;								// offset of the checkbox from left
}

CEnhancedHeaderCtrl::~CEnhancedHeaderCtrl()
{
}


BEGIN_MESSAGE_MAP(CEnhancedHeaderCtrl, CHeaderCtrl)
//{{AFX_MSG_MAP(CEnhancedHeaderCtrl)
	ON_WM_LBUTTONUP()
	ON_WM_LBUTTONDOWN()
	ON_WM_NCLBUTTONUP()
	ON_WM_CANCELMODE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CEnhancedHeaderCtrl message handlers

void CEnhancedHeaderCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	// This code only works with header controls.
	ASSERT(lpDrawItemStruct->CtlType == ODT_HEADER);
	
	HDITEM hdi = {0};
	TCHAR  lpBuffer[256];
	
	hdi.mask = HDI_TEXT|HDI_LPARAM|HDI_FORMAT|HDI_IMAGE;
	hdi.pszText = lpBuffer;
	hdi.cchTextMax = 256;
	GetItem( lpDrawItemStruct->itemID, &hdi);

	// Draw the button frame.
	if(hdi.fmt & HDF_NOSORT)
		DrawFrameControl(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH|DFCS_FLAT);
	else
		DrawFrameControl(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, DFC_BUTTON, DFCS_BUTTONPUSH);
	
	lpDrawItemStruct->rcItem.left += m_cxOffsetLeft;
	
	if(LOWORD(hdi.lParam))
	{
		// Draw the button frame.
		int button = 0;
		
		if(LOWORD(hdi.lParam) == 1)
			button |= DFCS_BUTTONCHECK;
		else if(LOWORD(hdi.lParam) == 2)
			button |= DFCS_BUTTONCHECK|DFCS_CHECKED;
		else if(LOWORD(hdi.lParam) == 3)
			button |= DFCS_BUTTON3STATE;

		if(!IsWindowEnabled() || !GetParent()->IsWindowEnabled())
			button |= DFCS_INACTIVE;

		if(m_nLastClickCol == lpDrawItemStruct->itemID )
			button |= DFCS_PUSHED;

		CRect rc = lpDrawItemStruct->rcItem;
		rc.right = rc.left + m_cxCheck;
		DrawFrameControl(lpDrawItemStruct->hDC, &rc, DFC_BUTTON, button);
		lpDrawItemStruct->rcItem.left += rc.Width() + m_cxSpacing;

		lpDrawItemStruct->rcItem.left  += m_cxOffsetLeft;
		lpDrawItemStruct->rcItem.right -= 2*m_cxSpacing;
	}
	
	
	int fmt = DT_SINGLELINE|DT_VCENTER|DT_END_ELLIPSIS;
	switch(hdi.fmt)
	{
		case HDF_LEFT:		fmt |= DT_LEFT; break;
		case HDF_CENTER:	fmt |= DT_CENTER; break;
		case HDF_RIGHT:		fmt |= DT_RIGHT; break;
	}
	
	if((hdi.fmt & (HDF_IMAGE|HDF_BITMAP|HDF_BITMAP_ON_RIGHT)) && (hdi.iImage != -1))
	{
		CRect rc = lpDrawItemStruct->rcItem;
		::DrawText(lpDrawItemStruct->hDC, lpBuffer, _tcslen(lpBuffer), rc, DT_CALCRECT|fmt);
		if(lpDrawItemStruct->rcItem.right - rc.right > 16)
		{
			CImageList* pImageList = GetImageList();
			
			CPoint pt;
			if(hdi.fmt & HDF_BITMAP_ON_RIGHT)
			{
				// pt.x = lpDrawItemStruct->rcItem.right - 16 - m_cxSpacing;		// 16=image 4=spacing
				pt.x = rc.right + m_cxSpacing;		// 16=image 4=spacing
			}
			else
			{
				pt.x = lpDrawItemStruct->rcItem.left + m_cxOffsetLeft;		// 16=image 4=spacing
				lpDrawItemStruct->rcItem.left += m_cxSpacing;
			}
			
			pt.y = lpDrawItemStruct->rcItem.top;
			
			pImageList->Draw(CDC::FromHandle(lpDrawItemStruct->hDC), hdi.iImage, pt ,ILD_NORMAL);
		}
	}
	::DrawText(lpDrawItemStruct->hDC, lpBuffer, _tcslen(lpBuffer), &lpDrawItemStruct->rcItem, fmt);
}

int CEnhancedHeaderCtrl::InsertItem( int nPos, HDITEM* phdi )
{
	ASSERT(phdi);
	phdi->fmt |= HDF_OWNERDRAW;
	phdi->lParam = 0;
	return CHeaderCtrl::InsertItem(nPos, phdi);
}

BOOL CEnhancedHeaderCtrl::SubclassDlgItem(UINT nID, CWnd* pParent)
{
	return CWnd::SubclassDlgItem(nID, pParent);
}

BOOL CEnhancedHeaderCtrl::SetCheck(int nCol, int nCheck)
{
	HDITEM hdi = {0};
	hdi.mask = HDI_LPARAM;
	CHeaderCtrl::GetItem(nCol, &hdi);
	hdi.lParam = MAKELONG(1+nCheck, HIWORD(hdi.lParam));
	return CHeaderCtrl::SetItem(nCol, &hdi);
}

int CEnhancedHeaderCtrl::GetCheck(int nCol)
{
	HDITEM hdi = {0};
	hdi.mask = HDI_LPARAM;
	CHeaderCtrl::GetItem(nCol, &hdi);
	return LOWORD(hdi.lParam) - 1;	
}

BOOL CEnhancedHeaderCtrl::SetItemData(int nCol, WORD wData)
{
	HDITEM hdi = {0};
	hdi.mask = HDI_LPARAM;
	CHeaderCtrl::GetItem(nCol, &hdi);
	hdi.lParam = MAKELONG(LOWORD(hdi.lParam), wData);
	return CHeaderCtrl::SetItem(nCol, &hdi);
}

WORD CEnhancedHeaderCtrl::GetItemData(int nCol)
{
	HDITEM hdi = {0};
	hdi.mask = HDI_LPARAM;
	CHeaderCtrl::GetItem(nCol, &hdi);
	return HIWORD(hdi.lParam);
}

BOOL CEnhancedHeaderCtrl::PtInCheckbox(int nCol, CPoint& pt)
{
	CRect rc;
	CHeaderCtrl::GetItemRect(nCol, &rc);
	
	if(rc.Width() > m_cxCheck + m_cxOffsetLeft)
	{
		rc.left += m_cxOffsetLeft;
		rc.right = rc.left + m_cxCheck;
		
		if(PtInRect(&rc, pt))
		{
			return TRUE;
		}
	}
	return FALSE;
}

BOOL CEnhancedHeaderCtrl::PtInItem(int nCol, CPoint& pt)
{
	CRect rc;
	CHeaderCtrl::GetItemRect(nCol, &rc);
	
	return PtInRect(&rc, pt);
}

void CEnhancedHeaderCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
{
	int nLastCol = m_nLastClickCol;
	if(m_nLastClickCol != -1)
	{
		ReleaseCapture();
		InvalidateColumn(m_nLastClickCol);
		m_nLastClickCol = -1;
	}
	
	CRect rc;
	int nCount = CHeaderCtrl::GetItemCount();
	for(int i = 0; i < nCount; ++i)
	{
		HDITEM hdi = {0};
		
		hdi.mask = HDI_LPARAM|HDI_FORMAT;
		CHeaderCtrl::GetItem(i, &hdi);

		if(PtInCheckbox(i, point) && (i == nLastCol))
		{
			if(LOWORD(hdi.lParam))
			{
				if(LOWORD(hdi.lParam) == 1) 
					hdi.lParam = MAKELONG(2, HIWORD(hdi.lParam));
				else 
					hdi.lParam = MAKELONG(1, HIWORD(hdi.lParam));

				NMHEADER hdn;
				hdn.hdr.code = HDN_CHECKCLICK;
				hdn.hdr.hwndFrom = this->m_hWnd;
				hdn.hdr.idFrom = GetDlgCtrlID();
				
				hdn.iItem = nLastCol;
				hdn.iButton = 0;
				hdn.pitem = &hdi;
				GetParent()->SendMessage(WM_NOTIFY, 0, (LPARAM)&hdn);
				
				CHeaderCtrl::SetItem(i, &hdi);
				return;
			}
		}
	}

	CHeaderCtrl::OnLButtonUp(nFlags, point);
}

void CEnhancedHeaderCtrl::OnLButtonDown(UINT nFlags, CPoint point) 
{
	int nCount = CHeaderCtrl::GetItemCount();
	for(int i = 0; i < nCount; ++i)
	{
		HDITEM hdi = {0};
		
		hdi.mask = HDI_LPARAM;
		CHeaderCtrl::GetItem(i, &hdi);
		
		if(PtInCheckbox(i, point))
		{
			if(LOWORD(hdi.lParam))
			{
				m_nLastClickCol = i;
				InvalidateColumn(i);
				::SetCapture(m_hWnd);
				return;
			}
		}
	}
	CHeaderCtrl::OnLButtonDown(nFlags, point);
}

void CEnhancedHeaderCtrl::OnNcLButtonUp(UINT nHitTest, CPoint point) 
{
	if(m_nLastClickCol != -1)
	{
		ReleaseCapture();
	}
	CHeaderCtrl::OnNcLButtonUp(nHitTest, point);
}

void CEnhancedHeaderCtrl::OnCancelMode() 
{
	CHeaderCtrl::OnCancelMode();
	if(m_nLastClickCol != -1)
	{
		ReleaseCapture();
	}
}

void CEnhancedHeaderCtrl::InvalidateColumn(int nCol)
{
	CRect rc;
	CHeaderCtrl::GetItemRect(nCol, &rc);
	CHeaderCtrl::InvalidateRect(&rc);
}


BOOL CEnhancedListCtrl::PreTranslateMessage(MSG* pMsg) 
{
	if( pMsg->message == WM_SYSKEYDOWN )	
	{		
		if(pMsg->wParam == VK_DOWN || pMsg->wParam == VK_INSERT)	
		{
			::TranslateMessage(pMsg);
			::DispatchMessage(pMsg);			
			return 1;
		}	
	}
	else if( pMsg->message == WM_KEYDOWN )
	{
		if((pMsg->wParam == 'A') && (GetKeyState(VK_CONTROL) & ~1))
		{
			OnSelectAll();
		}
	}

	
	return CListCtrl::PreTranslateMessage(pMsg);
}


void CEnhancedListCtrl::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// TODO: Add your message handler code here and/or call default
	if(nChar == VK_DOWN)
	{
		int nItem = GetItemFocus();

		if(GetItemState(nItem, 0xf) & LVIS_FOCUSED)
		{
			NMLISTVIEW lvn;
			lvn.hdr.code = LVN_QUERYCOMBO;
			lvn.hdr.hwndFrom = this->m_hWnd;
			lvn.hdr.idFrom = GetDlgCtrlID();
			
			lvn.iItem = nItem;
			lvn.uNewState = NULL;
			lvn.uOldState = NULL;
			lvn.uChanged = NULL;
			lvn.ptAction.x = 0;
			lvn.ptAction.y = 0;
			lvn.lParam = NULL;
			
			int nCount = GetHeaderCtrl()->GetItemCount();
			for(int i = 0; i < nCount; ++i)
			{
				lvn.iSubItem = i;
				switch(GetParent()->SendMessage(WM_NOTIFY, 0, (LPARAM)&lvn))
				{
				case LV_NONE:
					// do nothing
					break;
				case LV_LISTCTRL:
					DoListCtrl(nItem, i);
					return;
				default:
					DoCombo(nItem, i);
					return;
				}
			}
		}
	}
	else if(nChar == VK_INSERT)
	{
		CString cs;
		cs.Format(_T("List contains %i items (%i selected)"), GetItemCount(), GetSelectedCount());
		AfxGetMainWnd()->SendMessage(WM_OUTPUT_MSG, (WPARAM)-1, (LPARAM)(LPCTSTR)cs);
	}
	else
	{
		CListCtrl::OnSysKeyDown(nChar, nRepCnt, nFlags);
	}
}

void CEnhancedListCtrl::OnSelectAll()
{
	int nCount = CListCtrl::GetItemCount();
	for(int i = 0; i < nCount; i++)
	{
		CListCtrl::SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
	}
}


BOOL CEnhancedListCtrl::ExportCSV(LPCTSTR pstrFilename, BOOL bSelectedOnly)
{
	CString csFilename;

	ASSERT(pstrFilename != NULL);
	
	//
	// construct a filename like SCAN_YYYYMMDD.CSV
	//
	
	csFilename = pstrFilename;
	csFilename += _T("_");
	csFilename += CTime::GetCurrentTime().FormatGmt(_T("%Y%m%d"));
	csFilename += _T(".CSV");

	{
		static TCHAR szFilter[] = _T("Comma Separated Files (*.csv)|*.csv|All Files (*.*)|*.*||");
		CFileDialog*	pcBrowse;	//Need a file open dialog
		
		pcBrowse = (CFileDialog*)new CFileDialog(FALSE, NULL, csFilename, 
			OFN_PATHMUSTEXIST|OFN_EXPLORER|OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
			szFilter, NULL);
		if(IDOK == pcBrowse->DoModal())
		{
			POSITION p = pcBrowse->GetStartPosition();
			if(p)
			{
				csFilename = pcBrowse->GetNextPathName(p);
				pstrFilename = LPCTSTR(csFilename);
			}
		}
		else
		{
			return FALSE;
		}
		delete pcBrowse;
	}

	CWaitCursor cWait;
	CStdioFile	file;
	CFileException e;
	
	if(file.Open(pstrFilename, CFile::modeCreate|CFile::modeWrite, &e))
	{
		CHeaderCtrl* pHeader = GetHeaderCtrl();

		int nRows = bSelectedOnly ? GetSelectedCount() : GetItemCount();
		int nCols = GetHeaderCtrl()->GetItemCount();
		
		int		x, y;
		HDITEM	hdi;
		TCHAR	lpBuffer[256];
		TCHAR	lpSep[128];
		

		int nSep = ::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SLIST, lpSep, 127);
		CString csSep = _T("\"");
		csSep += lpSep;
		csSep += _T("\"");
		nSep = _tcslen(lpSep) + 1;// increment the separator length by one (")

		//
		// write the header
		//
		
		CString cs;
		CString csSubstring;

		hdi.mask = HDI_TEXT;
		hdi.pszText = lpBuffer;
		hdi.cchTextMax = 256;
		
		cs = _T("\"");
		for(x = 0; x < nCols; ++x)
		{
			pHeader->GetItem(x, &hdi);
			cs += hdi.pszText;
			cs += csSep;
		}
		cs = cs.Left(cs.GetLength()-nSep);		// remove the trailing ,"
		
		file.WriteString(cs);
		file.WriteString(_T("\n"));

		if(bSelectedOnly)
		{
			POSITION n = GetFirstSelectedItemPosition();
			while(n)
			{
				y = GetNextSelectedItem(n);
				
				cs = _T("\"");
				for(x = 0; x < nCols; ++x)
				{
					csSubstring = GetItemText(y, x);
					csSubstring.Replace(_T("\""), _T("\"\""));
					cs += csSubstring;
					cs += csSep;
				}
				cs = cs.Left(cs.GetLength()-nSep);		// remove the trailing ,"
				
				file.WriteString(cs);
				file.WriteString(_T("\n"));
			}
		}
		else
		{
			for(y = 0; y < nRows; ++y)
			{
				cs = _T("\"");
				for(x = 0; x < nCols; ++x)
				{
					csSubstring = GetItemText(y, x);
					csSubstring.Replace(_T("\""), _T("\"\""));
					cs += csSubstring;
					cs += csSep;
				}
				cs = cs.Left(cs.GetLength()-nSep);		// remove the trailing ,"
				
				file.WriteString(cs);
				file.WriteString(_T("\n"));
			}
		}
		
		file.Close();
		return TRUE;
	}
	else
	{
		TCHAR szError[1024];
		e.GetErrorMessage(szError, 1024);
		AfxMessageBox(szError, MB_OK|MB_ICONSTOP);
	}
	return FALSE;
}

void CEnhancedListCtrl::SetMessage(LPCTSTR pstrMsg)
{
	// dont update if the string is unchanged
	if(m_csMessage.Compare(pstrMsg) != 0)
	{
		m_csMessage = pstrMsg;
		
		Invalidate(TRUE);		// force repaint of the background
		UpdateWindow();
	}
}

BOOL CEnhancedListCtrl::InsertColumns(LPENHANCEDCOLUMNINFO pInfo, int nColumns)
{
	VERIFY_ISREADDATA(pInfo, sizeof(ENHANCEDCOLUMNINFO) * nColumns);
	CString		strTemp;
	LV_COLUMN	lvc;
	
	lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
	lvc.iSubItem = 0;
	
	for(int i = 0; i < nColumns; ++i)
	{
		if(pInfo->dwFlags & EHC_VISIBLE)
		{
			// if the string pointer is not a valid string, we assume that it is a resource id
			if(::IsBadReadPtr(pInfo->pstrTitle, 1))
			{
				strTemp.LoadString((int)(pInfo->pstrTitle));
				lvc.pszText = (TCHAR *)(LPCTSTR)strTemp;
			}
			else
			{
				lvc.pszText = (TCHAR *)(LPCTSTR)pInfo->pstrTitle;
			}

			lvc.cx = pInfo->nWidth;
			lvc.fmt = pInfo->dwFlags & ~EHC_MASK;
			int nCol = InsertColumn( i, &lvc );

			SetColumnData(nCol, pInfo->nColumnID);
			
			if(pInfo->dwFlags & EHC_CHECKABLE)
				SetColumnCheck(nCol, 0);

			++lvc.iSubItem;
		}
		++pInfo;
	}
	return TRUE;
}

BOOL CEnhancedListCtrl::UpdateColumns(LPENHANCEDCOLUMNINFO pInfo, int nColumns)
{
	//
	// save necessary states
	//
	int nOldSortID = GetColumnID(GetSortIndicator());
	
	//
	// delete all existing columns
	//
	while(DeleteColumn(0)) ;
	
	//
	// insert the new columns
	//
	BOOL bRet = InsertColumns(pInfo, nColumns);
	
	
	//
	// set all states back
	//
	if(bRet && IsColumnVisible(nOldSortID))
	{
		IndicateSortOrder(GetColumnIndex(nOldSortID), m_bLastSortOrder);
	}
	else
	{
		IndicateSortOrder(-1, m_bLastSortOrder);

		/* 
		int nItems = GetHeaderCtrl()->GetItemCount();
		for(int i = 0; i < nItems; i++)
		{
			// if(IsSortable(i))
			{
				IndicateSortOrder(i, m_bLastSortOrder);
				break;
			}
		}
		*/
	}
	return bRet;
}
	
BOOL CEnhancedListCtrl::LoadColumnState(LPCTSTR pstrName, LPENHANCEDCOLUMNINFO pInfo, int nColumns)
{
	VERIFY_ISREADDATA(pInfo, sizeof(ENHANCEDCOLUMNINFO) * nColumns);
	
	// copy the column info, we will modify it
	LPENHANCEDCOLUMNINFO pInfo2 = new ENHANCEDCOLUMNINFO[nColumns];

	int nCols = AfxGetApp()->GetProfileInt(pstrName, _T("Columns"), 0);
	if(nCols)
	{
		int* nSaveInfo0 = NULL;
		int* nSaveInfo1 = NULL;
		int* nOrder		= new int[nCols];
		int  o          = 0;
		UINT nBytes = nCols * sizeof(int);
		
		TRACE(_T("Loading %i columns: "), nCols);
		AfxGetApp()->GetProfileBinary(pstrName, _T("ColWidth"), (LPBYTE*)&nSaveInfo0, &nBytes);
		AfxGetApp()->GetProfileBinary(pstrName, _T("ColID"),    (LPBYTE*)&nSaveInfo1, &nBytes);

		for(int i = 0; i < nCols; ++i)
		{
			for(int n = 0; n < nColumns; ++n)
			{
				if(pInfo[n].nColumnID == nSaveInfo1[i])
				{
					pInfo2[o] = pInfo[n];

					TRACE(_T("%i(%ipx) "), pInfo2[o].nColumnID, nSaveInfo0[i]);
					pInfo2[o].nWidth = nSaveInfo0[i];
					pInfo2[o].dwFlags |= EHC_VISIBLE;
					o++;
					break;
				}
			}
		}

		delete[] nSaveInfo1;
		delete[] nSaveInfo0;

		// remove all existing columns
		while(CListCtrl::DeleteColumn(0))
			;

		for(; o < nColumns; ++o)
		{
			pInfo2[o] = pInfo[o];
			pInfo2[o].dwFlags &= ~EHC_VISIBLE;
		}
		
		InsertColumns(pInfo2, nColumns);
		// GetHeaderCtrl()->SetOrderArray(nCols, &nOrder[0]);
		delete[] nOrder;
	}

	delete[] pInfo2;
	return TRUE;
}

BOOL CEnhancedListCtrl::SaveColumnState(LPCTSTR pstrName, LPENHANCEDCOLUMNINFO, int)
{
	// VERIFY_ISREADDATA(pInfo, sizeof(ENHANCEDCOLUMNINFO) * nColumns);
	
	int nCols = GetHeaderCtrl()->GetItemCount();
	int* nSaveInfo0 = new int[nCols];
	int* nSaveInfo1 = new int[nCols];
	TRACE(_T("Saving %i columns : "), nCols);
	for(int i = 0; i < nCols; ++i)
	{
		HDITEM	hdi = {0};
		hdi.mask = HDI_WIDTH;
		
		//
		// using OrderToIndex because we want the real order from 0 to X
		//
		
		GetHeaderCtrl()->GetItem(GetHeaderCtrl()->OrderToIndex(i), &hdi);
		// GetHeaderCtrl()->GetItem(i, &hdi);
		
		nSaveInfo0[i] = hdi.cxy;
		nSaveInfo1[i] = GetColumnID(GetHeaderCtrl()->OrderToIndex(i));
		// nSaveInfo1[i] = GetColumnID(i);
		TRACE(_T("%i(%ipx) "), nSaveInfo1[i], nSaveInfo0[i]);
	}
	CString cs = pstrName;
	
	AfxGetApp()->WriteProfileInt(pstrName, _T("Columns"), nCols);
	AfxGetApp()->WriteProfileBinary(pstrName, _T("ColWidth"), (LPBYTE)&nSaveInfo0[0], sizeof(int)*nCols);
	AfxGetApp()->WriteProfileBinary(pstrName, _T("ColID"),    (LPBYTE)&nSaveInfo1[0], sizeof(int)*nCols);
	
	TRACE(_T("\r\n"));
	delete[] nSaveInfo1;
	delete[] nSaveInfo0;
	
	return TRUE;
}


LPENHANCEDCOLUMNINFO CEnhancedListCtrl::GetColumnInfo(const LPENHANCEDCOLUMNINFO pInfo, int nColumns)
{
	VERIFY_ISREADDATA(pInfo, sizeof(ENHANCEDCOLUMNINFO) * nColumns);
	
	int nCols = GetHeaderCtrl()->GetItemCount();
	LPENHANCEDCOLUMNINFO pInfo2 = new ENHANCEDCOLUMNINFO[nColumns];
	
	ZeroMemory(pInfo2, sizeof(ENHANCEDCOLUMNINFO) * nColumns);
	
	for(int i = 0; i < nCols; ++i)
	{
		HDITEM	hdi = {0};
		hdi.mask = HDI_WIDTH;
		
		int n = GetColumnID(GetHeaderCtrl()->OrderToIndex(i));
		for(int t = 0; t < nColumns; ++t)
			if(pInfo[t].nColumnID == n)
				break;
			
		CopyMemory(&pInfo2[i], &pInfo[t], sizeof(ENHANCEDCOLUMNINFO));
		
		GetHeaderCtrl()->GetItem(GetHeaderCtrl()->OrderToIndex(i), &hdi);
		
		pInfo2[i].nWidth = hdi.cxy;
		pInfo2[i].dwFlags |= EHC_VISIBLE;
 	}
	if(nColumns > nCols)
	{
		for(int i = 0; i < nColumns; ++i)
		{
			bool bFound = false;
			for(int n = 0; n < nCols; ++n)
			{
				if(pInfo[i].nColumnID == pInfo2[n].nColumnID)
				{ 
					bFound = true;
					break;
				}
			}
			if(!bFound)
			{
				CopyMemory(&pInfo2[nCols], &pInfo[i], sizeof(ENHANCEDCOLUMNINFO));
				pInfo2[nCols].dwFlags &= ~EHC_VISIBLE;
				nCols++;
			}
		}
	}
	
	return pInfo2;
}

void CEnhancedListCtrl::SetMinRowHeight(int n)
{
	if((CListCtrl::GetImageList(LVSIL_SMALL) == NULL) || (CListCtrl::GetImageList(LVSIL_SMALL) == m_ilRowHeight))
	{
		if(m_ilRowHeight)
		{
			m_ilRowHeight->DeleteImageList();
			delete m_ilRowHeight;
		}
		
		m_ilRowHeight = NULL;
		
		if(n > 0)
		{
			m_ilRowHeight = new CImageList();
			m_ilRowHeight->Create(1, n, ILC_COLOR, 1, 0);
			CListCtrl::SetImageList(m_ilRowHeight, LVSIL_SMALL);
		}
	}
}

int CEnhancedListCtrl::GetMinRowHeight()
{
	if(GetItemCount() == 0)
	{
		LOGFONT font;
		GetFont()->GetLogFont(&font);
		return (-font.lfHeight)+2;
	}
	CRect rc;
	CListCtrl::GetItemRect(0, &rc, LVIR_BOUNDS);
	return rc.Height();
}

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 Microsoft Public License (Ms-PL)


Written By
Software Developer (Senior)
Portugal Portugal
Software Smith, Blacksmith, Repeat Founder, Austrian, Asgardian.

Comments and Discussions