www.pudn.com > Multilines_demo.zip > MultiLineListCtrl.cpp


/*********************************************************************************************************/ 
/*Class Name: MultiLineListCtrl                                                                          */ 
/*Class Definition: It creates a CListCtrl to support multiple lines in one row. When there are many     */ 
/*                  lines, a [+] icon show to user be able to expand the row.                            */ 
/*Class Author: Carlos Andre Sanches de Souza                                                            */ 
/*Class Creation Date: 08/08/2007                                                                        */ 
/*********************************************************************************************************/ 
 
#include "stdafx.h" 
#include "MultiLineListCtrl.h" 
 
// CMultiLineListCtrl 
 
IMPLEMENT_DYNAMIC(CMultiLineListCtrl, CListCtrl) 
CMultiLineListCtrl::CMultiLineListCtrl() 
: m_skipTextCheck(false) 
, m_lastSelected(-1) 
, m_selected(-1) 
, m_repeatEvent(false) 
, m_grid(true) 
, m_minimized(false) 
, m_gridColor(RGB(236,233,216)) 
{ 
	colorSymbol = RGB(180,180,180); 
	LOGBRUSH LogBrush; 
	LogBrush.lbColor = colorSymbol; 
	LogBrush.lbStyle = PS_SOLID; 
	dot.CreatePen( PS_COSMETIC | PS_ALTERNATE , 1, &LogBrush, 0, NULL ); 
} 
 
CMultiLineListCtrl::~CMultiLineListCtrl() 
{ 
	dot.DeleteObject(); 
} 
 
 
BEGIN_MESSAGE_MAP(CMultiLineListCtrl, CListCtrl) 
	ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw) 
	ON_NOTIFY_REFLECT(LVN_ITEMACTIVATE, OnActivate) 
	ON_NOTIFY_REFLECT(NM_DBLCLK, OnDblClick) 
END_MESSAGE_MAP() 
 
/**********************************************************************************************/ 
/*Function Name: SetGridColor                                                                 */ 
/*Function Definition: Set grid color                                                         */ 
/*Function Author: Carlos Andre Sanches de Souza                                              */ 
/*Function Creation Date: 08/08/2007                                                          */ 
/*Variables Name:                                                                             */ 
/*   COLORREF color: new color to grid                                                        */ 
/**********************************************************************************************/ 
void CMultiLineListCtrl::SetGridColor(COLORREF color) { 
	m_gridColor = color; 
	Invalidate(); 
} 
 
/**********************************************************************************************/ 
/*Function Name: SetBkColor                                                                   */ 
/*Function Definition: Set background color                                                   */ 
/*Function Author: Carlos Andre Sanches de Souza                                              */ 
/*Function Creation Date: 08/08/2007                                                          */ 
/*Variables Name:                                                                             */ 
/*   COLORREF color: new color to background                                                  */ 
/**********************************************************************************************/ 
BOOL CMultiLineListCtrl::SetBkColor(COLORREF cr) { 
	m_gridColor = cr; 
	return CListCtrl::SetBkColor(cr); 
} 
 
/**********************************************************************************************/ 
/*Function Name: ShowGrid                                                                     */ 
/*Function Definition: Show/hide grid                                                         */ 
/*Function Author: Carlos Andre Sanches de Souza                                              */ 
/*Function Creation Date: 08/08/2007                                                          */ 
/*Variables Name:                                                                             */ 
/*   bool show: true = show grid                                                              */ 
/**********************************************************************************************/ 
void CMultiLineListCtrl::ShowGrid(bool show) { 
	m_grid = show; 
} 
 
/**********************************************************************************************/ 
/*Function Name: ShowMinimized                                                                */ 
/*Function Definition: Show/hide the whole string in one line when minimized                  */ 
/*Function Author: Carlos Andre Sanches de Souza                                              */ 
/*Function Creation Date: 08/08/2007                                                          */ 
/*Variables Name:                                                                             */ 
/*   bool show: true = show the string minimized                                              */ 
/**********************************************************************************************/ 
void CMultiLineListCtrl::ShowMinimized(bool show) { 
	m_minimized = show; 
} 
 
/**********************************************************************************************/ 
/*Function Name: OnCustomDraw                                                                 */ 
/*Function Definition: Draw list items                                                        */ 
/*Function Author: Carlos Andre Sanches de Souza                                              */ 
/*Function Creation Date: 08/08/2007                                                          */ 
/**********************************************************************************************/ 
void CMultiLineListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) 
{ 
	*pResult = CDRF_DODEFAULT; 
	LPNMLVCUSTOMDRAW  lplvcd = (LPNMLVCUSTOMDRAW)pNMHDR; 
 
	switch(lplvcd->nmcd.dwDrawStage) { 
	case CDDS_PREPAINT : 
		*pResult = CDRF_NOTIFYITEMDRAW; 
		return; 
 
	case CDDS_ITEMPREPAINT: 
		*pResult = CDRF_NOTIFYSUBITEMDRAW; 
		return; 
 
	case CDDS_SUBITEM | CDDS_PREPAINT | CDDS_ITEM:  
		{ 
		int nItem = lplvcd->nmcd.dwItemSpec; 
		int nSubItem = lplvcd->iSubItem; 
		int nNumLines = (int)GetItemData(nItem); 
		int nCurrentSelection; 
		bool changeSelection = false; 
		POSITION posSelection; 
		HDC hdc = lplvcd->nmcd.hdc; 
		CString str; 
		CRect boxRect, bounds, rect; 
		CDC* pDC = CDC::FromHandle(hdc); 
 
		// Get background box 
		boxRect = lplvcd->nmcd.rc; 
		GetItemRect( lplvcd->nmcd.dwItemSpec, &bounds, LVIR_BOUNDS ); 
		boxRect.top = bounds.top; 
		boxRect.bottom = bounds.bottom; 
		if( nSubItem == 0 ) 
		{ 
			CRect lrect; 
			GetItemRect( lplvcd->nmcd.dwItemSpec, &lrect, LVIR_LABEL ); 
			boxRect.left = lrect.left; 
			boxRect.right = lrect.right; 
		} 
		else 
		{ 
			boxRect.right += bounds.left; 
			boxRect.left  += bounds.left; 
		} 
 
		// Get selection 
		posSelection = GetFirstSelectedItemPosition(); 
		if (posSelection)  
			nCurrentSelection = GetNextSelectedItem(posSelection); 
		else 
			nCurrentSelection = -1; 
		if (nCurrentSelection != m_selected) { 
			m_lastSelected = m_selected; 
			m_selected = nCurrentSelection; 
			changeSelection = true; 
		} 
 
		// Fill background box 
		if ( m_selected == nItem || 
			(nNumLines>1 && nItemnItem && m_selected=nItem+nNumLines && m_selectedFillRect(boxRect, &CBrush(::GetSysColor(COLOR_HIGHLIGHT))); 
			pDC->SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT)) ; 
		} 
		else { 
			pDC->FillRect(boxRect, &CBrush(::GetSysColor(COLOR_WINDOW))); 
			pDC->SetTextColor(GetSysColor(COLOR_WINDOWTEXT)) ; 
		} 
		 
		// Get text string 
		str = GetItemText( nItem, nSubItem ); 
		 
		// Get text box 
		rect = boxRect; 
		rect.left += nSubItem?6:2; 
		 
		// Draw symbol [+] 
		if (nNumLines>1 && GetItemText(nItem, nSubItem).Find('\n')>-1 ) 
		{ 
			CPen light(PS_SOLID, 1, colorSymbol); 
			pDC->SelectObject(light); 
			pDC->MoveTo(rect.left,rect.top+2); 
			pDC->LineTo(rect.left+8,rect.top+2); 
			pDC->LineTo(rect.left+8,rect.top+10); 
			pDC->LineTo(rect.left,rect.top+10); 
			pDC->LineTo(rect.left,rect.top+2); 
			pDC->MoveTo(rect.left+2,rect.top+6); 
			pDC->LineTo(rect.left+7,rect.top+6); 
			if (nItemSelectObject(dot); 
				pDC->MoveTo(rect.left+4,rect.top+10); 
				pDC->LineTo(rect.left+4,rect.bottom); 
			} 
			else { 
				pDC->MoveTo(rect.left+4,rect.top+4); 
				pDC->LineTo(rect.left+4,rect.top+9); 
			} 
			if (!m_minimized || (nItem-1) 
					str = str.Left(str.Find('\n')); 
				if (str.Find('\r')>-1) 
					str = str.Left(str.Find('\r')); 
			} 
			else { 
                str.Replace("\r\n"," | "); 
				str.Replace("\n"," | "); 
			} 
			rect.left += 12; 
		} 
		// Draw dot lines 
		if (nNumLines<1 && GetItemText(nItem+nNumLines, nSubItem).Find('\n')>-1) { 
			int countLines = 1; 
			CString text = GetItemText(nItem+nNumLines, nSubItem); 
			for (int k=0;k-nNumLines) { 
				pDC->SelectObject(dot); 
				pDC->MoveTo(rect.left+4,rect.top); 
				if (countLines-1==-nNumLines) { 
					pDC->LineTo(rect.left+4,rect.top+6); 
					pDC->LineTo(rect.left+8,rect.top+6); 
				} 
				else 
					pDC->LineTo(rect.left+4,rect.bottom); 
			} 
			rect.left += 12; 
		} 
 
		// Draw text 
		pDC->DrawText( str, rect, DT_SINGLELINE|DT_NOPREFIX|DT_LEFT|DT_VCENTER|DT_END_ELLIPSIS); 
 
		// Draw grid 
		if (m_grid) { 
			CPen gridline(PS_SOLID, 1, m_gridColor); 
			pDC->SelectObject(gridline); 
			if (nSubItem>0) { 
				pDC->MoveTo(boxRect.left, boxRect.top); 
				pDC->LineTo(boxRect.left, boxRect.bottom); 
			} 
			if (nNumLines==1 || nItem==GetItemCount()-1 || (int)GetItemData(nItem+1)>=1) { 
				pDC->MoveTo(boxRect.left, boxRect.bottom-1); 
				pDC->LineTo(boxRect.right, boxRect.bottom-1); 
			} 
		} 
		*pResult = CDRF_SKIPDEFAULT; 
 
		// invalidate 
		if (changeSelection) { 
			int topSel = m_selected+((m_selected>-1 && (int)GetItemData(m_selected)<1)?(int)GetItemData(m_selected):0); 
			int topLast = m_lastSelected+((m_lastSelected>-1 && (int)GetItemData(m_lastSelected)<1)?(int)GetItemData(m_lastSelected):0); 
			CRect clean; 
			if (topSel != topLast) { 
				if (topSel > -1) { 
					GetItemRect(topSel, &clean, LVIR_BOUNDS); 
					if ((int)GetItemData(topSel)>1 && topSel -1) { 
					GetItemRect(topLast, &clean, LVIR_BOUNDS); 
					if ((int)GetItemData(topLast)>1 && topLast(pNMHDR); 
	*pResult = 0; 
 
	if (m_repeatEvent) 
		return; 
 
	if (GetItemData((int)pNMLV->iItem)==1) 
		return; 
 
	m_repeatEvent = true; 
	OpenCloseRow((int)pNMLV->iItem); 
} 
 
/**********************************************************************************************/ 
/*Function Name: OnDblClick                                                                   */ 
/*Function Definition: Avoid to repeat OnActivate event                                       */ 
/*Function Author: Carlos Andre Sanches de Souza                                              */ 
/*Function Creation Date: 08/08/2007                                                          */ 
/**********************************************************************************************/ 
void CMultiLineListCtrl::OnDblClick(NMHDR *pNMHDR, LRESULT *pResult) 
{ 
	m_repeatEvent = false; 
	*pResult = 0; 
} 
 
/**********************************************************************************************/ 
/*Function Name: InsertItem / SetItemText                                                     */ 
/*Function Definition: Overwrite functions to calculate number of lines                       */ 
/*Function Author: Carlos Andre Sanches de Souza                                              */ 
/*Function Creation Date: 08/08/2007                                                          */ 
/**********************************************************************************************/ 
int CMultiLineListCtrl::InsertItem(const LVITEM* pItem) { 
	int result = CListCtrl::InsertItem(pItem); 
	if (result == -1) 
		return -1; 
	CalcNumberOfLines(pItem->iItem,pItem->pszText); 
	return result; 
} 
int CMultiLineListCtrl::InsertItem(int nItem,LPCTSTR lpszItem) { 
	int result = CListCtrl::InsertItem(nItem,lpszItem); 
	if (result == -1) 
		return -1; 
	CalcNumberOfLines(nItem,lpszItem); 
	return result; 
} 
int CMultiLineListCtrl::InsertItem(int nItem,LPCTSTR lpszItem,int nImage) { 
	int result = CListCtrl::InsertItem(nItem,lpszItem,nImage); 
	if (result == -1) 
		return -1; 
	CalcNumberOfLines(nItem,lpszItem); 
	return result; 
} 
BOOL CMultiLineListCtrl::SetItemText(int nItem,int nSubItem,LPCTSTR lpszText) 
{ 
	CalcNumberOfLines(nItem,lpszText,false); 
	return CListCtrl::SetItemText(nItem,nSubItem,lpszText); 
}; 
 
/**********************************************************************************************/ 
/*Function Name: CalcNumberOfLines                                                            */ 
/*Function Definition: Count number of lines ('\n')                                           */ 
/*Function Author: Carlos Andre Sanches de Souza                                              */ 
/*Function Creation Date: 08/08/2007                                                          */ 
/**********************************************************************************************/ 
void CMultiLineListCtrl::CalcNumberOfLines(int nItem, LPCTSTR lpszText, bool insert) { 
	if (m_skipTextCheck)  
		return; 
    int numLines = 1; 
	for (unsigned int i=0; i=1 && (row==GetItemCount()-1 || (int)GetItemData(row+1)>=1)) { 
		// open 
		for (i=0; iGetItemCount(); i++) { 
			str = GetItemText(row, i); 
			for (j=1; j<(int)GetItemData(row); j++) { 
				if (str.Find('\n')==-1) 
					break; 
				s = str = str.Right(str.GetLength() - str.Find('\n') - 1);  
				if (str.Find('\n')>-1) 
					s = str.Left(str.Find('\n'));  
				if (s.Find('\r')>-1) 
					s = s.Left(s.Find('\r')); 
				if (j>inserted) { 
					InsertItem(row+j,""); 
					inserted = j; 
				} 
				SetItemText(row+j,i,s); 
				SetItemData(row+j,-j); 
			} 
		} 
	}  
	else { 
		// close 
		if ((int)GetItemData(row)>1)  
	        for (j=1; j<(int)GetItemData(row); j++) 
				DeleteItem(row+1); 
	} 
	m_skipTextCheck = false; 
	Invalidate(); 
} 
 
/**********************************************************************************************/ 
/*Function Name: CloseAll                                                                     */ 
/*Function Definition: Contract all rows                                                      */ 
/*Function Author: Carlos Andre Sanches de Souza                                              */ 
/*Function Creation Date: 08/08/2007                                                          */ 
/**********************************************************************************************/ 
void CMultiLineListCtrl::CloseAll() { 
	int i,j; 
	for (i=0; i1 && i