www.pudn.com > ExtListctrl_new000.rar > ExtListCtrl.cpp
// ExtListCtrl.cpp : implementation file
//
#include "stdafx.h"
#include "ExtListCtrl.h"
#include "Ext.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define MUST_STYLE (WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL)
#define MUST_NOT_STYLE (LVS_EDITLABELS | LVS_REPORT | LVS_ICON | LVS_SMALLICON | LVS_LIST | LVS_NOSCROLL)
#define MUST_EX_STYLE (LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES)
#define MUST_NOT_EX_STYLE (0)
/////////////////////////////////////////////////////////////////////////////
// CExtListCtrl
CExtListCtrl::CExtListCtrl()
{
m_pWndEdit = new CEdit;
m_bEditable = TRUE;
m_ptEdit.x = -1;
m_ptEdit.y = -1;
m_nSortedColumn = 0;
m_bSortAscending = TRUE;
m_bSortable = TRUE;
m_nCompareAs = ELCT_STRING_CASE;
}
CExtListCtrl::~CExtListCtrl()
{
m_Font.DeleteObject();
if (m_pWndEdit != NULL)
delete m_pWndEdit;
}
BEGIN_MESSAGE_MAP(CExtListCtrl, CListCtrl)
//{{AFX_MSG_MAP(CExtListCtrl)
ON_WM_CREATE()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONDBLCLK()
ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnclick)
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
ON_WM_DESTROY()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
int CExtListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CListCtrl::OnCreate(lpCreateStruct) == -1)
return -1;
return 0;
}
void CExtListCtrl::OnDestroy()
{
m_pWndEdit->DestroyWindow();
for (int i = 0; i < GetItemCount(); i++)
_FreeItemMemory(i);
CListCtrl::OnDestroy();
}
/////////////////////////////////////////////////////////////////////////////
// CExtListCtrl message handlers
BOOL CExtListCtrl::_GetHeaderColumn()
{
return (GetStyle() & LVS_NOCOLUMNHEADER) == 0;
}
int CExtListCtrl::_GetHeaderFMT(int nCol)
{
if (!_GetHeaderColumn())
return HDF_LEFT;
HDITEM hdItem;
hdItem.mask = HDI_FORMAT;
if (!GetHeaderCtrl()->GetItem(nCol, &hdItem))
return HDF_LEFT;
return hdItem.fmt;
}
UINT CExtListCtrl::_GetItemFMT(int nItem, int nSubItem)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
if (pItem != NULL)
return pItem->nFormat[nSubItem];
return DT_LEFT;
}
DWORD CExtListCtrl::_AllocItemMemory(int nItem)
{
if (nItem < 0 || nItem > GetItemCount())
return EXT_INVALIDINDEX;
int nCols = GetColumnCount();
if (nCols == 0)
nCols = 1;
EXT_ITEM *pItem = new EXT_ITEM;
pItem->dwData = CListCtrl::GetItemData(nItem);
pItem->bModify = FALSE;
pItem->lpszItemEx.SetSize(nCols);
pItem->crTxColor.SetSize(nCols);
pItem->crBkColor.SetSize(nCols);
pItem->nChecked.SetSize(nCols);
pItem->nFormat.SetSize(nCols);
for (int i = 0; i < nCols; i++)
{
pItem->crTxColor[i] = GetSysColor(COLOR_WINDOWTEXT);
pItem->crBkColor[i] = GetSysColor(COLOR_WINDOW);
pItem->nChecked[i] = -1;
pItem->nFormat[i] = DT_LEFT;
}
CListCtrl::SetItemData(nItem, (DWORD)pItem);
return EXT_SUCCESS;
}
void CExtListCtrl::_FreeItemMemory(int nItem)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
if (pItem)
delete pItem;
}
void CExtListCtrl::_UpdateColumn(int nCol, BOOL bInsert)
{
for (int i = 0; i < GetItemCount(); i++)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(i);
if (!pItem)
continue;
if (bInsert)
{
pItem->lpszItemEx.InsertAt(nCol, "");
pItem->crBkColor.InsertAt(nCol, GetSysColor(COLOR_WINDOW));
pItem->crTxColor.InsertAt(nCol, GetSysColor(COLOR_WINDOWTEXT));
pItem->nChecked.InsertAt(nCol, 0);
}
else
{
pItem->lpszItemEx.RemoveAt(nCol);
pItem->crBkColor.RemoveAt(nCol);
pItem->crTxColor.RemoveAt(nCol);
pItem->nChecked.RemoveAt(nCol);
}
}
}
void CExtListCtrl::_MouseClkMonitor(UINT nMsg, UINT nFlags, CPoint point, BOOL bTriggerEdit)
{
_EndEdit(TRUE);
LVHITTESTINFO hti;
hti.pt = point;
const int IDX = SubItemHitTest(&hti);
if (IDX == -1)
return;
switch (nMsg)
{
case WM_LBUTTONDOWN:
CListCtrl::OnLButtonDown(nFlags, point);
break;
case WM_LBUTTONDBLCLK:
CListCtrl::OnLButtonDblClk(nFlags, point);
break;
case WM_MBUTTONDOWN:
CListCtrl::OnMButtonDown(nFlags, point);
break;
case WM_MBUTTONDBLCLK:
CListCtrl::OnMButtonDblClk(nFlags, point);
break;
case WM_RBUTTONDOWN:
CListCtrl::OnRButtonDown(nFlags, point);
break;
case WM_RBUTTONDBLCLK:
CListCtrl::OnRButtonDblClk(nFlags, point);
break;
default:
break;
}
_ItemCheckMonitor(nMsg, IDX, hti.iSubItem);
if (!bTriggerEdit)
return;
_StartEdit(IDX, hti.iSubItem);
}
void CExtListCtrl::_ItemCheckMonitor(UINT nMsg, int nItem, int nSubItem)
{
if (nMsg != WM_LBUTTONDOWN)
return;
int nChecked = _GetCheck(nItem, nSubItem);
if (nChecked != -1)
SetCheck(nItem, nSubItem, !nChecked);
}
void CExtListCtrl::_StartEdit(int nItem, int nSubItem)
{
//第一列允许编辑
if (!m_bEditable ||
nItem < 0 || nItem >= GetItemCount() ||
nSubItem < 0 || nSubItem == 0 || nSubItem >= GetColumnCount())
return;
if (m_ptEdit.x == nItem && m_ptEdit.y == nSubItem)
return;
m_ptEdit.x = nItem;
m_ptEdit.y = nSubItem;
if (m_pWndEdit->GetSafeHwnd() != NULL)
m_pWndEdit->DestroyWindow();
int nFMT = _GetHeaderFMT(nSubItem);
if (nFMT & HDF_CENTER)
nFMT = ES_CENTER;
else if (nFMT & HDF_RIGHT)
nFMT = ES_RIGHT;
else
nFMT = ES_LEFT;
if (!m_pWndEdit->Create(WS_CHILD | WS_BORDER | ES_AUTOHSCROLL | ES_NOHIDESEL | nFMT, CRect(0, 0, 1, 1), this, 0))
return;
m_pWndEdit->SetFont(GetFont());
CRect rcEdit;
GetSubItemRect(m_ptEdit.x, m_ptEdit.y, LVIR_LABEL, rcEdit);
//ListView_GetSubItemRect(GetSafeHwnd(), m_ptEdit.x, m_ptEdit.y, LVIR_LABEL, &rcEdit);
if (m_ptEdit.y > 0 && GetItemImage(m_ptEdit.x, m_ptEdit.y) != -1)
rcEdit.DeflateRect(16, 0, 0, 0);
m_pWndEdit->MoveWindow(&rcEdit);
m_pWndEdit->SetWindowText(GetItemText(m_ptEdit.x, m_ptEdit.y));
m_pWndEdit->ShowWindow(SW_SHOW);
m_pWndEdit->SetSel(0, -1);
m_pWndEdit->SetFocus();
}
void CExtListCtrl::_EndEdit(BOOL bCommit)
{
if (m_ptEdit.x < 0 && m_ptEdit.y < 0)
return;
CString str;
m_pWndEdit->GetWindowText(str);
BOOL bChanged = bCommit && str.Compare(GetItemText(m_ptEdit.x, m_ptEdit.y)) != 0;
if (bChanged)
SetItemText(m_ptEdit.x, m_ptEdit.y, str, _GetItemFMT(m_ptEdit.x, m_ptEdit.y));
m_pWndEdit->ShowWindow(SW_HIDE);
m_ptEdit.x = -1;
m_ptEdit.y = -1;
}
/*========================================================================
Name: QuickSort().
-----------------------------------------------------------
Params: [p]: start position, usually index 0.
[q]: end position, usually last index.
==========================================================================*/
void CExtListCtrl::_QuickSort(int p, int r)
{
if (p < r)
{
int q = _Partition(p, r);
_QuickSort(p, q);
_QuickSort(q + 1, r);
}
}
/*========================================================================
Name: Partition().
-----------------------------------------------------------
Params: [p]: start position.
[q]: end position.
==========================================================================*/
int CExtListCtrl::_Partition(int p, int r)
{
CString tmp;
CString x = GetItemText(p, m_nSortedColumn);
int i = p - 1;
int j = r + 1;
while (i < j)
{
do
{
j--;
tmp = GetItemText(j, m_nSortedColumn);
} while (_CompareBy(tmp, x, m_bSortAscending ? ELOT_GT : ELOT_LT));
do
{
i++;
tmp = GetItemText(i, m_nSortedColumn);
} while (_CompareBy(tmp, x, m_bSortAscending ? ELOT_LT : ELOT_GT));
if (i < j)
{
_SwapRow(i, j);
}
}
return j;
}
/*========================================================================
Name: Swap nRow1 with nRow2.
-----------------------------------------------------------
Params: [nRow1]: row index (zero based).
[nRow2]: row index (zero based).
[op]: operator type
return:
TRUE if successful, else FALSE
==========================================================================*/
BOOL CExtListCtrl::_SwapRow(int nRow1, int nRow2)
{
int nMaxRows = GetItemCount();
if ((nRow1 >= 0) && (nRow1 < nMaxRows) &&
(nRow2 >= 0) && (nRow2 < nMaxRows) &&
(nRow1 != nRow2))
{
const int nCol = GetColumnCount();
EXT_SORT *pSort = new EXT_SORT[2];
for (int i = 0; i < 2; i++)
{
int nRow = (i == 0 ? nRow1 : nRow2);
pSort[i].dwData = CListCtrl::GetItemData(nRow);
pSort[i].nState = GetItemState(nRow);
pSort[i].lpszItem.SetSize(nCol);
pSort[i].lpszItemEx.SetSize(nCol);
pSort[i].nImage.SetSize(nCol);
pSort[i].nChecked.SetSize(nCol);
pSort[i].nFormat.SetSize(nCol);
for (int j = 0; j < nCol; j++)
{
pSort[i].lpszItem[j] = GetItemText(nRow, j);
pSort[i].lpszItemEx[j] = GetItemTextEx(nRow, j);
pSort[i].nImage[j] = GetItemImage(nRow, j);
pSort[i].nChecked[j] = _GetCheck(nRow, j);
pSort[i].nFormat[j] = _GetItemFMT(nRow, j);
}
}
for (i = 0; i < 2; i++)
{
int nRow = (i == 0 ? nRow2 : nRow1);
CListCtrl::SetItemData(nRow, pSort[i].dwData);
SetItemState(nRow, pSort[i].nState, TRUE);
for (int j = 0; j < nCol; j++)
{
SetItemText(nRow, j, pSort[i].lpszItem[j], pSort[i].nFormat[j]);
SetItemTextEx(nRow, j, pSort[i].lpszItemEx[j]);
SetItemImage(nRow, j, pSort[i].nImage[j]);
if (pSort[i].nChecked[j] != -1)
SetCheck(nRow, j, pSort[i].nChecked[j]);
}
}
delete[] pSort;
}
return TRUE;
}
/*========================================================================
Name: Convert strings to new data type based on m_nCompareAs'
value. Compare the strings using the operator.
-----------------------------------------------------------
Params: [str1]: string 1 (left operand).
[str2]: string 2 (right operand).
[op]: operator type
return:
The result of (str1 op str2)
==========================================================================*/
BOOL CExtListCtrl::_CompareBy(CString str1, CString str2, ListOperatorType op)
{
BOOL bReturn = FALSE;
switch (m_nCompareAs)
{
case ELCT_INTEGER:
{
int val1 = atoi(str1);
int val2 = atoi(str2);
if (op == ELOT_LT)
bReturn = (val1 < val2);
else if (op == ELOT_GT)
bReturn = (val1 > val2);
else if (op == ELOT_LTE)
bReturn = (val1 <= val2);
else if (op == ELOT_GTE)
bReturn = (val1 >= val2);
else
bReturn = (val1 == val2);
break;
}
case ELCT_DOUBLE:
{
double val1 = atof(str1);
double val2 = atof(str2);
if (op == ELOT_LT)
bReturn = (val1 < val2);
else if (op == ELOT_GT)
bReturn = (val1 > val2);
else if (op == ELOT_LTE)
bReturn = (val1 <= val2);
else if (op == ELOT_GTE)
bReturn = (val1 >= val2);
else
bReturn = (val1 == val2);
break;
}
case ELCT_STRING_NOCASE:
{
str1.MakeUpper();
str2.MakeUpper();
}
case ELCT_STRING_CASE:
default:
{
if (op == ELOT_LT)
bReturn = (str1 < str2);
else if (op == ELOT_GT)
bReturn = (str1 > str2);
else if (op == ELOT_LTE)
bReturn = (str1 <= str2);
else if (op == ELOT_GTE)
bReturn = (str1 >= str2);
else
bReturn = (str1 == str2);
break;
}
}
return bReturn;
}
/*========================================================================
Name: set view style.
-----------------------------------------------------------
Params: [style]: view style.
[LVS_ICON]
[LVS_SMALLICON]
[LVS_LIST]
[LVS_REPORT]
[LVS_EDITLABELS]
==========================================================================*/
LONG CExtListCtrl::SetStyle(DWORD dwStyle)
{
LONG style;
style = ::GetWindowLong(m_hWnd, GWL_STYLE);
style &= ~LVS_TYPEMASK;
style |= dwStyle;
return SetWindowLong(m_hWnd, GWL_STYLE, style);
}
LONG CExtListCtrl::GetStyle()
{
return ::GetWindowLong(m_hWnd, GWL_STYLE);
}
LONG CExtListCtrl::_GetStyle()
{
LONG style = GetStyle() & LVS_ICON; //0
style = GetStyle() & LVS_REPORT; //1
style = GetStyle() & LVS_SMALLICON; //2
style = GetStyle() & LVS_LIST; //3
return style;
}
DWORD CExtListCtrl::SetExtendedStyle(DWORD dwNewStyle)
{
dwNewStyle &= ~MUST_NOT_EX_STYLE;
dwNewStyle |= MUST_EX_STYLE;
return CListCtrl::SetExtendedStyle(dwNewStyle);
}
DWORD CExtListCtrl::GetExtendedStyle()
{
return CListCtrl::GetExtendedStyle();
}
BOOL CExtListCtrl::ModifyStyle(DWORD dwRemove, DWORD dwAdd, UINT nFlags)
{
return CListCtrl::ModifyStyle(dwRemove, dwAdd, nFlags);
}
BOOL CExtListCtrl::ModifyStyleEx(DWORD dwRemove, DWORD dwAdd, UINT nFlags)
{
return CListCtrl::ModifyStyleEx(dwRemove, dwAdd, nFlags);
}
void CExtListCtrl::Modify(int nItem, BOOL bModify)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
if (pItem == NULL)
return;
pItem->bModify = bModify;
}
BOOL CExtListCtrl::ItemModified(int nItem)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
if (pItem == NULL)
return FALSE;
return pItem->bModify;
}
int CExtListCtrl::InsertColumn(int nCol, const LVCOLUMN* pColumn)
{
const int IDX = CListCtrl::InsertColumn(nCol, pColumn);
if (IDX >= 0)
_UpdateColumn(nCol, TRUE);
return IDX;
}
int CExtListCtrl::InsertColumn(int nCol, LPCTSTR lpszColumnHeading, int nWidth, int nFormat, int nSubItem)
{
const int IDX = CListCtrl::InsertColumn(nCol, lpszColumnHeading, nFormat, nWidth, nSubItem);
if (IDX >= 0)
_UpdateColumn(nCol, TRUE);
return IDX;
}
void CExtListCtrl::SetEditable(BOOL bEditable)
{
m_bEditable = bEditable;
}
void CExtListCtrl::SetSortable(BOOL bSortable)
{
m_bSortable = bSortable;
}
BOOL CExtListCtrl::DeleteColumn(int nCol)
{
_EndEdit(FALSE);
const BOOL bFlag = CListCtrl::DeleteColumn(nCol);
if (bFlag)
_UpdateColumn(nCol, FALSE);
return bFlag;
}
void CExtListCtrl::DeleteAllColumns()
{
while (GetColumnCount() > 0)
DeleteColumn(0);
}
CHeaderCtrl* CExtListCtrl::GetHeaderCtrl() const
{
return (CHeaderCtrl*)(CWnd::FromHandle(ListView_GetHeader(GetSafeHwnd())));
}
int CExtListCtrl::GetColumnCount()
{
if (GetStyle() & LVS_REPORT)
return GetHeaderCtrl()->GetItemCount();
return 0;
}
BOOL CExtListCtrl::EnsureVisible(int nItem, BOOL bPartialOK)
{
//SetItemState(nItem, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
return CListCtrl::EnsureVisible(nItem, bPartialOK);
}
int CExtListCtrl::InsertItem()
{
int nItem = GetItemCount();
nItem = (nItem == 0 ? 0 : nItem);
return InsertItem(nItem, "");
}
int CExtListCtrl::InsertItem(const LVITEM* pItem)
{
const int IDX = CListCtrl::InsertItem(pItem);
if (IDX >= 0)
_AllocItemMemory(IDX);
//EnsureVisible(IDX, FALSE);
return IDX;
}
int CExtListCtrl::InsertItem(int nItem, LPCTSTR lpszItem, int nImage)
{
const int IDX = (nImage == -1 ? CListCtrl::InsertItem(nItem, lpszItem) :
CListCtrl::InsertItem(nItem, lpszItem, nImage));
if (IDX >= 0)
_AllocItemMemory(IDX);
//EnsureVisible(IDX, FALSE);
return IDX;
}
int CExtListCtrl::InsertItem(UINT nMask, int nItem, LPCTSTR lpszItem, UINT nState, UINT nStateMask, int nImage, LPARAM lParam)
{
const int IDX = CListCtrl::InsertItem(nMask, nItem, lpszItem, nState, nStateMask, nImage, lParam);
if (IDX >= 0)
_AllocItemMemory(IDX);
//EnsureVisible(IDX, FALSE);
return IDX;
}
BOOL CExtListCtrl::DeleteItem(int nItem)
{
_EndEdit(FALSE);
if (nItem < 0) return TRUE;
SetItemState(nItem - 1, LVIS_SELECTED|LVIS_FOCUSED);
_FreeItemMemory(nItem);
return CListCtrl::DeleteItem(nItem);
}
BOOL CExtListCtrl::DeleteAllItems()
{
_EndEdit(FALSE);
for (int i = 0; i < GetItemCount(); i++)
_FreeItemMemory(i);
return CListCtrl::DeleteAllItems();
}
BOOL CExtListCtrl::SetItemText(int nItem, int nSubItem, LPCTSTR lpszItem, UINT nFormat)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
if (pItem == NULL)
return FALSE;
pItem->nFormat[nSubItem] = nFormat;
return CListCtrl::SetItemText(nItem, nSubItem, lpszItem);
}
BOOL CExtListCtrl::SetItemText(int nItem, int nSubItem, INT lpszItem, UINT nFormat)
{
return SetItemText(nItem, nSubItem, (LONG)lpszItem, nFormat);
}
BOOL CExtListCtrl::SetItemText(int nItem, int nSubItem, UINT lpszItem, UINT nFormat)
{
return SetItemText(nItem, nSubItem, (ULONG)lpszItem, nFormat);
}
BOOL CExtListCtrl::SetItemText(int nItem, int nSubItem, LONG lpszItem, UINT nFormat)
{
CString strItem;
strItem.Format("%d", lpszItem);
return SetItemText(nItem, nSubItem, strItem, nFormat);
}
BOOL CExtListCtrl::SetItemText(int nItem, int nSubItem, ULONG lpszItem, UINT nFormat)
{
CString strItem;
strItem.Format("%u", lpszItem);
return SetItemText(nItem, nSubItem, strItem, nFormat);
}
BOOL CExtListCtrl::SetItemText(int nItem, int nSubItem, DOUBLE lpszItem, int nPrecision, UINT nFormat)
{
CString strFmt, strItem;
strFmt.Format(_T("%%.%df"), nPrecision);
strItem.Format(strFmt, lpszItem);
return SetItemText(nItem, nSubItem, strItem, nFormat);
}
BOOL CExtListCtrl::SetItemTextEx(int nItem, int nSubItem, LPCTSTR lpszItem, LPCTSTR lpszItemEx)
{
return SetItemText(nItem, nSubItem, lpszItem) && SetItemTextEx(nItem, nSubItem, lpszItemEx);
}
BOOL CExtListCtrl::SetItemTextEx(int nItem, int nSubItem, LPCTSTR lpszItemEx)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
if (pItem == NULL)
return FALSE;
pItem->lpszItemEx[nSubItem] = lpszItemEx;
return TRUE;
}
CString CExtListCtrl::GetItemText(int nItem, int nSubItem)
{
CString value = CListCtrl::GetItemText(nItem, nSubItem);
value.TrimLeft();
value.TrimRight();
return value;
}
CString CExtListCtrl::GetItemTextEx(int nItem, int nSubItem)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
if (pItem == NULL)
return "";
return pItem->lpszItemEx[nSubItem];
}
BOOL CExtListCtrl::SetItemImage(int nItem, int nSubItem, int nImage)
{
return CListCtrl::SetItem(nItem, nSubItem, LVIF_IMAGE, NULL, nImage, 0, 0, 0);
}
int CExtListCtrl::GetItemImage(int nItem, int nSubItem)
{
LVITEM lvItem;
lvItem.iItem = nItem;
lvItem.iSubItem = nSubItem;
lvItem.mask = LVIF_IMAGE;
return CListCtrl::GetItem(&lvItem) ? lvItem.iImage : -1;
}
BOOL CExtListCtrl::SetItemData(int nItem, DWORD dwData)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
if (pItem == NULL)
return FALSE;
pItem->dwData = dwData;
return CListCtrl::SetItemData(nItem, (DWORD)pItem);
}
DWORD CExtListCtrl::GetItemData(int nItem) const
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
return pItem == NULL ? 0 : pItem->dwData;
}
void CExtListCtrl::SetBkColor(int nItem, int nSubItem, COLORREF crColor, BOOL bRedraw)
{
const int nRows = GetItemCount();
const int nCols = GetColumnCount();
BOOL bRow = (nItem >= 0 && nItem < nRows);
BOOL bCol = (nSubItem >= 0 && nSubItem < nCols);
if (bRow && bCol)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
ASSERT(pItem != NULL);
pItem->crBkColor[nSubItem] = crColor;
}
else if (bRow && !bCol)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
ASSERT(pItem != NULL);
for (int i = 0; i < nCols; i++)
pItem->crBkColor[i] = crColor;
}
else if (!bRow && bCol)
{
for (int i = 0; i < nRows; i++)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(i);
ASSERT(pItem != NULL);
pItem->crBkColor[nSubItem] = crColor;
}
}
else
{
for (int i = 0; i < nRows; i++)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(i);
ASSERT(pItem != NULL);
for (int j = 0; j < nCols; j++)
pItem->crBkColor[j] = crColor;
}
}
if (bRedraw) /* bRedraw=true,记录多时,闪烁厉害 */
RedrawWindow();
}
void CExtListCtrl::SetTextColor(int nItem, int nSubItem, COLORREF crColor, BOOL bRedraw)
{
const int nRows = GetItemCount();
const int nCols = GetColumnCount();
BOOL bRow = (nItem >= 0 && nItem < nRows);
BOOL bCol = (nSubItem >= 0 && nSubItem < nCols);
if (bRow && bCol)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
ASSERT(pItem != NULL);
pItem->crTxColor[nSubItem] = crColor;
}
else if (bRow && !bCol)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
ASSERT(pItem != NULL);
for (int i = 0; i < nCols; i++)
pItem->crTxColor[i] = crColor;
}
else if (!bRow && bCol)
{
for (int i = 0; i < nRows; i++)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(i);
ASSERT(pItem != NULL);
pItem->crTxColor[nSubItem] = crColor;
}
}
else
{
for (int i = 0; i < nRows; i++)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(i);
ASSERT(pItem != NULL);
for (int j = 0; j < nCols; j++)
pItem->crTxColor[j] = crColor;
}
}
if (bRedraw)/* bRedraw=true,记录多时,闪烁厉害 */
RedrawWindow();
}
BOOL CExtListCtrl::SetItemState(int nItem, UINT nState, BOOL bState)
{
UINT nMask = (nState == 0 ? LVIS_SELECTED|LVIS_FOCUSED : nState);
nState = !bState ? 0 : nState;
return CListCtrl::SetItemState(nItem, nState, nMask);
}
UINT CExtListCtrl::GetItemState(int nItem)
{
UINT nState = 0;
nState |= CListCtrl::GetItemState(nItem, LVIS_CUT);
nState |= CListCtrl::GetItemState(nItem, LVIS_DROPHILITED);
nState |= CListCtrl::GetItemState(nItem, LVIS_SELECTED);
nState |= CListCtrl::GetItemState(nItem, LVIS_FOCUSED);
return nState;
}
BOOL CExtListCtrl::SetCheck(int nItem, int nSubItem, BOOL bChecked)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
if (pItem == NULL)
return FALSE;
pItem->nChecked[nSubItem] = (bChecked ? 1 : 0);
CRect rcItem;
GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rcItem);
InvalidateRect(rcItem);
return TRUE;
}
BOOL CExtListCtrl::GetCheck(int nItem, int nSubItem)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
if (pItem == NULL)
return FALSE;
return pItem->nChecked[nSubItem] == 1 ? TRUE : FALSE;
}
int CExtListCtrl:: _GetCheck(int nItem, int nSubItem)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
if (pItem == NULL)
return FALSE;
return pItem->nChecked[nSubItem];
}
int CExtListCtrl::GetSelectedItem()
{
for (int i = 0; i < GetItemCount(); i++)
{
if (GetItemState(i) & LVIS_SELECTED)
return i;
}
return -1;
}
BOOL CExtListCtrl::SelectItem(int nItem)
{
EnsureVisible(nItem, FALSE);
return SetItemState(nItem, LVIS_SELECTED|LVIS_FOCUSED, TRUE);
}
int CExtListCtrl::Find(LPCTSTR lpszItem, int nSubItem, int nBegin)
{
/* 二分查找算法 */
int nLow = nBegin;
int nHigh = GetItemCount() - 1;
while (nLow <= nHigh)
{
int nMid = (nLow + nHigh) / 2;
if (GetItemText(nMid, nSubItem) == lpszItem)
return nMid;
if (GetItemText(nMid, nSubItem) > lpszItem)
nHigh = nMid - 1;
else
nLow = nMid + 1;
}
return -1;
}
int CExtListCtrl::FindEx(LPCTSTR lpszItem, int nSubItem, int nBegin)
{
/* 二分查找算法 */
int nLow = nBegin;
int nHigh = GetItemCount() - 1;
while (nLow <= nHigh)
{
int nMid = (nLow + nHigh) / 2;
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nMid);
if (pItem != NULL)
{
if (pItem->lpszItemEx[nSubItem] == lpszItem)
return nMid;
if (pItem->lpszItemEx[nSubItem] > lpszItem)
nHigh = nMid - 1;
else
nLow = nMid + 1;
}
}
return -1;
}
BOOL CExtListCtrl::PreCreateWindow(CREATESTRUCT& cs)
{
return CListCtrl::PreCreateWindow(cs);
}
void CExtListCtrl::PreSubclassWindow()
{
// Report
LONG lStyle = GetWindowLong(m_hWnd, GWL_STYLE);
lStyle |= LVS_REPORT;
SetWindowLong(m_hWnd, GWL_STYLE, lStyle);
// Full select and subitem image.
SetExtendedStyle(MUST_EX_STYLE);
if (!m_pHeader.SubclassWindow(GetDlgItem(0)->GetSafeHwnd()))
return;
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(NONCLIENTMETRICS);
VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0));
memcpy(&m_Logfont, &(ncm.lfMessageFont), sizeof(LOGFONT));
m_Logfont.lfHeight = 13;
m_Font.DeleteObject();
m_Font.CreateFontIndirect(&m_Logfont);
SetFont(&m_Font);
CListCtrl::PreSubclassWindow();
}
void CExtListCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
_MouseClkMonitor(WM_LBUTTONDOWN, nFlags, point, FALSE);
}
void CExtListCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
{
_MouseClkMonitor(WM_LBUTTONDBLCLK, nFlags, point, TRUE);
}
BOOL CExtListCtrl::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYDOWN)
{
POINT pt = m_ptEdit;
switch (pMsg->wParam)
{
case VK_ESCAPE:
_EndEdit(FALSE);
return TRUE;
case VK_RETURN:
_EndEdit(TRUE);
return TRUE;
case VK_TAB:
if (pt.y == GetItemCount() - 1)
pt.y = 0;
else
pt.y ++;
_EndEdit(TRUE);
_StartEdit(pt.x, pt.y);
return TRUE;
case VK_UP:
if (pt.x > 0)
{
pt.x --;
_EndEdit(TRUE);
_StartEdit(pt.x, pt.y);
EnsureVisible(pt.x, FALSE);
return TRUE;
}
break;
case VK_DOWN:
if (pt.x < GetItemCount() - 1)
{
pt.x ++;
_EndEdit(TRUE);
_StartEdit(pt.x, pt.y);
EnsureVisible(pt.x, FALSE);
return TRUE;
}
break;
default:
break;
}
}
return CListCtrl::PreTranslateMessage(pMsg);
}
void CExtListCtrl::OnColumnclick(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if (!m_bSortable) return;
m_bSortAscending = (pNMListView->iSubItem == m_nSortedColumn) ? !m_bSortAscending : TRUE;
m_nSortedColumn = pNMListView->iSubItem;
_EndEdit(FALSE);
_QuickSort(0, GetItemCount() - 1);
*pResult = 0;
}
void CExtListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
LPNMLVCUSTOMDRAW pLVCD = (LPNMLVCUSTOMDRAW)pNMHDR;
switch (pLVCD->nmcd.dwDrawStage)
{
case CDDS_PREPAINT: // Before the painting cycle begins.
{
if (_IsDraw())
{
CDC *pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
CRect r(pLVCD->nmcd.rc);
_OnDraw(pDC, r);
}
else
{
}
}
_SwitchTo(pLVCD->nmcd.dwDrawStage, pResult, TRUE);
break;
case CDDS_POSTPAINT: //After the painting cycle is complete.
{
if (_IsPostDraw())
{
CDC *pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
CRect r(pLVCD->nmcd.rc);
_OnPostDraw(pDC, r);
}
else
{
}
}
break;
case CDDS_ITEMPREPAINT: // Before an item is drawn.
{
int nItem = pLVCD->nmcd.dwItemSpec;
if (_IsItemDraw(nItem))
{
CDC *pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
_OnItemDraw(pDC, nItem);
_SwitchTo(pLVCD->nmcd.dwDrawStage, pResult, FALSE);
}
else
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
pLVCD->clrTextBk = _BKColorFor(nItem, 0, pItem);
pLVCD->clrText = _TXColorFor(nItem, 0, pItem);
}
_SwitchTo(pLVCD->nmcd.dwDrawStage, pResult, TRUE);
break;
}
case CDDS_ITEMPREPAINT|CDDS_SUBITEM:
{
int nItem = pLVCD->nmcd.dwItemSpec;
int nSubItem = pLVCD->iSubItem;
if (_IsSubItemDraw(nItem, nSubItem))
{
CDC *pDC = CDC::FromHandle(pLVCD->nmcd.hdc);
_OnSubItemDraw(pDC, nItem, nSubItem);
_SwitchTo(pLVCD->nmcd.dwDrawStage, pResult, FALSE);
}
else
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
ASSERT(pItem != NULL);
ASSERT(nSubItem >= 0 && nSubItem < pItem->crTxColor.GetSize());
pLVCD->clrTextBk = _BKColorFor(nItem, nSubItem, pItem);
pLVCD->clrText = _TXColorFor(nItem, nSubItem, pItem);
_SwitchTo(pLVCD->nmcd.dwDrawStage, pResult, TRUE);
}
break;
}
case CDDS_ITEMPOSTPAINT: // After an item has been drawn.
{
int nItem = pLVCD->nmcd.dwItemSpec;
if (_IsItemPostDraw(nItem))
{
}
else
{
}
}
break;
case CDDS_ITEMPOSTPAINT|CDDS_SUBITEM:
{
int nItem = pLVCD->nmcd.dwItemSpec;
int nSubItem = pLVCD->iSubItem;
if (_IsSubItemPostDraw(nItem, nSubItem))
{
}
else
{
}
}
break;
}
}
void CExtListCtrl::_SwitchTo(DWORD dwDrawStage, LRESULT* pResult, BOOL bDefault)
{
switch (dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_POSTPAINT:
break;
case CDDS_ITEMPREPAINT:
{
LONG style = _GetStyle();
if (style == 0) //LVS_ICON
*pResult = bDefault ? CDRF_DODEFAULT : CDRF_SKIPDEFAULT;
else if (style == 1) //LVS_REPORT
*pResult = bDefault ? CDRF_NOTIFYSUBITEMDRAW : CDRF_SKIPDEFAULT;
else if (style == 2) //LVS_SMALLICON
*pResult = bDefault ? CDRF_DODEFAULT : CDRF_SKIPDEFAULT;
else if (style == 3) //LVS_LIST
*pResult = CDRF_SKIPDEFAULT;
}
break;
case CDDS_ITEMPREPAINT|CDDS_SUBITEM:
if (bDefault)
*pResult = CDRF_DODEFAULT;
else
*pResult = CDRF_SKIPDEFAULT;
break;
case CDDS_ITEMPOSTPAINT:
break;
case CDDS_ITEMPOSTPAINT|CDDS_SUBITEM:
break;
}
}
COLORREF CExtListCtrl::_BKColorFor(int nItem, int nSubItem, EXT_ITEM *pItem)
{
if (_GetStyle() == 1) //LVS_REPORT
return _BKColorForSubItem(nItem, nSubItem, pItem);
else
return _BKColorForItem(nItem, pItem);
}
COLORREF CExtListCtrl::_TXColorFor(int nItem, int nSubItem, EXT_ITEM *pItem)
{
if (_GetStyle() == 1) //LVS_REPORT
return _TXColorForSubItem(nItem, nSubItem, pItem);
else
return _TXColorForItem(nItem, pItem);
}
COLORREF CExtListCtrl::_BKColorForItem(int nItem, EXT_ITEM *pItem)
{
if ((GetItemState(nItem) & LVIS_SELECTED) || pItem == NULL)
return ::GetSysColor(COLOR_HIGHLIGHT);
else
return pItem->crBkColor[0];
}
COLORREF CExtListCtrl::_TXColorForItem(int nItem, EXT_ITEM *pItem)
{
if ((GetItemState(nItem) & LVIS_SELECTED) || pItem == NULL)
return ::GetSysColor(COLOR_HIGHLIGHTTEXT);
else
return pItem->crTxColor[0];
}
COLORREF CExtListCtrl::_BKColorForSubItem(int nItem, int nSubItem, EXT_ITEM *pItem)
{
if ((GetItemState(nItem) & LVIS_SELECTED) || pItem == NULL)
return ::GetSysColor(COLOR_HIGHLIGHT);
else
return pItem->crBkColor[nSubItem];
}
COLORREF CExtListCtrl::_TXColorForSubItem(int nItem, int nSubItem, EXT_ITEM *pItem)
{
if ((GetItemState(nItem) & LVIS_SELECTED) || pItem == NULL)
return ::GetSysColor(COLOR_HIGHLIGHTTEXT);
else
return pItem->crTxColor[nSubItem];
}
BOOL CExtListCtrl::_IsDraw()
{
return FALSE;
}
DWORD CExtListCtrl::_OnDraw(CDC *pDC, const CRect rcItem)
{
return EXT_SUCCESS;
}
BOOL CExtListCtrl::_IsPostDraw()
{
return FALSE;
}
DWORD CExtListCtrl::_OnPostDraw(CDC *pDC, const CRect rcItem)
{
return EXT_SUCCESS;
}
BOOL CExtListCtrl::_IsItemDraw(int nItem)
{
if (_GetStyle() == 3) //LVS_LIST
return TRUE;
return FALSE;
}
DWORD CExtListCtrl::_OnItemDraw(CDC *pDC, int nItem)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
if (pItem == NULL)
return EXT_FAILE;
CRect rcItem, rcLabel, rcCheck;
_CalcRect(nItem, 0, rcItem, rcLabel, rcCheck, pItem);
pDC->FillSolidRect(rcItem, _BKColorFor(nItem, 0, pItem));
_DrawCheckbox(pDC, nItem, 0, rcCheck, pItem);
_DrawText(pDC, nItem, 0, rcLabel, pItem);
return EXT_SUCCESS;
}
BOOL CExtListCtrl::_IsItemPostDraw(int nItem)
{
return FALSE;
}
DWORD CExtListCtrl::_OnPostDraw(CDC *pDC, int nItem)
{
return EXT_SUCCESS;
}
BOOL CExtListCtrl::_IsSubItemDraw(int nItem, int nSubItem)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
if (pItem == NULL)
return FALSE;
return TRUE;
}
DWORD CExtListCtrl::_OnSubItemDraw(CDC *pDC, int nItem, int nSubItem)
{
EXT_ITEM *pItem = (EXT_ITEM*)CListCtrl::GetItemData(nItem);
if (pItem == NULL)
return EXT_FAILE;
CRect rcItem, rcLabel, rcCheck;
_CalcRect(nItem, nSubItem, rcItem, rcLabel, rcCheck, pItem);
pDC->FillSolidRect(rcItem, _BKColorFor(nItem, nSubItem, pItem));
_DrawCheckbox(pDC, nItem, nSubItem, rcCheck, pItem);
_DrawText(pDC, nItem, nSubItem, rcLabel, pItem);
return EXT_SUCCESS;
}
BOOL CExtListCtrl::_IsSubItemPostDraw(int nItem, int nSubItem)
{
return FALSE;
}
DWORD CExtListCtrl::_OnSubItemPostDraw(CDC *pDC, int nItem, int nSubItem)
{
return EXT_SUCCESS;
}
void CExtListCtrl::_CalcRect(int nItem, int nSubItem, CRect &rcItem, CRect &rcLabel, CRect &rcCheck, EXT_ITEM *pItem)
{
if (_GetStyle() != 1)
_CalcItemRect(nItem, rcItem, rcLabel, rcCheck, pItem);
else
_CalcSubItemRect(nItem, nSubItem, rcItem, rcLabel, rcCheck, pItem);
}
void CExtListCtrl::_CalcItemRect(int nItem, CRect &rcItem, CRect &rcLabel, CRect &rcCheck, EXT_ITEM *pItem)
{
// Item rect
GetItemRect(nItem, rcItem, LVIR_BOUNDS);
CString strTxt = GetItemText(nItem, 0);
BOOL bCheck = pItem->nChecked[0] == -1 ? FALSE : TRUE;
BOOL bLabel = strTxt.IsEmpty() ? FALSE : TRUE;
if (bCheck && !bLabel)
{
rcCheck = rcItem;
rcCheck.DeflateRect(1, 1);
rcCheck.right = rcCheck.left + rcCheck.Height();
}
else if (!bCheck && bLabel)
{
rcLabel = rcItem;
}
else if (bCheck && bLabel)
{
rcCheck = rcItem;
rcCheck.DeflateRect(1, 1);
rcCheck.right = rcCheck.left + rcCheck.Height();
rcLabel = rcItem;
rcLabel.left = rcCheck.right + 2;
}
}
void CExtListCtrl::_CalcSubItemRect(int nItem, int nSubItem, CRect &rcItem, CRect &rcLabel, CRect &rcCheck, EXT_ITEM *pItem)
{
// Item rect
GetSubItemRect(nItem, nSubItem, LVIR_BOUNDS, rcItem);
// if nSubItem == 0, the rect returned by CListCtrl::GetSubItemRect
// is the entire row, so use left edge of second subitem
if (nSubItem == 0 && GetColumnCount() > 1)
{
CRect rc;
GetSubItemRect(nItem, 1, LVIR_BOUNDS, rc);
// if rc.Width() == 0, then rc.left = 0
// do not show nSubItem's text.
if (rc.Width() != 0)
rcItem.right = rc.left;
}
//
CString strTxt = GetItemText(nItem, nSubItem);
BOOL bCheck = pItem->nChecked[nSubItem] == -1 ? FALSE : TRUE;
BOOL bLabel = strTxt.IsEmpty() ? FALSE : TRUE;
if (bCheck && !bLabel)
{
rcCheck = rcItem;
rcCheck.DeflateRect(1, 1);
rcCheck.left = rcItem.left + rcItem.Width()/2 - 6;
rcCheck.right = rcCheck.left + rcCheck.Height();
}
else if (!bCheck && bLabel)
{
rcLabel = rcItem;
//rcLabel.left += 4;
}
else if (bCheck && bLabel)
{
rcCheck = rcItem;
rcCheck.DeflateRect(1, 1);
rcCheck.left = rcItem.left + 6;
rcCheck.right = rcCheck.left + rcCheck.Height();
rcLabel = rcItem;
rcLabel.left = rcCheck.right + 4;
}
}
DWORD CExtListCtrl::_DrawText(CDC *pDC, int nItem, int nSubItem, CRect rcLabel, EXT_ITEM *pItem)
{
CString str = GetItemText(nItem, nSubItem);
if (str.IsEmpty())
return EXT_SUCCESS;
UINT nFMT = pItem->nFormat[nSubItem];
switch (nFMT)
{
case DT_LEFT:
rcLabel.left += 6; break;
case DT_CENTER:
rcLabel.left += 0; break;
case DT_RIGHT:
rcLabel.right -= 6; break;
}
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(_TXColorFor(nItem, nSubItem, pItem));
pDC->DrawText(str, rcLabel, DT_VCENTER|DT_SINGLELINE|nFMT);
return EXT_SUCCESS;
}
DWORD CExtListCtrl::_DrawCheckbox(CDC *pDC, int nItem, int nSubItem, CRect rcCheck, EXT_ITEM *pItem)
{
if (pItem->nChecked[nSubItem] == -1)
return EXT_SUCCESS;
pDC->Draw3dRect(rcCheck, GetSysColor(COLOR_3DDKSHADOW), GetSysColor(COLOR_3DDKSHADOW));
if (pItem->nChecked[nSubItem] == 0)
return EXT_SUCCESS;
CPen newPen(PS_SOLID, 1, RGB(0,0,0));
CPen *oldPen = pDC->SelectObject(&newPen);
for (int i = 0; i < 3; i++)
{
pDC->MoveTo(rcCheck.left + 2, rcCheck.top + 5 + i);
pDC->LineTo(rcCheck.left + 4, rcCheck.top + 7 + i);
pDC->LineTo(rcCheck.left + 10, rcCheck.top + 1 + i);
}
pDC->SelectObject(oldPen);
return EXT_SUCCESS;
}