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