www.pudn.com > ListView.rar > ReportCtrl.cpp
/////////////////////////////////////////////////////////////////////////////// // ReportCtrl.cpp // // CReportCtrl, a CListCtrl derived class that is specialized on "Report View" // style. // // Features: // // 1, Item sorting by clicking on column header. // 2, Sub-item text edit. // 3, Item repositioning. // 4, Customizing checkbox styles, including "single" and "disabled". // 5, Sending a message to parent window when user clicks on a checkbox. // 6, Convenient item insertion, deletion, moving, and sub-item text changing. // 7, Sub-item images and color // 8, And much more... // // This code may be used in compiled form in any way you desire. This file may be // redistributed unmodified by any means PROVIDING it is not sold for profit without // the authors written consent, and providing that this notice and the authors name // is included. If the source code in this file is used in any commercial application // then acknowledgement must be made to the author of this file . // // This file is provided "as is" with no expressed or implied warranty. // // Written by Bin Liu (abinn32@yahoo.com) // // History // // Nov. 26, 2003 - Initial release. // Dec. 03, 2003 - Fixed a bug in "EndEdit" where item text were not preperly committed. // - Completed the implementation of the "Sort-Separator" feature. // Jan. 01, 2004 - Fixed a bug in "SetItemData". // - Added message "WM_EDIT_COMMITTED" which is sent to the parent window // when an item text editing is committed. // - Fixed a bug in "SetItemText"(double type). // - Fixed a bug where item sorting does not work properly when there // are multiple CReportCtrl objects on same window. // /////////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "ReportCtrl.h" #include#ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // Below styles MUST be present in a report ctrl #define MUST_STYLE (WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | LVS_REPORT) #define MUST_EX_STYLE (LVS_EX_FULLROWSELECT | LVS_EX_SUBITEMIMAGES) // Below styles MUST NOT be present in a report ctrl #define MUST_NOT_STYLE (LVS_EDITLABELS | LVS_ICON | LVS_SMALLICON | LVS_LIST | LVS_NOSCROLL) #define MUST_NOT_EX_STYLE (0) struct ROWINFO { DWORD dwData; DWORD dwStates; CArray aImages; CStringArray aTexts; }; ////////////////////////////////////////////////////////////////////////// // CItemData class is used for store extra information ////////////////////////////////////////////////////////////////////////// class CItemData { public: CItemData() { dwData = 0; } void InsertColumn(int nColumn); void DeleteColumn(int nColumn); DWORD dwData; // The actual 32-bit user data stores here CArray aTextColors; // Sub item text colors CArray aBkColors; // Sub item backgroud colors }; void CItemData::InsertColumn(int nColumn) { aTextColors.InsertAt(nColumn, ::GetSysColor(COLOR_WINDOWTEXT)); aBkColors.InsertAt(nColumn, ::GetSysColor(COLOR_WINDOW)); } void CItemData::DeleteColumn(int nColumn) { aTextColors.RemoveAt(nColumn); aBkColors.RemoveAt(nColumn); } /////////////////////////////////////////////////////////////////////// // A set of functions used for item text format determining /////////////////////////////////////////////////////////////////////// namespace _ITEM_COMPARE_FUNCS { BOOL _IsDecNumber(const CString& str, double& f); int _DecNumberCompare(double f1, double f2); BOOL _IsHexNumber(const CString& str, DWORD& dw); int _HexNumberCompare(DWORD dw1, DWORD dw2); BOOL _IsDate(const CString& str, COleDateTime& date); int _DateCompare(const COleDateTime& date1, const COleDateTime& date2); }; BOOL _ITEM_COMPARE_FUNCS::_IsDecNumber(const CString& str, double& f) { if (str.IsEmpty()) return FALSE; LPTSTR p; f = _tcstod(str, &p); return (*p == _T('\0') || (*p == _T('%') && p[1] == _T('\0'))); } int _ITEM_COMPARE_FUNCS::_DecNumberCompare(double f1, double f2) { if(f1 < f2) return -1; if(f1 > f2) return 1; return 0; } BOOL _ITEM_COMPARE_FUNCS::_IsHexNumber(const CString& str, DWORD& dw) { if (str.IsEmpty()) return FALSE; LPTSTR p; dw = _tcstoul(str, &p, 16); return *p == _T('\0'); } int _ITEM_COMPARE_FUNCS::_HexNumberCompare(DWORD dw1, DWORD dw2) { if (dw1 > dw2) return 1; if (dw1 < dw2) return -1; return 0; } BOOL _ITEM_COMPARE_FUNCS::_IsDate(const CString& str, COleDateTime& date) { return date.ParseDateTime(str); } int _ITEM_COMPARE_FUNCS::_DateCompare(const COleDateTime& date1, const COleDateTime& date2) { if (date1 < date2) return -1; if (date1 > date2) return 1; return 0; } ///////////////////////////////////////////////////////////////////////////// // CReportCtrl Implementation ///////////////////////////////////////////////////////////////////////////// CReportCtrl::CReportCtrl(): m_pWndEdit(NULL) { m_pWndEdit = new CEdit; VERIFY(m_pWndEdit != NULL); m_pszSeparator = NULL; m_bAllowEdit = FALSE; m_ptEditting.x = -1; m_ptEditting.y = -1; m_nChkStyle = RC_CHKBOX_NORMAL; m_dwPrevEditFmt = ES_LEFT; m_nSortCol = -1; m_bSortAscending = TRUE; } CReportCtrl::~CReportCtrl() { if (m_pszSeparator != NULL) delete [] m_pszSeparator; if (m_pWndEdit != NULL) delete m_pWndEdit; } BEGIN_MESSAGE_MAP(CReportCtrl, CListCtrl) //{{AFX_MSG_MAP(CReportCtrl) ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnclick) ON_WM_LBUTTONDOWN() ON_WM_CREATE() ON_WM_DESTROY() ON_WM_LBUTTONDBLCLK() ON_WM_MBUTTONDOWN() ON_WM_MBUTTONDBLCLK() ON_WM_RBUTTONDOWN() ON_WM_RBUTTONDBLCLK() ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CReportCtrl message handlers void CReportCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) { LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)pNMHDR; if (lplvcd->nmcd.dwDrawStage == CDDS_PREPAINT) { *pResult = CDRF_NOTIFYITEMDRAW; } else if (lplvcd->nmcd.dwDrawStage == CDDS_ITEMPREPAINT) { *pResult = CDRF_NOTIFYSUBITEMDRAW; } else if (lplvcd->nmcd.dwDrawStage == (CDDS_ITEMPREPAINT | CDDS_SUBITEM)) { CItemData* p = (CItemData*)(CListCtrl::GetItemData(lplvcd->nmcd.dwItemSpec)); ASSERT(p != NULL); ASSERT(lplvcd->iSubItem >= 0 && lplvcd->iSubItem < p->aTextColors.GetSize()); lplvcd->clrText = p->aTextColors[lplvcd->iSubItem]; lplvcd->clrTextBk = p->aBkColors[lplvcd->iSubItem]; *pResult = CDRF_DODEFAULT; } } void CReportCtrl::OnColumnclick(NMHDR* pNMHDR, LRESULT* pResult) { NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; // TODO: Add your control notification handler code here const int COL = pNMListView->iSubItem; SortItems(COL, COL == m_nSortCol ? !m_bSortAscending : TRUE); *pResult = 0; } void CReportCtrl::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default _MouseClkMonitor(WM_LBUTTONDOWN, nFlags, point, TRUE); } void CReportCtrl::OnLButtonDblClk(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default _MouseClkMonitor(WM_LBUTTONDBLCLK, nFlags, point, TRUE); } void CReportCtrl::OnMButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default _MouseClkMonitor(WM_MBUTTONDOWN, nFlags, point, FALSE); } void CReportCtrl::OnMButtonDblClk(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default _MouseClkMonitor(WM_MBUTTONDBLCLK, nFlags, point, FALSE); } void CReportCtrl::OnRButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default _MouseClkMonitor(WM_RBUTTONDOWN, nFlags, point, FALSE); } void CReportCtrl::OnRButtonDblClk(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default _MouseClkMonitor(WM_RBUTTONDBLCLK, nFlags, point, FALSE); } int CReportCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) { lpCreateStruct->style &= ~MUST_NOT_STYLE; lpCreateStruct->style |= MUST_STYLE; if (CListCtrl::OnCreate(lpCreateStruct) == -1) return -1; // TODO: Add your specialized creation code here SetExtendedStyle(GetExtendedStyle()); ASSERT(GetHeaderCtrl() != NULL); return 0; } void CReportCtrl::OnDestroy() { DeleteAllItems(); m_pWndEdit->DestroyWindow(); m_imgList.DeleteImageList(); m_headerImgList.DeleteImageList(); CListCtrl::OnDestroy(); // TODO: Add your message handler code here } BOOL CReportCtrl::_IsValidIndex(int nIndex) const { return nIndex >= 0 && nIndex < CListCtrl::GetItemCount(); } // the heading text is in the format of "text,width,format;text,width,format;..." BOOL CReportCtrl::SetColumnHeader(const CString& strHeadings) { DeleteAllItems(); DeleteAllColumns(); EndEdit(TRUE); BOOL bInserted = FALSE; CStringArray aLong, aShort; _StringSplit(strHeadings, aLong, _T(';')); for (int i = 0; i < aLong.GetSize(); i++) { _StringSplit(aLong[i], aShort, _T(',')); if (aShort.GetSize() > 0) { const int WIDTH = aShort.GetSize() > 1 ? _ttoi(aShort[1]) : 100; int nFormat = aShort.GetSize() > 2 ? _ttoi(aShort[2]) : 0; if (nFormat == 1) nFormat = LVCFMT_CENTER; else if (nFormat == 2) nFormat = LVCFMT_RIGHT; else nFormat = LVCFMT_LEFT; bInserted |= (InsertColumn(GetColumnCount(), aShort[0], nFormat, WIDTH) >= 0); } } return bInserted; } int CReportCtrl::InsertItem(int nIndex, LPCTSTR lpText) { EndEdit(TRUE); _UnsetSortedColumn(); const int IDX = CListCtrl::InsertItem(nIndex, lpText); if (IDX >= 0) _AllocItemMemory(IDX); return IDX; } BOOL CReportCtrl::DeleteItem(int nItem, BOOL bSelectNextItem) { EndEdit(m_ptEditting.x != nItem); if (bSelectNextItem) SetItemStates(nItem + 1, RC_ITEM_SELECTED); _FreeItemMemory(nItem); return CListCtrl::DeleteItem(nItem); } int CReportCtrl::DeleteAllItems(DWORD dwStates) { EndEdit(FALSE); int nItemCount = CListCtrl::GetItemCount(); if (dwStates & RC_ITEM_ALL) { LockWindowUpdate(); for (int i = 0; i < nItemCount; i++) _FreeItemMemory(i); CListCtrl::DeleteAllItems(); UnlockWindowUpdate(); return nItemCount; } int nDelCount = 0; LockWindowUpdate(); for (int i = 0; i < nItemCount; i++) { if (ExamItemStates(i, dwStates)) { DeleteItem(i--); nItemCount--; nDelCount++; } } UnlockWindowUpdate(); return nDelCount; } void CReportCtrl::SortItems(int nColumn, BOOL bAscending) { EndEdit(TRUE); if (nColumn < 0 || nColumn >= GetColumnCount() || !IsSortable()) return; // do the sorting m_nSortCol = nColumn; m_bSortAscending = bAscending; BOOL bEnd = FALSE; int nSep1 = -1; int nSep2 = _FindSeparator(-1, nColumn); do { if (nSep2 < 0) { nSep2 = GetItemCount(); bEnd = TRUE; } _PartialSort(nSep1 + 1, nSep2 - 1); nSep1 = _FindSeparator(nSep2 - 1, nColumn); nSep2 = _FindSeparator(nSep1, nColumn); } while (!bEnd && nSep1 >= 0); GetParent()->SendMessage(WM_ITEM_SORTED, (WPARAM)m_nSortCol, (LPARAM)m_bSortAscending); } BOOL CReportCtrl::SetItemText(int nItem, int nSubItem, LPCTSTR lpText) { EndEdit(TRUE); _UnsetSortedColumn(); return CListCtrl::SetItemText(nItem, nSubItem, lpText); } int CReportCtrl::GetColumnCount() const { return GetHeaderCtrl()->GetItemCount(); } int CReportCtrl::InsertItem(const LVITEM *pItem) { EndEdit(TRUE); _UnsetSortedColumn(); const int IDX = CListCtrl::InsertItem(pItem); if (IDX >= 0) _AllocItemMemory(IDX); return IDX; } int CReportCtrl::InsertItem(int nItem, LPCTSTR lpItem, int nImage) { EndEdit(TRUE); _UnsetSortedColumn(); const int IDX = CListCtrl::InsertItem(nItem, lpItem, nImage); if (IDX >= 0) _AllocItemMemory(IDX); return IDX; } int CReportCtrl::InsertItem(UINT nMask, int nItem, LPCTSTR lpItem, UINT nState, UINT nStateMask, int nImage, LPARAM lParam) { EndEdit(TRUE); _UnsetSortedColumn(); const int IDX = CListCtrl::InsertItem(nMask, nItem, lpItem, nState, nStateMask, nImage, lParam); if (IDX >= 0) _AllocItemMemory(IDX); return IDX; } void CReportCtrl::SetGridLines(BOOL bSet) { DWORD dwStype = GetExtendedStyle(); if (bSet) dwStype |= LVS_EX_GRIDLINES; else dwStype &= ~LVS_EX_GRIDLINES; SetExtendedStyle(dwStype); } int CReportCtrl::InsertColumn(int nCol, const LVCOLUMN *pColumn) { EndEdit(TRUE); const int IDX = CListCtrl::InsertColumn(nCol, pColumn); if (IDX >= 0) _UpdateColumn(IDX, TRUE); return IDX; } int CReportCtrl::InsertColumn(int nCol, LPCTSTR lpColumnHeading, int nFormat, int nWidth, int nSubItem) { EndEdit(TRUE); const int IDX = CListCtrl::InsertColumn(nCol, lpColumnHeading, nFormat, nWidth, nSubItem); if (IDX >= 0) _UpdateColumn(IDX, TRUE); return IDX; } BOOL CReportCtrl::DeleteColumn(int nCol) { EndEdit(TRUE); const BOOL RES = CListCtrl::DeleteColumn(nCol); if (RES) _UpdateColumn(nCol, FALSE); return RES; } void CReportCtrl::SetCheckboxeStyle(int nStyle) { m_nChkStyle = nStyle; EndEdit(TRUE); DWORD dwStype = GetExtendedStyle(); if (nStyle == 0) { dwStype &= ~LVS_EX_CHECKBOXES; } else { dwStype |= LVS_EX_CHECKBOXES; _EnsureSingleCheck(-1); } SetExtendedStyle(dwStype); } int CReportCtrl::MoveTo(int nItem, int nNewPosition) { if (!_IsValidIndex(nItem)) return -1; EndEdit(TRUE); const int ITEMS = CListCtrl::GetItemCount(); nNewPosition = max(0, nNewPosition); nNewPosition = min(ITEMS - 1, nNewPosition); if (nItem == nNewPosition) return nNewPosition; _UnsetSortedColumn(); // Backup all states and attributes const int COLS = GetColumnCount(); const DWORD STATES = GetItemStates(nItem); const DWORD DATA = CListCtrl::GetItemData(nItem); CArray aImages; CStringArray aTexts; aImages.SetSize(COLS); aTexts.SetSize(COLS); for (int i = 0; i < COLS; i++) { aImages[i] = GetItemImage(nItem, i); aTexts[i] = GetItemText(nItem, i); } // Delete the item CListCtrl::DeleteItem(nItem); // Insert a new item to the new position const int IDX = CListCtrl::InsertItem(nNewPosition, ""); // Restore all states & attributes to the newly inserted item for (int j = 0; j < COLS; j++) { CListCtrl::SetItemText(IDX, j, aTexts[j]); SetItemImage(IDX, j, aImages[j]); } CListCtrl::SetItemData(IDX, DATA); SetItemStates(IDX, STATES); return IDX; } int CReportCtrl::MoveUp(int nItem, int nCount) { return MoveTo(nItem, nItem - nCount); } BOOL CReportCtrl::MoveDown(int nItem, int nCount) { return MoveTo(nItem, nItem + nCount); } BOOL CReportCtrl::SwapItems(int nItem1, int nItem2) { EndEdit(TRUE); if (!_IsValidIndex(nItem1) || !_IsValidIndex(nItem2)) return FALSE; if (nItem1 == nItem2) return TRUE; _UnsetSortedColumn(); // record previous states first const DWORD STATES1 = GetItemStates(nItem1); const DWORD STATES2 = GetItemStates(nItem2); const DWORD DATA1 = CListCtrl::GetItemData(nItem1); const DWORD DATA2 = CListCtrl::GetItemData(nItem2); // swap item texts and images for (int i = 0; i < GetColumnCount(); i++) { CString str = GetItemText(nItem1, i); CListCtrl::SetItemText(nItem1, i, GetItemText(nItem2, i)); CListCtrl::SetItemText(nItem2, i, str); UINT nImg = GetItemImage(nItem1, i); SetItemImage(nItem1, i, GetItemImage(nItem2, i)); SetItemImage(nItem2, i, nImg); } // swap item data CListCtrl::SetItemData(nItem1, DATA2); CListCtrl::SetItemData(nItem2, DATA1); // restore states SetItemStates(nItem1, STATES2); SetItemStates(nItem2, STATES1); return TRUE; } int CReportCtrl::MoveToTop(int nItem) { return MoveTo(nItem, -1); } int CReportCtrl::MoveToBottom(int nItem) { return MoveTo(nItem, INT_MAX); } BOOL CReportCtrl::SetItemText(int nItem, int nSubItem, TCHAR val) { return SetItemText(nItem, nSubItem, CString(val)); } BOOL CReportCtrl::SetItemText(int nItem, int nSubItem, INT val) { return SetItemText(nItem, nSubItem, (LONG)val); } BOOL CReportCtrl::SetItemText(int nItem, int nSubItem, UINT val) { return SetItemText(nItem, nSubItem, (ULONG)val); } BOOL CReportCtrl::SetItemText(int nItem, int nSubItem, DOUBLE val, int nPrecision) { CString sText, sFmt; if (nPrecision >= 0) { sFmt.Format(_T("%%.%df"), nPrecision); sText.Format(sFmt, val); } else { sText.Format(_T("%f"), val); sText.TrimRight(_T('0')); sText.TrimRight(_T('.')); } return SetItemText(nItem, nSubItem, sText); } BOOL CReportCtrl::SetItemText(int nItem, int nSubItem, const COleDateTime &dateTime, DWORD dwFlags) { return SetItemText(nItem, nSubItem, dateTime.GetStatus() == COleDateTime::valid ? dateTime.Format(dwFlags) : _T("")); } BOOL CReportCtrl::SetItemText(int nItem, int nSubItem, LONG val) { CString str; str.Format(_T("%d"), val); return SetItemText(nItem, nSubItem, str); } BOOL CReportCtrl::SetItemText(int nItem, int nSubItem, ULONG val) { CString str; str.Format(_T("%u"), val); return SetItemText(nItem, nSubItem, str); } BOOL CReportCtrl::DeleteAllColumns() { while (GetColumnCount() > 0) DeleteColumn(0); return TRUE; } LPCTSTR CReportCtrl::GetSortSeparator() const { return m_pszSeparator; } void CReportCtrl::SetSortSeparator(LPCTSTR lpSortSeparator) { if (m_pszSeparator != NULL) { delete [] m_pszSeparator; m_pszSeparator = NULL; } if (lpSortSeparator != NULL) m_pszSeparator = _tcsdup(lpSortSeparator); } int CReportCtrl::InsertItemEx(int nItem, LPCTSTR lpText, ...) { EndEdit(TRUE); _UnsetSortedColumn(); const int IDX = CListCtrl::InsertItem(nItem, lpText); if (IDX < 0) return -1; va_list list; va_start(list, lpText); for(int iColumn = 1; iColumn < GetColumnCount(); iColumn++) { LPCTSTR lp = va_arg(list, LPCTSTR); if (lp != NULL) { CListCtrl::SetItemText(IDX, iColumn, lp); } else { break; } } va_end(list); if (IDX >= 0) _AllocItemMemory(IDX); return IDX; } BOOL CReportCtrl::_IsEditVisible() const { return m_bAllowEdit && m_ptEditting.x >= 0 && m_ptEditting.x < CListCtrl::GetItemCount() && m_ptEditting.y >= 0 && m_ptEditting.y < GetColumnCount(); } void CReportCtrl::SetEditable(BOOL bSet) { if (!bSet) EndEdit(TRUE); m_bAllowEdit = bSet; } BOOL CReportCtrl::IsEditable() const { return m_bAllowEdit; } BOOL CReportCtrl::_ItemCheckMonitor(int nIndex, BOOL bBefore, BOOL bAfter, UINT nMsg) { if (!_IsValidIndex(nIndex) || m_nChkStyle == RC_CHKBOX_NONE || (bBefore && bAfter) || (!bBefore && !bAfter)) { return FALSE; } if (m_nChkStyle == RC_CHKBOX_SINGLE) { // Only one item can be checked if (!bBefore && bAfter) { SetAllItemStates(RC_ITEM_CHECKED, RC_ITEM_UNCHECKED); // uncheck all SetItemStates(nIndex, RC_ITEM_CHECKED); } } else if (m_nChkStyle == RC_CHKBOX_DISABLED) { // Cannot change the item checked/unchecked states by mouse clicks SetItemStates(nIndex, bBefore ? RC_ITEM_CHECKED : RC_ITEM_UNCHECKED); return TRUE; } SetAllItemStates(RC_ITEM_SELECTED, RC_ITEM_UNSELECTED); // unselect all SetItemStates(nIndex, RC_ITEM_SELECTED | RC_ITEM_FOCUSED); GetParent()->SendMessage(WM_ON_CHKBOX, (WPARAM)nIndex, (LPARAM)nMsg); return TRUE; } int CReportCtrl::GetCheckboxStyle() const { return m_nChkStyle; } void CReportCtrl::_StringSplit(const CString &str, CStringArray &arr, TCHAR chDelimitior) { int nStart = 0, nEnd = 0; arr.RemoveAll(); while (nEnd < str.GetLength()) { // determine the paragraph ("xxx,xxx,xxx;") nEnd = str.Find(chDelimitior, nStart); if( nEnd == -1 ) { // reached the end of string nEnd = str.GetLength(); } CString s = str.Mid(nStart, nEnd - nStart); if (!s.IsEmpty()) arr.Add(s); nStart = nEnd + 1; } } BOOL CReportCtrl::SetSortable(BOOL bSet) { if (!HasColumnHeader()) return FALSE; LONG lStyle = ::GetWindowLong(GetHeaderCtrl()->GetSafeHwnd(), GWL_STYLE); if (bSet) { lStyle |= HDS_BUTTONS; } else { lStyle &= ~HDS_BUTTONS; _UnsetSortedColumn(); } ::SetWindowLong(GetHeaderCtrl()->GetSafeHwnd(), GWL_STYLE, lStyle); CListCtrl::GetHeaderCtrl()->RedrawWindow(); return TRUE; } DWORD CReportCtrl::SetExtendedStyle(DWORD dwNewStyle) { dwNewStyle &= ~MUST_NOT_EX_STYLE; dwNewStyle |= MUST_EX_STYLE; return CListCtrl::SetExtendedStyle(dwNewStyle); } BOOL CReportCtrl::PreCreateWindow(CREATESTRUCT& cs) { // TODO: Add your specialized code here and/or call the base class cs.style &= ~MUST_NOT_STYLE; cs.style |= MUST_STYLE; return CListCtrl::PreCreateWindow(cs); } BOOL CReportCtrl::PreTranslateMessage(MSG* pMsg) { // TODO: Add your specialized code here and/or call the base class if (pMsg->message == WM_KEYDOWN) { if (!_IsEditVisible()) { // disable user from check/uncheck the checkboxes using space key // things get nasty if the user is HOLDING the space bar down if (pMsg->wParam == VK_SPACE) return TRUE; } else { POINT pt = m_ptEditting; switch (pMsg->wParam) { case VK_ESCAPE: // Cancel edit EndEdit(FALSE); return TRUE; case VK_RETURN: // Commit edit EndEdit(TRUE); return TRUE; case VK_TAB: // switch edit sub items if (pt.y == GetColumnCount() - 1) pt.y = 0; else pt.y++; EndEdit(TRUE); StartEdit(pt.x, pt.y); return TRUE; case VK_UP: // edit upper item if (pt.x > 0) { pt.x--; EndEdit(TRUE); StartEdit(pt.x, pt.y); EnsureVisible(pt.x, FALSE); return TRUE; } break; case VK_DOWN: // edit lower item if (pt.x < CListCtrl::GetItemCount() - 1) { pt.x++; EndEdit(TRUE); StartEdit(pt.x, pt.y); EnsureVisible(pt.x, FALSE); return TRUE; } break; default: break; } } } return CListCtrl::PreTranslateMessage(pMsg); } CEdit* CReportCtrl::GetEditControl() { return m_pWndEdit; } BOOL CReportCtrl::Create(CWnd* pParentWnd, UINT nID, LPCRECT lpRect, DWORD dwStyle) { // TODO: Add your specialized code here and/or call the base class ASSERT(pParentWnd != NULL); dwStyle &= ~MUST_NOT_STYLE; dwStyle |= MUST_STYLE; CRect rect; if (lpRect == NULL) pParentWnd->GetClientRect(&rect); else rect = *lpRect; return CListCtrl::Create(dwStyle, rect, pParentWnd, nID); } void CReportCtrl::PreSubclassWindow() { // TODO: Add your specialized code here and/or call the base class LONG lStyle = ::GetWindowLong(GetSafeHwnd(), GWL_STYLE); lStyle &= ~MUST_NOT_STYLE; lStyle |= MUST_STYLE; ::SetWindowLong(GetSafeHwnd(), GWL_STYLE, lStyle); SetExtendedStyle(0); ASSERT(GetHeaderCtrl() != NULL); CListCtrl::PreSubclassWindow(); } BOOL CReportCtrl::IsSortable() const { if (!HasColumnHeader()) return FALSE; LONG lStyle = ::GetWindowLong(GetHeaderCtrl()->GetSafeHwnd(), GWL_STYLE); return (lStyle & HDS_BUTTONS) != 0; } BOOL CReportCtrl::HasColumnHeader() const { return (GetStyle() & LVS_NOCOLUMNHEADER) == 0; } void CReportCtrl::ResizeToFitParent() { // resize the list ctrl to fit parent client area if (GetSafeHwnd() != NULL) { CRect rect; GetParent()->GetClientRect(&rect); MoveWindow(&rect); } } BOOL CReportCtrl::ModifyStyleEx(DWORD dwRemove, DWORD dwAdd, UINT nFlags) { dwRemove &= ~MUST_EX_STYLE; dwRemove |= MUST_NOT_EX_STYLE; dwAdd &= ~MUST_NOT_EX_STYLE; dwAdd |= MUST_EX_STYLE; return CListCtrl::ModifyStyleEx(dwRemove, dwAdd, nFlags); } BOOL CReportCtrl::ModifyStyle(DWORD dwRemove, DWORD dwAdd, UINT nFlags) { dwRemove &= ~MUST_STYLE; dwRemove |= MUST_NOT_STYLE; dwAdd &= ~MUST_NOT_STYLE; dwAdd |= MUST_STYLE; return CListCtrl::ModifyStyle(dwRemove, dwAdd, nFlags); } void CReportCtrl::_MouseClkMonitor(UINT nMsg, UINT nFlags, CPoint point, BOOL bTriggerEdit) { LVHITTESTINFO hti; hti.pt = point; const int IDX = SubItemHitTest(&hti); const BOOL BEFORE = CListCtrl::GetCheck(IDX) > 0; const BOOL WAS_EDIT = _IsEditVisible(); EndEdit(TRUE); const BOOL WASACTIVE = bTriggerEdit ? ExamItemStates(IDX, RC_ITEM_FOCUSED | RC_ITEM_SELECTED) : FALSE; 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; } const BOOL STATSCHANGED = _ItemCheckMonitor(IDX, BEFORE, CListCtrl::GetCheck(IDX) > 0, nMsg); if (bTriggerEdit && m_bAllowEdit && !STATSCHANGED && !WAS_EDIT && WASACTIVE) StartEdit(IDX, hti.iSubItem); } BOOL CReportCtrl::SetItemImage(int nItem, int nSubItem, int nImageIndex) { return CListCtrl::SetItem(nItem, nSubItem, LVIF_IMAGE, NULL, nImageIndex, 0, 0, 0); } int CReportCtrl::GetItemImage(int nItem, int nSubItem) const { LVITEM lvi; lvi.iItem = nItem; lvi.iSubItem = nSubItem; lvi.mask = LVIF_IMAGE; return CListCtrl::GetItem(&lvi) ? lvi.iImage : -1; } CImageList* CReportCtrl::SetImageList(CImageList *pImageList) { return CListCtrl::SetImageList(pImageList, LVSIL_SMALL); } CImageList* CReportCtrl::GetImageList() const { return CListCtrl::GetImageList(LVSIL_SMALL); } CImageList* CReportCtrl::SetImageList(UINT nBitmapID, COLORREF crMask) { m_imgList.DeleteImageList(); m_imgList.Create(nBitmapID, 16, 4, crMask); return CListCtrl::SetImageList(&m_imgList, LVSIL_SMALL); } BOOL CReportCtrl::StartEdit(int nItem, int nSubItem) { // Get the grid width and height if (!m_bAllowEdit || !_IsValidIndex(nItem) || nSubItem < 0 || nSubItem >= GetColumnCount()) return FALSE; if (m_ptEditting.x == nItem && m_ptEditting.y == nSubItem) return TRUE; EndEdit(TRUE); m_ptEditting.x = nItem; m_ptEditting.y = nSubItem; SetAllItemStates(RC_ITEM_SELECTED, RC_ITEM_UNSELECTED); // unselect all SetItemStates(m_ptEditting.x, RC_ITEM_SELECTED | RC_ITEM_FOCUSED); // determine editbox font and alignment const DWORD FMT = _GetHeaderTextFormat(nSubItem); if (FMT != m_dwPrevEditFmt) { m_dwPrevEditFmt = FMT; // Funny thing: // Changing CEdit style among ES_LEFT, ES_CENTER, ES_RIGHT at runtime works // sometimes and fails other times. It just cannot guarantee to be succeed. // So I decided to destroy and recreate the CEdit every time when the text // format changes. if (m_pWndEdit->GetSafeHwnd() != NULL) m_pWndEdit->DestroyWindow(); if (!m_pWndEdit->Create(ES_AUTOHSCROLL | ES_NOHIDESEL | WS_CHILD | WS_BORDER | FMT, CRect(0, 0, 1, 1), this, 0)) return FALSE; } else { if (m_pWndEdit->GetSafeHwnd() == NULL && !m_pWndEdit->Create(ES_AUTOHSCROLL | ES_NOHIDESEL | WS_CHILD | WS_BORDER | FMT, CRect(0, 0, 1, 1), this, 0)) { return FALSE; } } m_pWndEdit->SetFont(GetFont()); CRect rcEdit; ListView_GetSubItemRect(GetSafeHwnd(), m_ptEditting.x, m_ptEditting.y, LVIR_LABEL, &rcEdit); if (m_ptEditting.y > 0 && GetImageList() != NULL && GetItemImage(m_ptEditting.x, m_ptEditting.y) >= 0) rcEdit.DeflateRect(16, 0, 0, 0); // Move the editbox to that grid, obtain text from the grid, display the // editbox, and, finally, highlights all text in the editbox and set the // windows focus to the editbox. m_pWndEdit->MoveWindow(&rcEdit); m_pWndEdit->SetWindowText(GetItemText(m_ptEditting.x, m_ptEditting.y)); m_pWndEdit->ShowWindow(SW_SHOW); m_pWndEdit->SetSel(0, -1); m_pWndEdit->SetFocus(); return TRUE; } BOOL CReportCtrl::EndEdit(BOOL bCommit) { if (!_IsEditVisible()) return FALSE; CString str; m_pWndEdit->GetWindowText(str); BOOL bChanged = bCommit && str.Compare(GetItemText(m_ptEditting.x, m_ptEditting.y)) != 0; if (bChanged) { // update the list item CListCtrl::SetItemText(m_ptEditting.x, m_ptEditting.y, str); _UnsetSortedColumn(); GetParent()->SendMessage(WM_EDIT_COMMITTED, (WPARAM)m_ptEditting.x, (LPARAM)m_ptEditting.y); } m_pWndEdit->ShowWindow(SW_HIDE); m_ptEditting.x = -1; m_ptEditting.y = -1; return bChanged; } BOOL CReportCtrl::SetHeaderImage(int nColumn, int nImageIndex, BOOL bLeftSide) { if (GetHeaderCtrl()->GetImageList() == NULL) CListCtrl::GetHeaderCtrl()->SetImageList(GetImageList()); HDITEM hi; ::memset(&hi, 0, sizeof(HDITEM)); hi.mask = HDI_FORMAT; if (!GetHeaderCtrl()->GetItem(nColumn, &hi)) return FALSE; hi.mask |= HDI_IMAGE; hi.fmt |= HDF_IMAGE; if (!bLeftSide) hi.fmt |= HDF_BITMAP_ON_RIGHT; // draw the image on right side of text hi.iImage = nImageIndex; return CListCtrl::GetHeaderCtrl()->SetItem(nColumn, &hi); } int CReportCtrl::GetSortedColumn() const { return m_nSortCol; } BOOL CReportCtrl::IsSortAscending() const { return m_bSortAscending; } void CReportCtrl::_UnsetSortedColumn() { m_nSortCol = -1; m_bSortAscending = TRUE; } DWORD CReportCtrl::_GetHeaderTextFormat(int nColumn) const { if (!HasColumnHeader()) return ES_LEFT; HDITEM hd; hd.mask = HDI_FORMAT; if (!GetHeaderCtrl()->GetItem(nColumn, &hd)) return ES_LEFT; if (hd.fmt & HDF_CENTER) { return ES_CENTER; } else if (hd.fmt & HDF_RIGHT) { return ES_RIGHT; } else { return ES_LEFT; } } int CReportCtrl::GetHeaderImage(int nColumn) const { HDITEM hi; ::memset(&hi, 0, sizeof(HDITEM)); hi.mask = HDI_IMAGE; return !GetHeaderCtrl()->GetItem(nColumn, &hi) ? hi.iImage : -1; } CImageList* CReportCtrl::SetHeaderImageList(CImageList *pImageList) { return CListCtrl::GetHeaderCtrl()->SetImageList(pImageList); } CImageList* CReportCtrl::SetHeaderImageList(UINT nBitmapID, COLORREF crMask) { m_headerImgList.Create(nBitmapID, 16, 4, crMask); return SetHeaderImageList(&m_headerImgList); } BOOL CReportCtrl::ExamItemStates(int nItem, DWORD dwStates) const { if (dwStates == RC_ITEM_NONE) return FALSE; if (dwStates & RC_ITEM_ALL) return TRUE; return (GetItemStates(nItem) & dwStates) == dwStates; } int CReportCtrl::GetItemCount(DWORD dwStates) const { const int ITEMS = CListCtrl::GetItemCount(); if (dwStates == RC_ITEM_ALL) return ITEMS; int nCount = 0; for (int i = 0; i = 0; i--) { if (ExamItemStates(i, dwStates)) return i; } return -1; } void CReportCtrl::InvertItems(int nType) { const int ITEMS = CListCtrl::GetItemCount(); if (nType == RC_INVERT_SELECTION) { for (int i = 0; i < ITEMS; i++) SetItemStates(i, ExamItemStates(i, RC_ITEM_SELECTED) ? RC_ITEM_UNSELECTED : RC_ITEM_SELECTED); } else if (nType == RC_INVERT_CHECKMARK) { for (int i = 0; i < ITEMS; i++) SetItemStates(i, ExamItemStates(i, RC_ITEM_CHECKED) ? RC_ITEM_UNCHECKED : RC_ITEM_CHECKED); } } DWORD CReportCtrl::GetItemStates(int nItem) const { DWORD dwStates = 0; if (CListCtrl::GetItemState(nItem, LVIS_SELECTED)) dwStates |= RC_ITEM_SELECTED; else dwStates |= RC_ITEM_UNSELECTED; if (CListCtrl::GetCheck(nItem) > 0) dwStates |= RC_ITEM_CHECKED; else dwStates |= RC_ITEM_UNCHECKED; if (CListCtrl::GetItemState(nItem, LVIS_FOCUSED)) dwStates |= RC_ITEM_FOCUSED; else dwStates |= RC_ITEM_UNFOCUSED; return dwStates; } BOOL CReportCtrl::SetItemStates(int nItem, DWORD dwNewStates) { BOOL bResult = _SetItemStatesNoVerify(nItem, dwNewStates); if (dwNewStates & RC_ITEM_CHECKED) _EnsureSingleCheck(nItem); return bResult; } int CReportCtrl::SetAllItemStates(DWORD dwOldStates, DWORD dwNewStates) { if (dwNewStates == RC_ITEM_NONE || dwOldStates == RC_ITEM_NONE) return 0; int nCount = 0; const int ITEMS = CListCtrl::GetItemCount(); for (int i = 0; i < ITEMS; i++) { if (dwOldStates == RC_ITEM_ALL || ExamItemStates(i, dwOldStates)) { SetItemStates(i, dwNewStates); nCount++; } } if (dwNewStates & RC_ITEM_CHECKED) _EnsureSingleCheck(-1); return nCount; } void CReportCtrl::_EnsureSingleCheck(int nItem) { if (m_nChkStyle == RC_CHKBOX_SINGLE) { const int FIRST = nItem < 0 ? GetFirstItem(RC_ITEM_CHECKED) : nItem; if (FIRST >= 0) { const int ITEMS = CListCtrl::GetItemCount(); for (int i = FIRST + 1; i < ITEMS; i++) { if (CListCtrl::GetCheck(i)) CListCtrl::SetCheck(i, FALSE); } } } } BOOL CReportCtrl::_SetItemStatesNoVerify(int nItem, DWORD dwNewStates) { if (dwNewStates == RC_ITEM_NONE) return FALSE; BOOL bResult = TRUE; if (dwNewStates & RC_ITEM_SELECTED) bResult &= CListCtrl::SetItemState(nItem, LVIS_SELECTED, LVIS_SELECTED); if (dwNewStates & RC_ITEM_UNSELECTED) bResult &= CListCtrl::SetItemState(nItem, 0, LVIS_SELECTED); if (dwNewStates & RC_ITEM_CHECKED) bResult &= CListCtrl::SetCheck(nItem, TRUE); if (dwNewStates & RC_ITEM_UNCHECKED) bResult &= CListCtrl::SetCheck(nItem, FALSE); if (dwNewStates & RC_ITEM_FOCUSED) bResult &= CListCtrl::SetItemState(nItem, LVIS_FOCUSED, LVIS_FOCUSED); if (dwNewStates & RC_ITEM_UNFOCUSED) bResult &= CListCtrl::SetItemState(nItem, 0, LVIS_FOCUSED); return bResult; } BOOL CReportCtrl::SetCheck(int nItem, BOOL bCheck) { const BOOL RES = CListCtrl::SetCheck(nItem, bCheck); if (RES && bCheck) _EnsureSingleCheck(nItem); return RES; } CString CReportCtrl::GetHeaderText(int nColumn) const { if (!HasColumnHeader()) return _T(""); HDITEM hd; TCHAR szBuffer[256] = _T(""); hd.mask = HDI_TEXT; hd.pszText = szBuffer; hd.cchTextMax = 255; return GetHeaderCtrl()->GetItem(nColumn, &hd) ? hd.pszText : _T(""); } BOOL CReportCtrl::SetHeaderText(int nColumn, LPCTSTR lpText) { if (!HasColumnHeader()) return FALSE; LPTSTR psz = NULL; if (lpText == NULL) { psz = new TCHAR[1]; *psz = _T('\0'); } else { psz = _tcsdup(lpText); } HDITEM hd; hd.mask = HDI_TEXT; hd.pszText = psz; hd.cchTextMax = _tcslen(psz); const BOOL RES = CListCtrl::GetHeaderCtrl()->SetItem(nColumn, &hd); delete [] psz; return RES; } const CHeaderCtrl* CReportCtrl::GetHeaderCtrl() const { // Yes, CListCtrl already provides "GetHeaderCtrl", but not const. // I desperately need a "const" version of "GetHeaderCtrl" because that's the // only way to make "GetColumnCount" const, which, in turn, is being called by // A LOT of const member functions. So if "GetHeaderCtrl" is not const, there will // be almost no const member function at all in this class. Terrible. return (const CHeaderCtrl*)(CWnd::FromHandle(ListView_GetHeader(GetSafeHwnd()))); } void CReportCtrl::_FreeItemMemory(int nItem) { CItemData* p = (CItemData*)(CListCtrl::GetItemData(nItem)); if (p != NULL) { CListCtrl::SetItemData(nItem, 0); delete p; } } void CReportCtrl::_AllocItemMemory(int nItem) { ASSERT(_IsValidIndex(nItem)); const int COLS = GetColumnCount(); ASSERT(COLS > 0); CItemData* pData = new CItemData; pData->dwData = CListCtrl::GetItemData(nItem); pData->aTextColors.SetSize(COLS); pData->aBkColors.SetSize(COLS); for (int i = 0; i < COLS; i++) { pData->aTextColors[i] = ::GetSysColor(COLOR_WINDOWTEXT); pData->aBkColors[i] = ::GetSysColor(COLOR_WINDOW); } CListCtrl::SetItemData(nItem, (DWORD)pData); } void CReportCtrl::_UpdateColumn(int nColumn, BOOL bInsert) { const int ITEMS = GetItemCount(); for (int i = 0; i < ITEMS; i++) { CItemData* p = (CItemData*)(CListCtrl::GetItemData(i)); ASSERT(p != NULL); if (bInsert) p->InsertColumn(nColumn); else p->DeleteColumn(nColumn); } } BOOL CReportCtrl::SetItemData(int nItem, DWORD dwData) { CItemData* p = (CItemData*)(CListCtrl::GetItemData(nItem)); if (p == NULL) return FALSE; p->dwData = dwData; return CListCtrl::SetItemData(nItem, (DWORD)p); } DWORD CReportCtrl::GetItemData(int nItem) const { CItemData* p = (CItemData*)(CListCtrl::GetItemData(nItem)); return p == NULL ? 0 : p->dwData; } BOOL CReportCtrl::GetItem(LVITEM *pItem) const { const BOOL RES = CListCtrl::GetItem(pItem); if (RES && (pItem->mask & LVIF_PARAM) != 0) { CItemData* p = (CItemData*)(pItem->lParam); if (p != NULL) pItem->lParam = p->dwData; } return RES; } BOOL CReportCtrl::SetItem(const LVITEM *pItem) { if (pItem == NULL) return FALSE; LVITEM li; memcpy(&li, pItem, sizeof(LVITEM)); if ((li.mask & LVIF_PARAM) != 0) { CItemData* p = (CItemData*)(CListCtrl::GetItemData(li.iItem)); if (p != NULL) { p->dwData = li.lParam; li.lParam = (LPARAM)p; } } return CListCtrl::SetItem(&li); } BOOL CReportCtrl::SetItem(int nItem, int nSubItem, UINT nMask, LPCTSTR lpszItem, int nImage, UINT nState, UINT nStateMask, LPARAM lParam) { if ((nMask & LVIF_PARAM) != 0) { CItemData* p = (CItemData*)(CListCtrl::GetItemData(nItem)); if (p != NULL) { p->dwData = lParam; lParam = (LPARAM)p; } } return CListCtrl::SetItem(nItem, nSubItem, nMask, lpszItem, nImage, nState, nStateMask, lParam); } void CReportCtrl::SetItemTextColor(int nItem, int nSubItem, COLORREF color, BOOL bRedraw) { if (color == COLOR_INVALID) color = ::GetSysColor(COLOR_WINDOWTEXT); const int ROWS = GetItemCount(); const int COLS = GetColumnCount(); BOOL bRowValid = nItem >= 0 && nItem < ROWS; BOOL bColValid = nSubItem >= 0 && nSubItem < COLS; if (bRowValid && bColValid) { // apply to individual grid CItemData* p = (CItemData*)(CListCtrl::GetItemData(nItem)); ASSERT(p != NULL); p->aTextColors[nSubItem] = color; } else if (bRowValid && !bColValid) { // apply to whole row for the existing item CItemData* p = (CItemData*)(CListCtrl::GetItemData(nItem)); ASSERT(p != NULL); for (int i = 0; i < COLS; i++) p->aTextColors[i] = color; } else if (!bRowValid && bColValid) { // apply to whole column for all existing items for (int i = 0; i < ROWS; i++) { CItemData* p = (CItemData*)(CListCtrl::GetItemData(i)); ASSERT(p != NULL); p->aTextColors[nSubItem] = color; } } else { // apply to whole table for all existing items for (int i = 0; i < ROWS; i++) { CItemData* p = (CItemData*)(CListCtrl::GetItemData(i)); ASSERT(p != NULL); for (int j = 0; j < COLS; j++) p->aTextColors[j] = color; } } if (bRedraw) RedrawWindow(); } void CReportCtrl::SetItemBkColor(int nItem, int nSubItem, COLORREF color, BOOL bRedraw) { if (color == COLOR_INVALID) color = ::GetSysColor(COLOR_WINDOW); const int ROWS = GetItemCount(); const int COLS = GetColumnCount(); BOOL bRowValid = nItem >= 0 && nItem < ROWS; BOOL bColValid = nSubItem >= 0 && nSubItem < COLS; if (bRowValid && bColValid) { // apply to individual grid CItemData* p = (CItemData*)(CListCtrl::GetItemData(nItem)); ASSERT(p != NULL); p->aBkColors[nSubItem] = color; } else if (bRowValid && !bColValid) { // apply to whole row for the existing item CItemData* p = (CItemData*)(CListCtrl::GetItemData(nItem)); ASSERT(p != NULL); for (int i = 0; i < COLS; i++) p->aBkColors[i] = color; } else if (!bRowValid && bColValid) { // apply to whole column for all existing items for (int i = 0; i < ROWS; i++) { CItemData* p = (CItemData*)(CListCtrl::GetItemData(i)); ASSERT(p != NULL); p->aBkColors[nSubItem] = color; } } else { // apply to whole table for all existing items for (int i = 0; i < ROWS; i++) { CItemData* p = (CItemData*)(CListCtrl::GetItemData(i)); ASSERT(p != NULL); for (int j = 0; j < COLS; j++) p->aBkColors[j] = color; } } if (bRedraw) RedrawWindow(); } COLORREF CReportCtrl::GetItemTextColor(int nItem, int nSubItem) const { if (!_IsValidIndex(nItem) || nSubItem < 0 || nSubItem >= GetColumnCount()) return COLOR_INVALID; CItemData* p = (CItemData*)(CListCtrl::GetItemData(nItem)); ASSERT(p != NULL); return p->aTextColors[nSubItem]; } COLORREF CReportCtrl::GetItemBkColor(int nItem, int nSubItem) const { if (!_IsValidIndex(nItem) || nSubItem < 0 || nSubItem >= GetColumnCount()) return COLOR_INVALID; CItemData* p = (CItemData*)(CListCtrl::GetItemData(nItem)); ASSERT(p != NULL); return p->aBkColors[nSubItem]; } BOOL CReportCtrl::_PartialSort(int nStart, int nEnd) { if (nStart >= nEnd || !_IsValidIndex(nStart) || !_IsValidIndex(nEnd)) return FALSE; const int COUNT = nEnd - nStart + 1; int i = 0; int* aIndices = new int[COUNT]; for (i = 0; i < COUNT; i++) aIndices[i] = nStart + i; _QuickSortRecursive(aIndices, 0, COUNT - 1); // rearrange items const int COLS = GetColumnCount(); ROWINFO* aRows = new ROWINFO[COUNT]; for (i = 0; i < COUNT; i++) { int n = aIndices[i]; aRows[i].dwStates = GetItemStates(aIndices[i]); aRows[i].dwData = CListCtrl::GetItemData(aIndices[i]); aRows[i].aImages.SetSize(COLS); aRows[i].aTexts.SetSize(COLS); for (int j = 0; j < COLS; j++) { aRows[i].aImages[j] = GetItemImage(aIndices[i], j); aRows[i].aTexts[j] = GetItemText(aIndices[i], j); } } for (i = 0; i < COUNT; i++) { SetItemStates(nStart + i, aRows[i].dwStates); CListCtrl::SetItemData(nStart + i, aRows[i].dwData); for (int j = 0; j < COLS; j++) { SetItemImage(nStart + i, j, aRows[i].aImages[j]); CListCtrl::SetItemText(nStart + i, j, aRows[i].aTexts[j]); } } delete [] aRows; delete [] aIndices; return TRUE; } int CReportCtrl::_CompareItems(int nItem1, int nItem2) { const CString s1 = GetItemText(nItem1, m_nSortCol); const CString s2 = GetItemText(nItem2, m_nSortCol); DWORD dw1, dw2; if(_ITEM_COMPARE_FUNCS::_IsHexNumber(s1, dw1) && _ITEM_COMPARE_FUNCS::_IsHexNumber(s2, dw2)) return _ITEM_COMPARE_FUNCS::_HexNumberCompare(dw1, dw2); double f1, f2; if(_ITEM_COMPARE_FUNCS::_IsDecNumber(s1, f1) && _ITEM_COMPARE_FUNCS::_IsDecNumber(s2, f2)) return _ITEM_COMPARE_FUNCS::_DecNumberCompare(f1, f2); COleDateTime date1, date2; if(_ITEM_COMPARE_FUNCS::_IsDate(s1, date1) && _ITEM_COMPARE_FUNCS::_IsDate(s2, date2)) return _ITEM_COMPARE_FUNCS::_DateCompare(date1, date2); // plain text. return s1.Compare(s2); } int CReportCtrl::_FindSeparator(int nStartAfter, int nColumn) const { if (m_pszSeparator == NULL) return -1; const int ITEMS = GetItemCount(); for (int i = nStartAfter + 1; i < ITEMS; i++) { if (GetItemText(i, nColumn) == m_pszSeparator) return i; } return -1; } void CReportCtrl::_QuickSortRecursive(int* pArr, int nLow, int nHigh) { int i = nHigh, j = nLow; int n = pArr[(nLow + nHigh) / 2]; do { if (m_bSortAscending) { while (_CompareItems(pArr[j], n) < 0) j++; while (_CompareItems(pArr[i], n) > 0) i--; } else { while (_CompareItems(pArr[j], n) > 0) j++; while (_CompareItems(pArr[i], n) < 0) i--; } if ( i >= j ) { if ( i != j ) { int nTemp = pArr[i]; pArr[i] = pArr[j]; pArr[j] = nTemp; } i--; j++; } } while (j <= i); if (nLow < i) _QuickSortRecursive(pArr,nLow,i); if (j < nHigh) _QuickSortRecursive(pArr,j,nHigh); }