www.pudn.com > listclass1.zip > ListCtrlEx.cpp


// ListCtrlEx.cpp : implementation file 
// 
 
#include "stdafx.h" 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
///////////////////////////////////////////////////////////////////////////// 
// CHeaderCtrlEx 
CHeaderCtrlEx::CHeaderCtrlEx() 
	: marker_rect(0,0,0,0) 
{ 
	m_pWidth = NULL; 
	m_bDragging = FALSE; 
	m_bCheckForDrag = FALSE; 
	m_fpDragCol = NULL; 
	m_pOwnerWnd = NULL; 
	m_bDragEnable = false; 
} 
 
CHeaderCtrlEx::CHeaderCtrlEx(CWnd *pWnd, void (CWnd::*fpDragCol)(int, int)) 
	: marker_rect(0,0,0,0) 
{ 
	m_pWidth = NULL; 
	m_bDragging = FALSE; 
	m_bCheckForDrag = FALSE; 
	m_fpDragCol = fpDragCol; 
	m_pOwnerWnd = pWnd; 
	m_bDragEnable = false; 
} 
 
CHeaderCtrlEx::~CHeaderCtrlEx() 
{ 
} 
 
BEGIN_MESSAGE_MAP(CHeaderCtrlEx, CHeaderCtrl) 
	//{{AFX_MSG_MAP(CHeaderCtrlEx) 
	ON_WM_MOUSEMOVE() 
	ON_WM_LBUTTONUP() 
	ON_WM_LBUTTONDOWN() 
	//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
///////////////////////////////////////////////////////////////////////////// 
//	Public Properties 
// 
//	EnableDrag(bool bDrag=true):	Enable or disable column header dragging 
// 
///////////////////////////////////////////////////////////////////////////// 
 
bool CHeaderCtrlEx::EnableDrag(bool bDrag) 
{ 
	bool bOld = m_bDragEnable; 
	m_bDragEnable = bDrag; 
 
	return bOld; 
} 
 
//End Properties 
///////////////////////////////////////////////////////////////////////////// 
 
///////////////////////////////////////////////////////////////////////////// 
// CHeaderCtrlEx message handlers 
 
void CHeaderCtrlEx::OnMouseMove(UINT nFlags, CPoint point) 
{ 
	if(m_bDragEnable) { 
		if( (MK_LBUTTON & nFlags) == 0)	{ 
			// The left mouse button is not pressed - so reset flags 
			m_bCheckForDrag = FALSE; 
			m_bDragging = FALSE; 
		} 
		else if( m_bDragging ) { 
			// Get column number that falls under the mouse 
			int i=0, cx = 0; 
			if( point.x > 0 ) 
				for( i = 0; i < GetItemCount(); i++ ) { 
					if( point.x >= cx && point.x < cx + m_pWidth[i] ) 
						break; 
					cx += m_pWidth[i]; 
				} 
			if( i != m_nDropPos ) { 
				m_nDropPos = i; 
				CRect rect; 
				GetWindowRect( &rect ); 
				// Invalidate area occupied by previous marker 
				InvalidateRect( &marker_rect ); 
 
				// Draw a new marker 
				CClientDC dc(this); 
				POINT pts[3]; 
				pts[0].x = cx; 
				pts[1].x = cx - rect.Height()/4; 
				pts[2].x = cx + rect.Height()/4; 
				pts[0].y = rect.Height(); 
				pts[1].y = pts[2].y = rect.Height()/2; 
				dc.Polygon( pts, 3 ); 
 
				// save marker information 
				marker_rect.left = cx - (rect.Height()/4 + 1); 
				marker_rect.top = rect.Height() - (rect.Height()/2 + 1); 
				marker_rect.right = cx + (rect.Height()/4 + 1);	 
				marker_rect.bottom = rect.Height() + 1; 
			} 
			return; 
		} 
		else if( m_bCheckForDrag ) { 
			// The mouse button was pressed over a column header 
			// and now the mouse has moved - so start drag 
			m_bCheckForDrag = FALSE; 
			m_bDragging = TRUE; 
			m_nDropPos = m_nDragCol; 
			SetCapture(); 
 
			// Store information for later use 
			int iCount = GetItemCount(); 
			HD_ITEM hd_item; 
			m_pWidth = new int[iCount]; 
			for( int i = 0; i < iCount; i++ ) { 
				hd_item.mask = HDI_WIDTH; 
				GetItem( i, &hd_item ); 
				m_pWidth[i] = hd_item.cxy; 
			} 
			return; 
		} 
	} 
	 
	CHeaderCtrl::OnMouseMove(nFlags, point); 
} 
 
void CHeaderCtrlEx::OnLButtonDown(UINT nFlags, CPoint point) 
{ 
	if(m_bDragEnable) { 
		// Determine if mouse was pressed over a column header 
		HD_HITTESTINFO hd_hittestinfo; 
		hd_hittestinfo.pt = point; 
		SendMessage(HDM_HITTEST, 0, (LPARAM)(&hd_hittestinfo)); 
		if( hd_hittestinfo.flags == HHT_ONHEADER )	{ 
			m_nDragCol = hd_hittestinfo.iItem; 
			m_bCheckForDrag = TRUE; 
		} 
	} 
 
	CHeaderCtrl::OnLButtonDown(nFlags, point); 
} 
 
void CHeaderCtrlEx::OnLButtonUp(UINT nFlags, CPoint point) 
{ 
	if(m_bDragEnable) { 
		ASSERT( m_pOwnerWnd != NULL && m_fpDragCol != NULL ); 
		if( m_bDragging ) { 
			m_bDragging = FALSE; 
			delete[] m_pWidth; 
			ReleaseCapture(); 
			Invalidate(); 
				// Call the callback function. 
			if( m_nDragCol != m_nDropPos && m_nDragCol != m_nDropPos -1 ) 
				(m_pOwnerWnd->*m_fpDragCol)( m_nDragCol, m_nDropPos ); 
		} 
	} 
 
	CHeaderCtrl::OnLButtonUp(nFlags, point); 
} 
 
void CHeaderCtrlEx::SetCallback( CWnd* pWnd, void (CWnd::*fpDragCol)(int, int) ) 
{ 
	m_fpDragCol = fpDragCol; 
	m_pOwnerWnd = pWnd; 
} 
 
//End CHeaderCtrlEx 
///////////////////////////////////////////////////////////////////////////// 
 
///////////////////////////////////////////////////////////////////////////// 
// CListCtrlEx 
 
//Constructor for the list data structure 
CListCtrlEx::ListData::ListData(const DWORD dwOld, const CString &strTxt, const COleDateTime Start) 
{ 
	dwOldData = dwOld; 
	strText = strTxt; 
	BaseTime = Start; 
} 
 
CListCtrlEx::CListCtrlEx() 
{ 
	nSortedCol		= -1;  
	bSortAscending	= TRUE; 
	m_nNumCols		= 0; 
	m_pTypes		= NULL; 
	m_ctStart.SetStatus(COleDateTime::invalid); 
	m_bSort			= false; 
	m_nHighlight	= HIGHLIGHT_NORMAL; 
 
	m_headerctrl.SetCallback( this, (void (CWnd::*)(int, int))DragColumn ); 
} 
 
CListCtrlEx::~CListCtrlEx() 
{ 
	delete m_pTypes; 
} 
 
 
BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl) 
	//{{AFX_MSG_MAP(CListCtrlEx) 
	ON_WM_PAINT() 
	ON_WM_SETFOCUS() 
	ON_WM_KILLFOCUS() 
	ON_WM_LBUTTONDOWN() 
	ON_WM_LBUTTONUP() 
	ON_WM_MOUSEMOVE() 
	//}}AFX_MSG_MAP 
	ON_NOTIFY(HDN_ITEMCLICKA, 0, OnHeaderClicked)  
	ON_NOTIFY(HDN_ITEMCLICKW, 0, OnHeaderClicked) 
END_MESSAGE_MAP() 
 
///////////////////////////////////////////////////////////////////////////// 
//Public Properties 
// 
//	SetHighlightType(int hilite):		Set highlight type 
//	EnableSort(bool bSort=true):		Enable sorting by column 
//	SetColumnTypes(unsigned char* pColTypes, unsigned nColCnt): 
//										Set the column sort types									 
//	EnableHeaderDrag(bool bDrag=true}:	Enable column header dragging 
//	SetBaseTime(CString strTime):		Set the base time for 24hr clock 
// 
///////////////////////////////////////////////////////////////////////////// 
 
int CListCtrlEx::SetHighlightType(int hilite) 
{ 
	int oldhilite = m_nHighlight; 
	if( hilite <= HIGHLIGHT_ROW ) 
	{ 
		m_nHighlight = hilite; 
		//set to ownerdraw for full row highlighting 
		if(hilite > HIGHLIGHT_NORMAL) 
			ModifyStyle(0, LVS_OWNERDRAWFIXED); 
		Invalidate(); 
	}	 
	 
	return oldhilite; 
} 
 
bool CListCtrlEx::EnableSort(bool bSort) 
{ 
	bool bOldSort = m_bSort; 
	m_bSort = bSort; 
 
	return bOldSort; 
} 
 
bool CListCtrlEx::SetColumnTypes(unsigned char* pColTypes, unsigned nColCnt) 
{ 
	delete m_pTypes; 
	m_pTypes = new unsigned char[nColCnt]; 
	if(m_pTypes == NULL) { 
		m_nNumCols = 0; 
		return false; 
	} 
 
	for(unsigned i=0; i LVEX_LAST) { 
			m_nNumCols = 0; 
			delete m_pTypes; 
			return false; 
		} 
		m_pTypes[i] = pColTypes[i]; 
	} 
 
	m_nNumCols = nColCnt; 
 
	return true; 
} 
 
bool CListCtrlEx::EnableHeaderDrag(bool bDrag) 
{ 
	return m_headerctrl.EnableDrag(bDrag); 
} 
 
bool CListCtrlEx::SetBaseTime(CString strTime) 
{ 
	COleDateTime ctNew; 
	//there can only be one base time for all columns 
	//this function should only be called once 
	ASSERT(m_ctStart.GetStatus() == COleDateTime::invalid); 
 
	ctNew.ParseDateTime(strTime); 
	if(ctNew.GetStatus() != COleDateTime::invalid) { 
		m_ctStart = ctNew; 
		return true; 
	} 
 
	return false; 
} 
 
//End Properties 
///////////////////////////////////////////////////////////////////////////// 
 
///////////////////////////////////////////////////////////////////////////// 
// Highlighting 
// 
///////////////////////////////////////////////////////////////////////////// 
 
void CListCtrlEx::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 
{ 
	CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC); 
	CRect rcItem(lpDrawItemStruct->rcItem);	 
	int nItem = lpDrawItemStruct->itemID; 
	CImageList* pImageList;	// Save dc state	 
	int nSavedDC = pDC->SaveDC(); 
	CBrush WndBrush(::GetSysColor(COLOR_WINDOW)); 
	CBrush HltBrush(::GetSysColor(COLOR_HIGHLIGHT)); 
 
	// Get item image and state info	 
	LV_ITEM lvi; 
	lvi.mask = LVIF_IMAGE | LVIF_STATE;	 
	lvi.iItem = nItem;	 
	lvi.iSubItem = 0; 
	lvi.stateMask = 0xFFFF;		// get all state flags	 
	GetItem(&lvi); 
 
	// Should the item be highlighted 
	BOOL bHighlight =((lvi.state & LVIS_DROPHILITED) 
				|| ( (lvi.state & LVIS_SELECTED)			 
				&& ((GetFocus() == this) 
				|| (GetStyle() & LVS_SHOWSELALWAYS)))); 
 
	// Get rectangles for drawing	 
	CRect rcBounds, rcLabel, rcIcon; 
	GetItemRect(nItem, rcBounds, LVIR_BOUNDS); 
	GetItemRect(nItem, rcLabel, LVIR_LABEL);	 
	GetItemRect(nItem, rcIcon, LVIR_ICON); 
	CRect rcCol( rcBounds );  
	CString sLabel = GetItemText( nItem, 0 ); 
 
	// Labels are offset by a certain amount   
	// This offset is related to the width of a space character 
	int offset = pDC->GetTextExtent(_T(" "), 1 ).cx*2;	 
	CRect rcHighlight; 
	CRect rcWnd;	 
	int nExt;	 
	switch(m_nHighlight)	{	 
	case HIGHLIGHT_NORMAL:  
		nExt = pDC->GetOutputTextExtent(sLabel).cx + offset;		 
		rcHighlight = rcLabel; 
		if( rcLabel.left + nExt < rcLabel.right ) 
			rcHighlight.right = rcLabel.left + nExt;		 
		break;	 
	case HIGHLIGHT_ALLCOLUMNS: 
		rcHighlight = rcBounds;		 
		rcHighlight.left = rcLabel.left;		 
		break;	 
	case HIGHLIGHT_ROW: 
		GetClientRect(&rcWnd);		 
		rcHighlight = rcBounds; 
		rcHighlight.left = rcLabel.left;		 
		rcHighlight.right = rcWnd.right;		 
		break; 
	default:	 
		rcHighlight = rcLabel;	 
	}	 
	 
	// Draw the background color 
	if( bHighlight )	{		 
		pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT)); 
		pDC->SetBkColor(::GetSysColor(COLOR_HIGHLIGHT)); 
		pDC->FillRect(rcHighlight, &HltBrush);	 
	}	 
	else 
		pDC->FillRect(rcHighlight, &WndBrush);	 
 
	// Set clip region	 
	rcCol.right = rcCol.left + GetColumnWidth(0);	 
	CRgn rgn; 
	rgn.CreateRectRgnIndirect(&rcCol);	 
	pDC->SelectClipRgn(&rgn); 
	rgn.DeleteObject();	 
	 
	// Draw state icon	 
	if (lvi.state & LVIS_STATEIMAGEMASK)	{ 
		int nImage = ((lvi.state & LVIS_STATEIMAGEMASK)>>12) - 1; 
		pImageList = GetImageList(LVSIL_STATE);		 
		if (pImageList) { 
			pImageList->Draw(pDC, nImage, 
				CPoint(rcCol.left, rcCol.top), ILD_TRANSPARENT);		 
		}	 
	}	 
 
	// Draw normal and overlay icon	 
	pImageList = GetImageList(LVSIL_SMALL); 
	if (pImageList)	{		 
		UINT nOvlImageMask=lvi.state & LVIS_OVERLAYMASK; 
		pImageList->Draw(pDC, lvi.iImage, 			 
			CPoint(rcIcon.left, rcIcon.top), 
			(bHighlight?ILD_BLEND50:0) | ILD_TRANSPARENT | nOvlImageMask );	 
	}		 
	// Draw item label - Column 0	 
	rcLabel.left += offset/2; 
	rcLabel.right -= offset; 
	pDC->DrawText(sLabel,-1,rcLabel,DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP  
				| DT_VCENTER | DT_END_ELLIPSIS);	 
	 
	// Draw labels for remaining columns 
	LV_COLUMN lvc;	 
	lvc.mask = LVCF_FMT | LVCF_WIDTH; 
	if(m_nHighlight == HIGHLIGHT_NORMAL)	// Highlight only first column	 
	{ 
		pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT)); 
		pDC->SetBkColor(::GetSysColor(COLOR_WINDOW));	 
	}	 
	rcBounds.right = rcHighlight.right > rcBounds.right ? rcHighlight.right : 
							rcBounds.right;	rgn.CreateRectRgnIndirect(&rcBounds); 
	pDC->SelectClipRgn(&rgn);				    
	for(int nColumn = 1; GetColumn(nColumn, &lvc); nColumn++)	{ 
		rcCol.left = rcCol.right;		 
		rcCol.right += lvc.cx; 
 
		// Draw the background if needed		 
		if( m_nHighlight == HIGHLIGHT_NORMAL ) 
			pDC->FillRect(rcCol, &HltBrush); 
		sLabel = GetItemText(nItem, nColumn);		 
		if (sLabel.GetLength() == 0) 
			continue;		 
		 
		// Get the text justification		 
		UINT nJustify = DT_LEFT; 
		switch(lvc.fmt & LVCFMT_JUSTIFYMASK)		 
		{		 
		case LVCFMT_RIGHT: 
			nJustify = DT_RIGHT;			 
			break;		 
		case LVCFMT_CENTER:			 
			nJustify = DT_CENTER; 
			break;		 
		default:			 
			break;		 
		}		 
		rcLabel = rcCol;		 
		rcLabel.left += offset; 
		rcLabel.right -= offset; 
		pDC->DrawText(sLabel, -1, rcLabel, nJustify | DT_SINGLELINE |  
					DT_NOPREFIX | DT_VCENTER | DT_END_ELLIPSIS);	 
	} 
 
	// Draw focus rectangle if item has focus 
	if (lvi.state & LVIS_FOCUSED && (GetFocus() == this)) 
		pDC->DrawFocusRect(rcHighlight);		 
	 
	// Restore dc	 
	pDC->RestoreDC( nSavedDC ); 
	WndBrush.DeleteObject(); 
	HltBrush.DeleteObject(); 
} 
 
void CListCtrlEx::OnPaint()  
{	 
	// in full row select mode, we need to extend the clipping region 
	// so we can paint a selection all the way to the right 
	if (m_nHighlight == HIGHLIGHT_ROW && 
		(GetStyle() & LVS_TYPEMASK) == LVS_REPORT )	{ 
		CRect rcBounds; 
		GetItemRect(0, rcBounds, LVIR_BOUNDS); 
		CRect rcClient; 
		GetClientRect(&rcClient); 
		if(rcBounds.right < rcClient.right)		{ 
			CPaintDC dc(this); 
			CRect rcClip; 
			dc.GetClipBox(rcClip); 
			rcClip.left = min(rcBounds.right-1, rcClip.left); 
			rcClip.right = rcClient.right; 
			InvalidateRect(rcClip, FALSE); 
		} 
	} 
	 
	CListCtrl::OnPaint(); 
} 
 
void CListCtrlEx::OnSetFocus(CWnd* pOldWnd)  
{ 
	CListCtrl::OnSetFocus(pOldWnd); 
	 
	// check if we are getting focus from label edit box 
	if(pOldWnd!=NULL && pOldWnd->GetParent()==this) 
		return; 
	// repaint items that should change appearance 
	if((GetStyle() & LVS_TYPEMASK)==LVS_REPORT) 
		RepaintSelectedItems(); 
} 
 
void CListCtrlEx::OnKillFocus(CWnd* pNewWnd)  
{ 
	CListCtrl::OnKillFocus(pNewWnd); 
	 
	// check if we are losing focus to label edit box 
	if(pNewWnd != NULL && pNewWnd->GetParent() == this) 
		return; 
	// repaint items that should change appearance 
	if((GetStyle() & LVS_TYPEMASK) == LVS_REPORT) 
		RepaintSelectedItems(); 
} 
 
void CListCtrlEx::RepaintSelectedItems() 
{ 
	CRect rcBounds, rcLabel; 
 
	// Invalidate focused item so it can repaint  
	int nItem = GetNextItem(-1, LVNI_FOCUSED); 
 
	if(nItem != -1) 
	{ 
		GetItemRect(nItem, rcBounds, LVIR_BOUNDS); 
		GetItemRect(nItem, rcLabel, LVIR_LABEL); 
		rcBounds.left = rcLabel.left; 
 
		InvalidateRect(rcBounds, FALSE); 
	} 
 
	// Invalidate selected items depending on LVS_SHOWSELALWAYS 
	if(!(GetStyle() & LVS_SHOWSELALWAYS)) 
	{ 
		for(nItem = GetNextItem(-1, LVNI_SELECTED); 
			nItem != -1; nItem = GetNextItem(nItem, LVNI_SELECTED)) 
		{ 
			GetItemRect(nItem, rcBounds, LVIR_BOUNDS); 
			GetItemRect(nItem, rcLabel, LVIR_LABEL); 
			rcBounds.left = rcLabel.left; 
 
			InvalidateRect(rcBounds, FALSE); 
		} 
	} 
 
	UpdateWindow(); 
} 
 
//places check marks in state icon boxes when clicked 
void CListCtrlEx::OnLButtonDown(UINT nFlags, CPoint point)  
{ 
	UINT uFlags = 0; 
	int nHitItem = HitTest(point, &uFlags); 
 
	BOOL bHit = FALSE; 
	if (uFlags & LVHT_ONITEMSTATEICON) 
		bHit = TRUE; 
 
	if (bHit) { 
		UINT nState = GetItemState(nHitItem, LVIS_STATEIMAGEMASK); 
		if(nState == INDEXTOSTATEIMAGEMASK(1)) 
			SetItemState(nHitItem, INDEXTOSTATEIMAGEMASK(2), LVIS_STATEIMAGEMASK); 
		else 
			SetItemState(nHitItem, INDEXTOSTATEIMAGEMASK(1), LVIS_STATEIMAGEMASK); 
	} 
	 
	CListCtrl::OnLButtonDown(nFlags, point); 
} 
 
//End Hilighting 
////////////////////////////////////////////////////////////////////// 
 
////////////////////////////////////////////////////////////////////// 
//Sorting by columns 
// 
////////////////////////////////////////////////////////////////////// 
 
void CListCtrlEx::OnHeaderClicked(NMHDR* pNMHDR, LRESULT* pResult) 
{ 
	HD_NOTIFY *phdn = (HD_NOTIFY *) pNMHDR; 
 
	if(phdn->iButton == 0 && m_bSort)// Button is the left mouse button  
	{ 
		// User clicked on header using left mouse button 
		if(phdn->iItem == nSortedCol) 
			bSortAscending = !bSortAscending; 
		else 
			bSortAscending = TRUE; 
 
		nSortedCol = phdn->iItem; 
		int NumItems = GetItemCount();	 
 
		// replace Item data with pointer to ListData structure 
		for (int i = 0; i < NumItems; i++)	{ 
			DWORD dwData = GetItemData(i); // save current data to restore it later 
			CString strText = GetItemText(i, nSortedCol); 
			 
			SetItemData(i, (DWORD)new ListData(dwData, strText, m_ctStart));	 
		} 
 
		SortItems(Compare, bSortAscending | (m_pTypes[nSortedCol] << 8)); 
 
		for(i=0; idwOldData); 
			delete pData; 
 
		} 
	} 
	*pResult = 0; 
} 
 
int CALLBACK CListCtrlEx::Compare(LPARAM lParam1, LPARAM lParam2, LPARAM  
								 lParSortAsc) 
{ 
	CString str1 = ((ListData*)lParam1)->strText; 
	CString str2 = ((ListData*)lParam2)->strText; 
 
	unsigned char Type = unsigned char(lParSortAsc >> 8); 
 
	// restore data type and sort order from lParamSort 
	// if lParamSort positive - ascending sort order, negative - descending 
	int  sOrder = (lParSortAsc & 0xFF) ? 1 : -1;  
	COleDateTime t1, t2, bTime = ((ListData*)lParam1)->BaseTime;	 
	 
	switch (Type)	{ 
	//Date time column(s) with 24 hr base time	 
	case  LVEX_TIME_BASE://most people will never use this		 
		if (t1.ParseDateTime(str1) && t2.ParseDateTime(str2)) 
		{ 
			if(bTime.GetStatus() != COleDateTime::invalid &&  
				t1 < bTime) 
			{ 
				t1 += COleDateTimeSpan(1, 0, 0, 0); 
			} 
			if(bTime.GetStatus() != COleDateTime::invalid && 
				t2 < bTime) 
			{ 
				t2 += COleDateTimeSpan(1, 0, 0, 0); 
			} 
		 
			return (t1 < t2 ? -1 : 1 )*sOrder; 
		} 
		else			 
			return 0; 
	//Date time column(s) no base time 
	case  LVEX_TIME: 
		if (t1.ParseDateTime(str1) && t2.ParseDateTime(str2)) 
			return (t1 < t2 ? -1 : 1 )*sOrder;		 
		else			 
			return 0; 
	//integer number columns 
	case  LVEX_NUM:		 
		return (atol(str1) - atol(str2))*sOrder; 
	//float number columns 
	case  LVEX_NUM_FLOAT: 
		return (atof(str1) < atof(str2) ? -1 : 1)*sOrder; 
	//text columns (no case) 
	case  LVEX_NOCASE:		 
		return str1.CompareNoCase(str2)*sOrder; 
	//text columns (case sensitive) 
	case LVEX_CASE: 
		return (str1.Compare(str2))*sOrder;	 
	} 
 
	return 0; 
} 
 
//End Sorting 
////////////////////////////////////////////////////////////////////////// 
 
////////////////////////////////////////////////////////////////////// 
//Drag Column Headers 
// 
////////////////////////////////////////////////////////////////////// 
 
void CListCtrlEx::DragColumn(int source, int dest) 
{	 
	TCHAR sColText[160]; 
 
	// Insert a column at dest 
	LV_COLUMN lv_col; 
	lv_col.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; 
	lv_col.pszText = sColText; 
	lv_col.cchTextMax = 159; 
	GetColumn( source, &lv_col ); 
	lv_col.iSubItem = dest; 
	InsertColumn( dest, &lv_col ); 
 
	// Adjust source col number since it might have changed  
	// because a new column was inserted 
	if( source > dest ) 
		source++; 
 
	// Moving a col to position 0 is a special case	 
	if( dest == 0 ) 
		for( int i = GetItemCount()-1; i > -1 ; i-- ) 
			SetItemText(i, 1, GetItemText( i, 0) );	 
		 
	// Copy sub item from source to dest 
	for( int i = GetItemCount()-1; i > -1 ; i-- ) 
		SetItemText(i, dest, GetItemText( i, source ) ); 
 
	// Delete the source column, but not if it is the first	 
	if( source != 0 ) 
		DeleteColumn( source ); 
	else { 
		// If source col is 0, then copy col# 1 to col#0 
		// and then delete col# 1 
		GetColumn( 1, &lv_col ); 
		lv_col.iSubItem = 0; 
		SetColumn( 0, &lv_col ); 
		for( int i = GetItemCount()-1; i > -1 ; i-- ) 
			SetItemText(i, 0, GetItemText( i, 1) ); 
		DeleteColumn( 1 ); 
	} 
	 
	Invalidate(); 
} 
 
void CListCtrlEx::PreSubclassWindow()  
{ 
	CListCtrl::PreSubclassWindow(); 
	 
	// Add initialization code 
	m_headerctrl.SubclassWindow( ::GetDlgItem(m_hWnd,0) ); 
} 
 
//End Drag Headers 
//////////////////////////////////////////////////////////////////////////