Click here to Skip to main content
15,888,273 members

onlinewan - Professional Profile



Summary

    Blog RSS
10
Authority
18
Debator
124
Organiser
426
Participant
0
Author
0
Editor
0
Enquirer
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Reputation

Weekly Data. Recent events may not appear immediately. For information on Reputation please see the FAQ.

Privileges

Members need to achieve at least one of the given member levels in the given reputation categories in order to perform a given action. For example, to store personal files in your account area you will need to achieve Platinum level in either the Author or Authority category. The "If Owner" column means that owners of an item automatically have the privilege. The member types column lists member types who gain the privilege regardless of their reputation level.

ActionAuthorAuthorityDebatorEditorEnquirerOrganiserParticipantIf OwnerMember Types
Have no restrictions on voting frequencysilversilversilversilver
Bypass spam checks when posting contentsilversilversilversilversilversilvergoldSubEditor, Mentor, Protector, Editor
Store personal files in your account areaplatinumplatinumSubEditor, Editor
Have live hyperlinks in your profilebronzebronzebronzebronzebronzebronzesilverSubEditor, Protector, Editor
Have the ability to include a biography in your profilebronzebronzebronzebronzebronzebronzesilverSubEditor, Protector, Editor
Edit a Question in Q&AsilversilversilversilverYesSubEditor, Protector, Editor
Edit an Answer in Q&AsilversilversilversilverYesSubEditor, Protector, Editor
Delete a Question in Q&AYesSubEditor, Protector, Editor
Delete an Answer in Q&AYesSubEditor, Protector, Editor
Report an ArticlesilversilversilversilverSubEditor, Mentor, Protector, Editor
Approve/Disapprove a pending ArticlegoldgoldgoldgoldSubEditor, Mentor, Protector, Editor
Edit other members' articlesSubEditor, Protector, Editor
Create an article without requiring moderationplatinumSubEditor, Mentor, Protector, Editor
Approve/Disapprove a pending QuestionProtector
Approve/Disapprove a pending AnswerProtector
Report a forum messagesilversilverbronzeProtector, Editor
Approve/Disapprove a pending Forum MessageProtector
Have the ability to send direct emails to members in the forumsProtector
Create a new tagsilversilversilversilver
Modify a tagsilversilversilversilver

Actions with a green tick can be performed by this member.


 
General带有扩展功能的ListCtrl Pin
onlinewan20-May-10 21:00
onlinewan20-May-10 21:00 
.h
///////////////////////////////////////////////////////////////////////////////
// ReportCtrl.h
//
// 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.
//
///////////////////////////////////////////////////////////////////////////////

#ifndef __REPORTCTRL_H__
#define __REPORTCTRL_H__

// Sent to parent window when user clicked on the checkbox of an item:
// wParam: The item index in the list ctrl
// lParam: The mouse event type(WM_LBUTTONDOWN, WM_RBUTTONDOWN, etc) which generated this event. 
// Note: This message is not sent when the checkbox states were altered programmatically
//       by calling "SetItem", it is only sent when the user "physically" clicked the
//       checkbox using mouse or joystick etc.
#define WM_ON_CHKBOX	(WM_APP + 10000)

// Sent to parent window when a column of items were sorted
// wParam: The column index
// lParam: The sort method, either 0(descending) or 1(ascending)
#define WM_ITEM_SORTED	(WM_APP + 10001)

// Sent to parent window when an item text editing was committed
// wParam: The item index
// lParam: The column index
#define WM_EDIT_COMMITTED	(WM_APP + 10002)

// Checkbox styles.
#define RC_CHKBOX_NONE			0 // No checkbox displayed
#define	RC_CHKBOX_NORMAL		1 // Normal, multiple check allowed
#define	RC_CHKBOX_SINGLE		2 // Single check only
#define	RC_CHKBOX_DISABLED		3 // Disabled, cannot be checked/unchecked by user input,
								  // but can be by your code.

// Item state flags for selection, deletion, etc.
// Multiple flags can be combined together using the bit-or operator.
// Note: If RC_ITEM_ALL is set, all other flags are ignored
#define RC_ITEM_NONE		0x0000 // Void, indicates invalid items only
#define	RC_ITEM_ALL			0x0001 // All items regardless of states
#define	RC_ITEM_SELECTED	0x0002 // Selected items
#define	RC_ITEM_UNSELECTED	0x0004 // Unselected items
#define	RC_ITEM_CHECKED		0x0008 // Checked items
#define	RC_ITEM_UNCHECKED	0x0010 // Unchecked items
#define	RC_ITEM_FOCUSED		0x0020 // Focused item
#define	RC_ITEM_UNFOCUSED	0x0040 // Unfocused items

// Item inverting types
#define RC_INVERT_SELECTION	0 // Invert item selection
#define RC_INVERT_CHECKMARK	1 // Invert item check mark

// Removes any custom color from item text and item backgroun
#define COLOR_INVALID	0xffffffff

#include "atlbase.h"
#include <set>
#include <map>

//////////////////////////////////////////////////////////////////////////
class CListCtrlColumnEx : public CListCtrl
{
protected:	
	struct ColumnState
	{
		ColumnState()
			:m_Visible(false)
			,m_OrgWidth(0)
			,m_OrgPosition(-1)
			,m_strText(_T(""))
		{}
		bool m_Visible;
		int  m_OrgWidth;	// Width it had before being hidden
		int  m_OrgPosition;	// Position it had before being hidden
		CString m_strText;
	};
	CSimpleArray<ColumnState>	m_ColumnStates;
	int GetColumnStateCount();
	void InsertColumnState(int nCol, bool bVisible, int nOrgWidth, CString strText);
	void DeleteColumnState(int nCol);
	ColumnState& GetColumnState(int nCol);

	virtual afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
	virtual afx_msg LRESULT OnDeleteColumn(WPARAM wParam, LPARAM lParam);
	virtual afx_msg LRESULT OnInsertColumn(WPARAM wParam, LPARAM lParam);
	virtual afx_msg LRESULT OnSetColumnWidth(WPARAM wParam, LPARAM lParam);
	virtual afx_msg BOOL OnHeaderBeginResize(UINT id, NMHDR* pNmhdr, LRESULT* pResult);
	virtual afx_msg BOOL OnHeaderEndResize(UINT id, NMHDR* pNmhdr, LRESULT* pResult);
	virtual afx_msg BOOL OnHeaderEndDrag(UINT id, NMHDR* pNmhdr, LRESULT* pResult);
	virtual afx_msg BOOL OnHeaderDividerDblClick(UINT, NMHDR* pNMHDR, LRESULT* pResult);
	virtual afx_msg void OnContextMenu(CWnd*, CPoint point);
	virtual afx_msg void OnDestroy( );
	virtual afx_msg BOOL OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult ); 

	DECLARE_MESSAGE_MAP()

public:
	virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
	virtual void PreSubclassWindow();

public:
	CListCtrlColumnEx();
	virtual ~CListCtrlColumnEx();

	bool IsColumnVisible(int nCol);
	int GetFirstVisibleColumn();
	BOOL ShowColumn(int nCol, bool bShow);
	BOOL SetColumnWidthAuto(int nCol = -1, bool includeHeader = false);

	void AddReadOnlyShowState(LPCTSTR pszName);

	//bNoHideFirstColumn	标识是否设置第一列不可隐藏
	//strIdentifier			此ListCtrl的唯一标识
	virtual void ResetHeaderState(BOOL bNoHideFirstColumn = FALSE, CString strIdentifier = _T(""));

	void SetShowRightMenu(BOOL bShowRightMenu);
	BOOL IsShowRightMenu();

	virtual int OnToolHitTest(CPoint point, TOOLINFO * pTI) const;
	virtual CString GetItemToolTipText( int nItem, int nSubItem);

private:
	BOOL m_bShowRightMenu;

	BOOL SetRegister(LPCTSTR lpszName, LPCTSTR lpszValue, LPCTSTR lpszKeyName = _T("Software\\LiZheng\\CListCtrlHeader\\Settings"));
	CString GetRegister(LPCTSTR lpszName, LPCTSTR lpszPath = _T("Software\\LiZheng\\CListCtrlHeader\\Settings"));
	void SplitString(LPCTSTR sSource, CStringArray &sDestination, TCHAR cToken);

	BOOL m_bNoChangedFirstColumn;
	
	BOOL GetReadOnlyShowState(int i);
	std::set<CString> m_setReadOnlyShowState;

	CString m_strIdentifier;
	BOOL m_bNeedSaveStatus;
};


//////////////////////////////////////////////////////////////////////////
// The CReportCtrl Class Definition
//////////////////////////////////////////////////////////////////////////
class CReportCtrl : public CListCtrlColumnEx
{
public:		
		
	//////////////////////////////////////////////////////////////////////
	//		Constructor & Destructor
	//////////////////////////////////////////////////////////////////////
	CReportCtrl();
	virtual ~CReportCtrl();

	//////////////////////////////////////////////////////////////////////
	//		Run-time Creation
	//////////////////////////////////////////////////////////////////////
	virtual BOOL Create(CWnd* pParentWnd, UINT nID, LPCRECT lpRect = NULL, DWORD dwStyle = WS_BORDER | WS_TABSTOP);

	///////////////////////////////////////////////////////////////////////
	//		Column Header attributes
	///////////////////////////////////////////////////////////////////////
	//mlj added 081104
	int GetColumnCount();
	void AdjustColumnWidth();
	
	void CancelAllSelectedState();
	int GetAllSelectedIndexs(CDWordArray &aryIndex);

	BOOL SetColumnHeader(const CString& strHeadings); // Set columns and their formats.
	int GetColumnCount() const; // Get the column count.
	BOOL DeleteAllColumns();
	CString GetHeaderText(int nColumn) const;
	BOOL SetHeaderText(int nColumn, LPCTSTR lpText);	
	BOOL HasColumnHeader() const; // FALSE if the list control has LVS_NOCOLUMNHEADER flag
	const CHeaderCtrl* GetHeaderCtrl() const;

	///////////////////////////////////////////////////////////////////////
	//		Images & Color
	///////////////////////////////////////////////////////////////////////
	
	// Column header images
	BOOL SetHeaderImage(int nColumn, int nImageIndex, BOOL bLeftSide = TRUE);
	int GetHeaderImage(int nColumn) const;
	CImageList* SetHeaderImageList(UINT nBitmapID, COLORREF crMask = RGB(255, 0, 255));
	CImageList* SetHeaderImageList(CImageList* pImageList);
	
	// Sub-item images
	BOOL SetItemImage(int nItem, int nSubItem, int nImageIndex);
	int GetItemImage(int nItem, int nSubItem) const;
	CImageList* SetImageList(UINT nBitmapID, COLORREF crMask = RGB(255, 0, 255));
	CImageList* SetImageList(CImageList* pImageList);
	CImageList* GetImageList() const;

	// Sub-item Text & Background Color
	void SetItemTextColor(int nItem = -1, int nSubItem = -1, COLORREF color = COLOR_INVALID, BOOL bRedraw = TRUE);
	COLORREF GetItemTextColor(int nItem, int nSubItem) const;
	void SetItemBkColor(int nItem = -1, int nSubItem = -1, COLORREF color = COLOR_INVALID, BOOL bRedraw = TRUE);
	COLORREF GetItemBkColor(int nItem, int nSubItem) const;

	//////////////////////////////////////////////////////////////////////
	//		Control Styles & Attributes
	//////////////////////////////////////////////////////////////////////
	
	void ResizeToFitParent(); // Resize the list control to occupy parent's client area
	void SetGridLines(BOOL bSet = TRUE); // Show grid lines.
	void SetCheckboxeStyle(int nStyle = RC_CHKBOX_NORMAL); // Set checkbox styles.	
	int GetCheckboxStyle() const;	
	BOOL IsSortable() const; // Is sort allowed?
	BOOL SetSortable(BOOL bSet); // Allow/disallow sorting
	BOOL IsEditable() const; // Is Item text editable?
	void SetEditable(BOOL bSet = TRUE); // Allow item text editting
	
	///////////////////////////////////////////////////////////////////////
	//		Item attributes & operations
	///////////////////////////////////////////////////////////////////////	

	// Item states operation
	int GetFirstItem(DWORD dwStates = RC_ITEM_ALL, int nStartAfter = -1) const;
	int GetLastItem(DWORD dwStates = RC_ITEM_ALL, int nStartBefore = -1) const;
	int GetItemCount(DWORD dwStates = RC_ITEM_ALL) const;	
	DWORD GetItemStates(int nItem) const;
	BOOL ExamItemStates(int nItem, DWORD dwStates) const;
	BOOL SetItemStates(int nItem, DWORD dwNewStates);
	int SetAllItemStates(DWORD dwOldStates, DWORD dwNewStates);
	void InvertItems(int nType); // RC_INVERT_SELECTION or RC_INVERT_CHECKMARK

	// Item Insertion & Deletion	
	int InsertItemEx(int nItem, LPCTSTR lpText, ...); 	
	BOOL DeleteItem(int nItem, BOOL bSelectNextItem = FALSE); // Delete an item.
	int DeleteAllItems(DWORD dwStates = RC_ITEM_ALL); // Delete all qualified items.

	// Item positioning	
	int MoveUp(int nItem, int nCount = 1); // Move an item upwards by "nCount" positions.
	int MoveDown(int nItem, int nCount = 1); // Move an item downwards by "nCount" positions.
	int MoveToTop(int nItem); // Move an item up to the top.
	int MoveToBottom(int nItem); // Move an item down to the bottom.
	int MoveTo(int nItem, int nNewPosition); // Move an item to a particular position 
	BOOL SwapItems(int nItem1, int nItem2);	// Swap two items in the list, including all attributes.

	// Convenient versions of "CListCtrl::SetItemText"	
	BOOL SetItemText(int nItem, int nSubItem, INT val);
	BOOL SetItemText(int nItem, int nSubItem, UINT val);
	BOOL SetItemText(int nItem, int nSubItem, LONG val);
	BOOL SetItemText(int nItem, int nSubItem, ULONG val);
	BOOL SetItemText(int nItem, int nSubItem, TCHAR val);
	BOOL SetItemText(int nItem, int nSubItem, DOUBLE val, int nPrecision = -1);
	BOOL SetItemText(int nItem, int nSubItem, const COleDateTime& dateTime, DWORD dwFlags = 0);

	// Sorting	
	BOOL IsSortAscending() const;
	int GetSortedColumn() const;
	void SortItems(int nColumn, BOOL bAscending); // Sort a specified column.
	void SetSortSeparator(LPCTSTR lpSortSeparator = NULL); // Sort-separator, NULL=disabled
	LPCTSTR GetSortSeparator() const;

	// Item text edit	
	virtual BOOL StartEdit(int nItem, int nSubItem); // Display the editbox, previous edit are committed
	virtual BOOL EndEdit(BOOL bCommit = TRUE); // Commit/cancel text edit, hide the editbox
	CEdit* GetEditControl();	

	///////////////////////////////////////////////////////////////////////
	//		Necessary overloading but Irrelevant to Users
	///////////////////////////////////////////////////////////////////////
	
	int InsertColumn(int nCol, LPCTSTR lpColumnHeading, int nFormat = LVCFMT_LEFT, int nWidth = -1, int nSubItem = -1);
	int InsertColumn(int nCol, const LVCOLUMN* pColumn);
	BOOL DeleteColumn(int nCol);
	BOOL SetCheck(int nItem, BOOL bCheck = TRUE); // overloaded to guard the "single" checkbox style
	BOOL SetItem(int nItem, int nSubItem, UINT nMask, LPCTSTR lpszItem, int nImage, UINT nState, UINT nStateMask, LPARAM lParam);
	BOOL SetItem(const LVITEM* pItem);
	BOOL GetItem(LVITEM* pItem) const;
	DWORD GetItemData(int nItem) const;
	BOOL SetItemData(int nItem, DWORD dwData);
	DWORD SetExtendedStyle(DWORD dwNewStyle);
	BOOL ModifyStyle(DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0);
	BOOL ModifyStyleEx(DWORD dwRemove, DWORD dwAdd, UINT nFlags = 0);
	int InsertItem(UINT nMask, int nItem, LPCTSTR lpItem, UINT nState, UINT nStateMask, int nImage, LPARAM lParam);
	int InsertItem(int nItem, LPCTSTR lpItem, int nImage);
	int InsertItem(const LVITEM* pItem);
	int InsertItem(int nIndex, LPCTSTR lpText);
	BOOL SetItemText(int nItem, int nSubItem, LPCTSTR lpText);

	int GetCurSel();
	void ClearAllFilter();

protected:	
	
	// Helper functions for internal use
	BOOL _IsValidIndex(int nIndex) const;
	BOOL _ItemCheckMonitor(int nIndex, BOOL bBefore, BOOL bAfter, UINT nMsg);
	BOOL _IsEditVisible() const;
	void _MouseClkMonitor(UINT nMsg, UINT nFlags, CPoint point, BOOL bTriggerEdit);
	static void _StringSplit(const CString &str, CStringArray& arr, TCHAR chDelimitior);
	void _UnsetSortedColumn();
	BOOL _SetItemStatesNoVerify(int nItem, DWORD dwNewStates);
	void _EnsureSingleCheck(int nItem);
	DWORD _GetHeaderTextFormat(int nColumn) const;
	void _UpdateColumn(int nColumn, BOOL bInsert);
	void _AllocItemMemory(int nItem);
	void _FreeItemMemory(int nItem);
	BOOL _PartialSort(int nStart, int nEnd);
	int _FindSeparator(int nStartAfter, int nColumn) const;
	void _QuickSortRecursive(int* pArr, int nLow, int nHigh);
	int _CompareItems(int nItem1, int nItem2);

	// Member data	
	CEdit* m_pWndEdit; // Edit control, for subitem edit
	LPTSTR m_pszSeparator; // Sort separator
	BOOL m_bAllowEdit; // Is subitem edit allowed?
	POINT m_ptEditting; // Position of the subitem that is currently being edited
	int m_nChkStyle; // Checkbox style
	DWORD m_dwPrevEditFmt; // Format of previously edited subitem
	CImageList m_imgList; // Image list for items
	CImageList m_headerImgList; // Image list for the header control
	int m_nSortCol; // The sorted column, -1 if none
	BOOL m_bSortAscending; // Is sort ascending?

	//////////////////////////////////////////////////////////////////////
	// Wizard Generated Stuff
	//////////////////////////////////////////////////////////////////////

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CReportCtrl)
	public:
	virtual BOOL PreTranslateMessage(MSG* pMsg);
	protected:
	virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
	virtual void PreSubclassWindow();
	//}}AFX_VIRTUAL

	// Generated message map functions

protected:	

	//{{AFX_MSG(CReportCtrl)
	afx_msg void OnColumnclick(NMHDR* pNMHDR, LRESULT* pResult);
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
	afx_msg void OnDestroy();
	afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
	afx_msg void OnMButtonDown(UINT nFlags, CPoint point);
	afx_msg void OnMButtonDblClk(UINT nFlags, CPoint point);
	afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
	afx_msg void OnRButtonDblClk(UINT nFlags, CPoint point);
	afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult);
	afx_msg void OnFilterChange(NMHDR* pNMHDR, LRESULT* pResult);
	afx_msg void OnFilterButtonClicked(NMHDR* pNMHDR, LRESULT* pResult);
	//}}AFX_MSG

	DECLARE_MESSAGE_MAP()
};

//////////////////////////////////////////////////////////////////////////
// End of CReportCtrl Class Definition
//////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // __REPORTCTRL_H__


.cpp
///////////////////////////////////////////////////////////////////////////////
// ReportCtrl.cpp
//
// CReportCtrl, a CListCtrlColumnEx 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 <afxtempl.h>

#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_STYLE		(LVS_ICON | LVS_SMALLICON | LVS_LIST | LVS_NOSCROLL)
#define MUST_NOT_EX_STYLE	(0)

struct ROWINFO
{
	DWORD dwData;
	DWORD dwStates;
	CArray<int, int> 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<COLORREF, COLORREF> aTextColors; // Sub item text colors
	CArray<COLORREF, COLORREF> 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, CListCtrlColumnEx)
	//{{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)
	ON_NOTIFY(HDN_FILTERCHANGE, 0, OnFilterChange)
	ON_NOTIFY(HDN_FILTERBTNCLICK, 0, OnFilterButtonClicked)
	//}}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*)(CListCtrlColumnEx::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 (CListCtrlColumnEx::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();
	CListCtrlColumnEx::OnDestroy();
	// TODO: Add your message handler code here	
}

BOOL CReportCtrl::_IsValidIndex(int nIndex) const
{
	return nIndex >= 0 && nIndex < CListCtrlColumnEx::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 = CListCtrlColumnEx::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 CListCtrlColumnEx::DeleteItem(nItem);
}

int CReportCtrl::DeleteAllItems(DWORD dwStates)
{
	EndEdit(FALSE);
	int nItemCount = CListCtrlColumnEx::GetItemCount();
	if (dwStates & RC_ITEM_ALL)
	{
//		LockWindowUpdate();
		for (int i = 0; i < nItemCount; i++)
			_FreeItemMemory(i);
		CListCtrlColumnEx::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 CListCtrlColumnEx::SetItemText(nItem, nSubItem, lpText);
}

int CReportCtrl::GetColumnCount() const
{
	return GetHeaderCtrl()->GetItemCount();
}

int CReportCtrl::InsertItem(const LVITEM *pItem)
{
	EndEdit(TRUE);
	_UnsetSortedColumn();
	const int IDX = CListCtrlColumnEx::InsertItem(pItem);
	if (IDX >= 0)
		_AllocItemMemory(IDX);
	return IDX;
}

int CReportCtrl::InsertItem(int nItem, LPCTSTR lpItem, int nImage)
{	
	EndEdit(TRUE);
	_UnsetSortedColumn();
	const int IDX = CListCtrlColumnEx::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 = CListCtrlColumnEx::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 = CListCtrlColumnEx::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 = CListCtrlColumnEx::InsertColumn(nCol, lpColumnHeading, nFormat, nWidth, nSubItem);
	if (IDX >= 0)
		_UpdateColumn(IDX, TRUE);
	return IDX;
}

BOOL CReportCtrl::DeleteColumn(int nCol)
{
	EndEdit(TRUE);
	const BOOL RES = CListCtrlColumnEx::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 = CListCtrlColumnEx::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 = CListCtrlColumnEx::GetItemData(nItem);
	CArray<int, int> 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
	CListCtrlColumnEx::DeleteItem(nItem);

	// Insert a new item to the new position
	const int IDX = CListCtrlColumnEx::InsertItem(nNewPosition, _T(""));

	// Restore all states & attributes to the newly inserted item
	for (int j = 0; j < COLS; j++)
	{
		CListCtrlColumnEx::SetItemText(IDX, j, aTexts[j]);
		SetItemImage(IDX, j, aImages[j]);
	}
	CListCtrlColumnEx::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 = CListCtrlColumnEx::GetItemData(nItem1);
	const DWORD DATA2 = CListCtrlColumnEx::GetItemData(nItem2);

	// swap item texts and images
	for (int i = 0; i < GetColumnCount(); i++)
	{
		CString str = GetItemText(nItem1, i);
		CListCtrlColumnEx::SetItemText(nItem1, i, GetItemText(nItem2, i));
		CListCtrlColumnEx::SetItemText(nItem2, i, str);
		UINT nImg = GetItemImage(nItem1, i);
		SetItemImage(nItem1, i, GetItemImage(nItem2, i));
		SetItemImage(nItem2, i, nImg);
	}
	
	// swap item data
	CListCtrlColumnEx::SetItemData(nItem1, DATA2);
	CListCtrlColumnEx::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 = CListCtrlColumnEx::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)
		{
			CListCtrlColumnEx::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 < CListCtrlColumnEx::GetItemCount()
		&& m_ptEditting.y >= 0 && m_ptEditting.y < GetColumnCount();
}

void CReportCtrl::SetEditable(BOOL bSet)
{	
	if (!bSet)
		EndEdit(TRUE);
	m_bAllowEdit = bSet;

	if ( m_bAllowEdit )
	{
		ModifyStyle(LVS_EDITLABELS, 0, 0);
	}
}

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);
	CListCtrlColumnEx::GetHeaderCtrl()->RedrawWindow();
	return TRUE;
}

DWORD CReportCtrl::SetExtendedStyle(DWORD dwNewStyle)
{
	dwNewStyle &= ~MUST_NOT_EX_STYLE;
	dwNewStyle |= MUST_EX_STYLE;
	return CListCtrlColumnEx::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 CListCtrlColumnEx::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 < CListCtrlColumnEx::GetItemCount() - 1)
				{
					pt.x++;
					EndEdit(TRUE);
					StartEdit(pt.x, pt.y);
					EnsureVisible(pt.x, FALSE);
					return TRUE;
				}
				break;			

			default:
				break;				
			}
		}
	}

	return CListCtrlColumnEx::PreTranslateMessage(pMsg);
}

CEdit* CReportCtrl::GetEditControl()
{
	if ( !m_bAllowEdit )
		return CListCtrlColumnEx::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 CListCtrlColumnEx::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);
	CListCtrlColumnEx::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 CListCtrlColumnEx::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 CListCtrlColumnEx::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 = CListCtrlColumnEx::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:
		CListCtrlColumnEx::OnLButtonDown(nFlags, point);
		break;

	case WM_LBUTTONDBLCLK:
		CListCtrlColumnEx::OnLButtonDblClk(nFlags, point);
		break;

	case WM_MBUTTONDOWN:
		CListCtrlColumnEx::OnMButtonDown(nFlags, point);
		break;

	case WM_MBUTTONDBLCLK:
		CListCtrlColumnEx::OnMButtonDblClk(nFlags, point);
		break;

	case WM_RBUTTONDOWN:
		CListCtrlColumnEx::OnRButtonDown(nFlags, point);
		break;

	case WM_RBUTTONDBLCLK:
		CListCtrlColumnEx::OnRButtonDblClk(nFlags, point);
		break;

	default:
		break;		
	}
	
	const BOOL STATSCHANGED = _ItemCheckMonitor(IDX, BEFORE, CListCtrlColumnEx::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 CListCtrlColumnEx::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 CListCtrlColumnEx::GetItem(&lvi) ? lvi.iImage : -1;
}

CImageList* CReportCtrl::SetImageList(CImageList *pImageList)
{
	return CListCtrlColumnEx::SetImageList(pImageList, LVSIL_SMALL);
}

CImageList* CReportCtrl::GetImageList() const
{
	return CListCtrlColumnEx::GetImageList(LVSIL_SMALL);
}

CImageList* CReportCtrl::SetImageList(UINT nBitmapID, COLORREF crMask)
{
	m_imgList.DeleteImageList();
	m_imgList.Create(nBitmapID, 16, 4, crMask);	
	return CListCtrlColumnEx::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
		CListCtrlColumnEx::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)
		CListCtrlColumnEx::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 CListCtrlColumnEx::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 CListCtrlColumnEx::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 = CListCtrlColumnEx::GetItemCount();
	if (dwStates == RC_ITEM_ALL)
		return ITEMS;

	int nCount = 0;
	for (int i = 0; i <ITEMS; i++)
	{
		if (ExamItemStates(i, dwStates))
			nCount++;
	}
	return nCount;
}

int CReportCtrl::GetFirstItem(DWORD dwStates, int nStartAfter) const
{
	const int ITEMS = CListCtrlColumnEx::GetItemCount();
	nStartAfter = max(-1, nStartAfter);
	for (int i = nStartAfter + 1; i <ITEMS; i++)
	{
		if (ExamItemStates(i, dwStates))
			return i;
	}
	return -1;
}

int CReportCtrl::GetLastItem(DWORD dwStates, int nStartBefore) const
{
	const int ITEMS = CListCtrlColumnEx::GetItemCount();
	nStartBefore = nStartBefore < 0 ? ITEMS - 1 : min(ITEMS, nStartBefore);
	for (int i = nStartBefore - 1; i >= 0; i--)
	{
		if (ExamItemStates(i, dwStates))
			return i;
	}
	return -1;
}

void CReportCtrl::InvertItems(int nType)
{
	const int ITEMS = CListCtrlColumnEx::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 (CListCtrlColumnEx::GetItemState(nItem, LVIS_SELECTED))
		dwStates |= RC_ITEM_SELECTED;
	else
		dwStates |= RC_ITEM_UNSELECTED;

	if (CListCtrlColumnEx::GetCheck(nItem) > 0)
		dwStates |= RC_ITEM_CHECKED;
	else
		dwStates |= RC_ITEM_UNCHECKED;

	if (CListCtrlColumnEx::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 = CListCtrlColumnEx::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 = CListCtrlColumnEx::GetItemCount();
			for (int i = FIRST + 1; i < ITEMS; i++)
			{
				if (CListCtrlColumnEx::GetCheck(i))
					CListCtrlColumnEx::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 &= CListCtrlColumnEx::SetItemState(nItem, LVIS_SELECTED, LVIS_SELECTED);

	if (dwNewStates & RC_ITEM_UNSELECTED)
		bResult &= CListCtrlColumnEx::SetItemState(nItem, 0, LVIS_SELECTED);

	if (dwNewStates & RC_ITEM_CHECKED)
		bResult &= CListCtrlColumnEx::SetCheck(nItem, TRUE);

	if (dwNewStates & RC_ITEM_UNCHECKED)
		bResult &= CListCtrlColumnEx::SetCheck(nItem, FALSE);

	if (dwNewStates & RC_ITEM_FOCUSED)
		bResult &= CListCtrlColumnEx::SetItemState(nItem, LVIS_FOCUSED, LVIS_FOCUSED);

	if (dwNewStates & RC_ITEM_UNFOCUSED)
		bResult &= CListCtrlColumnEx::SetItemState(nItem, 0, LVIS_FOCUSED);

	return bResult;
}

BOOL CReportCtrl::SetCheck(int nItem, BOOL bCheck)
{
	const BOOL RES = CListCtrlColumnEx::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 = CListCtrlColumnEx::GetHeaderCtrl()->SetItem(nColumn, &hd);
	delete [] psz;
	return RES;
}

const CHeaderCtrl* CReportCtrl::GetHeaderCtrl() const
{
	// Yes, CListCtrlColumnEx 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*)(CListCtrlColumnEx::GetItemData(nItem));
	if (p != NULL)
	{
		CListCtrlColumnEx::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 = CListCtrlColumnEx::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);
	}
	CListCtrlColumnEx::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*)(CListCtrlColumnEx::GetItemData(i));
		ASSERT(p != NULL);
		if (bInsert)
			p->InsertColumn(nColumn);
		else
			p->DeleteColumn(nColumn);
	}
}

BOOL CReportCtrl::SetItemData(int nItem, DWORD dwData)
{
	CItemData* p = (CItemData*)(CListCtrlColumnEx::GetItemData(nItem));
	if (p == NULL)
		return FALSE;
	p->dwData = dwData;
	return CListCtrlColumnEx::SetItemData(nItem, (DWORD)p);
}

DWORD CReportCtrl::GetItemData(int nItem) const
{
	CItemData* p = (CItemData*)(CListCtrlColumnEx::GetItemData(nItem));
	return p == NULL ? 0 : p->dwData;
}

BOOL CReportCtrl::GetItem(LVITEM *pItem) const
{
	const BOOL RES = CListCtrlColumnEx::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*)(CListCtrlColumnEx::GetItemData(li.iItem));
		if (p != NULL)
		{
			p->dwData = li.lParam;
			li.lParam = (LPARAM)p;
		}		
	}
	return CListCtrlColumnEx::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*)(CListCtrlColumnEx::GetItemData(nItem));
		if (p != NULL)
		{
			p->dwData = lParam;
			lParam = (LPARAM)p;
		}		
	}
	return CListCtrlColumnEx::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*)(CListCtrlColumnEx::GetItemData(nItem));
		ASSERT(p != NULL);
		p->aTextColors[nSubItem] = color;
	}
	else if (bRowValid && !bColValid)
	{
		// apply to whole row for the existing item
		CItemData* p = (CItemData*)(CListCtrlColumnEx::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*)(CListCtrlColumnEx::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*)(CListCtrlColumnEx::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*)(CListCtrlColumnEx::GetItemData(nItem));
		ASSERT(p != NULL);
		p->aBkColors[nSubItem] = color;
	}
	else if (bRowValid && !bColValid)
	{
		// apply to whole row for the existing item
		CItemData* p = (CItemData*)(CListCtrlColumnEx::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*)(CListCtrlColumnEx::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*)(CListCtrlColumnEx::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*)(CListCtrlColumnEx::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*)(CListCtrlColumnEx::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 = CListCtrlColumnEx::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);
		CListCtrlColumnEx::SetItemData(nStart + i, aRows[i].dwData);
		for (int j = 0; j < COLS; j++)
		{
			SetItemImage(nStart + i, j, aRows[i].aImages[j]);			
			CListCtrlColumnEx::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);

	// 修改 by wxg [10/20/2009 14:50:03] 当字符串中含有特殊字符时,下面的语句会有异常,故注掉
// 	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.CompareNoCase(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);
}

//mlj added 081104
void CReportCtrl::AdjustColumnWidth()
{
	SetRedraw(FALSE);
	int nColumnCount = GetColumnCount();

	for(int i = 0; i < nColumnCount; i++)
	{
		SetColumnWidth(i, LVSCW_AUTOSIZE);
		int nColumnWidth = GetColumnWidth(i);
		SetColumnWidth(i, LVSCW_AUTOSIZE_USEHEADER);
		int nHeaderWidth = GetColumnWidth(i);

		SetColumnWidth(i, max(nColumnWidth, nHeaderWidth));
	}
	SetRedraw(TRUE);
}

int CReportCtrl::GetColumnCount()
{
	return(GetHeaderCtrl()->GetItemCount());
}

int CReportCtrl::GetCurSel()
{
    POSITION    pos             = GetFirstSelectedItemPosition ();
    int         nSelectedItem   = - 1;
    if ( pos != NULL )
    {
        nSelectedItem = GetNextSelectedItem ( pos );
    }
    return nSelectedItem;
}

void CReportCtrl::OnFilterChange(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NMHEADER	*pNMHdr = (NMHEADER*)pNMHDR;
	CWnd		*pParent = GetParent();

	*pResult = 0;											// Default RetCode
	if( pParent )											// If Parent Exists
	{
		pNMHdr -> hdr.idFrom = GetDlgCtrlID();				// Update Structure Data
		*pResult = pParent -> SendNotifyMessage( WM_NOTIFY, 
				GetDlgCtrlID(), (LPARAM)pNMHdr );			// Forward Message
	}
	return;													// Done!
}

void CReportCtrl::OnFilterButtonClicked(NMHDR* pNMHDR, LRESULT* pResult) 
{
	NMHDFILTERBTNCLICK	*pNMFButtonClick = (NMHDFILTERBTNCLICK*)pNMHDR;
	NMHEADER			*pNMHdr = (NMHEADER*)pNMHDR;
	CWnd				*pParent = GetParent();

	*pResult = 0;											// Default RetCode
	if( pParent )											// If Parent Exists
	{
		pNMHdr -> hdr.idFrom = GetDlgCtrlID();				// Update Structure Data
		*pResult = pParent -> SendNotifyMessage( WM_NOTIFY, 
				GetDlgCtrlID(), (LPARAM)pNMHdr );			// Forward Message
	}
	return;													// Done!
}

void CReportCtrl::ClearAllFilter()
{
	CHeaderCtrl	*pHeader = (CHeaderCtrl	*)GetHeaderCtrl();
	if ( pHeader != NULL )
	{
		Header_ClearAllFilters( pHeader -> m_hWnd );		
	}
}

void CReportCtrl::CancelAllSelectedState()
{
	CDWordArray aryIndex;
	GetAllSelectedIndexs(aryIndex);

	for ( int i = 0; i < aryIndex.GetSize(); ++i )
	{
		SetItemState(aryIndex.GetAt(i), 0, LVIS_SELECTED);
	}
}

int CReportCtrl::GetAllSelectedIndexs(CDWordArray &aryIndex)
{
	aryIndex.RemoveAll();
	POSITION pos = GetFirstSelectedItemPosition();
	while (pos)
	{
		aryIndex.Add(GetNextSelectedItem(pos));
	}

	return aryIndex.GetSize();
}
//////////////////////////////////////////////////////////////////////////
//start CListCtrlColumnEx
CListCtrlColumnEx::CListCtrlColumnEx()
{
	m_bNoChangedFirstColumn = FALSE;	
	m_bShowRightMenu = FALSE;

	m_strIdentifier = _T("");
	m_bNeedSaveStatus = FALSE;
}

CListCtrlColumnEx::~CListCtrlColumnEx()
{
	
}

BEGIN_MESSAGE_MAP(CListCtrlColumnEx, CListCtrl)
	ON_MESSAGE(LVM_DELETECOLUMN, OnDeleteColumn)
	ON_MESSAGE(LVM_INSERTCOLUMN, OnInsertColumn)
	ON_MESSAGE(LVM_SETCOLUMNWIDTH, OnSetColumnWidth)
	ON_NOTIFY_EX(HDN_BEGINTRACKA, 0, OnHeaderBeginResize)
	ON_NOTIFY_EX(HDN_BEGINTRACKW, 0, OnHeaderBeginResize)
	ON_NOTIFY_EX(HDN_ENDTRACKA, 0, OnHeaderEndResize)
	ON_NOTIFY_EX(HDN_ENDTRACKW, 0, OnHeaderEndResize)
	ON_NOTIFY_EX(HDN_ENDDRAG, 0, OnHeaderEndDrag)
	ON_NOTIFY_EX(HDN_DIVIDERDBLCLICKA, 0, OnHeaderDividerDblClick)
	ON_NOTIFY_EX(HDN_DIVIDERDBLCLICKW, 0, OnHeaderDividerDblClick)
	ON_WM_CONTEXTMENU()	// OnContextMenu
	ON_WM_KEYDOWN()		// OnKeyDown
	ON_WM_DESTROY()
	ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
	ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
END_MESSAGE_MAP()

int CListCtrlColumnEx::GetFirstVisibleColumn()
{
	int nColCount = GetHeaderCtrl()->GetItemCount();
	for(int i = 0; i < nColCount; ++i)
	{
		int nCol = GetHeaderCtrl()->OrderToIndex(i);
		if (IsColumnVisible(nCol))
		{
			return nCol;
		}
	}
	return -1;
}

BOOL CListCtrlColumnEx::ShowColumn(int nCol, bool bShow)
{
	SetRedraw(FALSE);

	ColumnState& columnState = GetColumnState(nCol);

	if (bShow)
	{
		// Restore the column width
		columnState.m_Visible = true;
		if ( columnState.m_OrgWidth == 0 )
		{
			columnState.m_OrgWidth = LVSCW_AUTOSIZE;
		}
		VERIFY( SetColumnWidth(nCol, columnState.m_OrgWidth) );
	}
	else
	{
		// Backup the column width
		int orgWidth = GetColumnWidth(nCol);
		VERIFY( SetColumnWidth(nCol, 0) );
		columnState.m_Visible = false;
		columnState.m_OrgWidth = orgWidth;
	}
	SetRedraw(TRUE);
	Invalidate(FALSE);
	return TRUE;
}

BOOL CListCtrlColumnEx::SetColumnWidthAuto(int nCol, bool includeHeader)
{
	if (nCol == -1)
	{
		for(int i = 0; i < GetHeaderCtrl()->GetItemCount() ; ++i)
		{
			SetColumnWidthAuto(i, includeHeader);
		}
		return TRUE;
	}
	else
	{
		if (includeHeader)
			return SetColumnWidth(nCol, LVSCW_AUTOSIZE_USEHEADER);
		else
			return SetColumnWidth(nCol, LVSCW_AUTOSIZE);
	}
}

CListCtrlColumnEx::ColumnState& CListCtrlColumnEx::GetColumnState(int nCol)
{
	VERIFY( nCol >=0 && nCol < m_ColumnStates.GetSize() );
	return m_ColumnStates[nCol];
}

bool CListCtrlColumnEx::IsColumnVisible(int nCol)
{
	return GetColumnState(nCol).m_Visible;
}

int CListCtrlColumnEx::GetColumnStateCount()
{
	return m_ColumnStates.GetSize();
}

void CListCtrlColumnEx::InsertColumnState(int nCol, bool bVisible, int nOrgWidth, CString strText)
{
	VERIFY( nCol >=0 && nCol <= m_ColumnStates.GetSize() );

	ColumnState columnState;
	columnState.m_OrgWidth = nOrgWidth;
	columnState.m_Visible = bVisible;
	columnState.m_strText = strText;
	columnState.m_OrgPosition = nCol;

	if (nCol == m_ColumnStates.GetSize())
	{
		// Append column picker to the end of the array
		m_ColumnStates.Add(columnState);
	}
	else
	{
		// Insert column in the middle of the array
		CSimpleArray<ColumnState> newArray;
		for(int i=0 ; i < m_ColumnStates.GetSize(); ++i)
		{
			if (i == nCol)
				newArray.Add(columnState);
			newArray.Add(m_ColumnStates[i]);
		}
		m_ColumnStates = newArray;
	}
}

void CListCtrlColumnEx::DeleteColumnState(int nCol)
{
	VERIFY( nCol >=0 && nCol < m_ColumnStates.GetSize() );
	m_ColumnStates.RemoveAt(nCol);
}

void CListCtrlColumnEx::OnContextMenu(CWnd* pWnd, CPoint point)
{
	if ( !m_bShowRightMenu )
	{
		return;
	}

	if (point.x==-1 && point.y==-1)
	{
		// OBS! point is initialized to (-1,-1) if using SHIFT+F10 or VK_APPS
	}
	else
	{
		CPoint pt = point;
		ScreenToClient(&pt);

		CRect headerRect;
		GetHeaderCtrl()->GetClientRect(&headerRect);
		if (headerRect.PtInRect(pt))
		{
			// Show context-menu with the option to show hide columns
			CMenu menu;
			if (menu.CreatePopupMenu())
			{
				for( int i = GetColumnStateCount()-1 ; i >= 0; --i)
				{
					UINT uFlags = MF_BYPOSITION | MF_STRING | MF_ENABLED;

					// Put check-box on context-menu
					if (IsColumnVisible(i))
						uFlags |= MF_CHECKED;
					else
						uFlags |= MF_UNCHECKED;

					if(GetReadOnlyShowState(i))
						uFlags |= MF_GRAYED;

					// Retrieve column-title
					LVCOLUMN lvc = {0};
					lvc.mask = LVCF_TEXT;
					TCHAR sColText[256];
					lvc.pszText = sColText;
					lvc.cchTextMax = sizeof(sColText)-1;
					VERIFY( GetColumn(i, &lvc) );

					menu.InsertMenu(0, uFlags, i, lvc.pszText);
				}

/*
								if ( menu.GetMenuItemCount() > 0 && m_bNoChangedFirstColumn )
								{
									menu.EnableMenuItem(0, MF_BYCOMMAND | MF_GRAYED);
								}

				*/
//				menu.EnableMenuItem(i, MF_BYCOMMAND | GetReadOnlyShowState(i) ? MF_GRAYED : MF_ENABLED);
				menu.TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, this, 0);
			}
		}
	}
}

// Handle context-menu event for showing / hiding columns
BOOL CListCtrlColumnEx::OnCommand(WPARAM wParam, LPARAM lParam)
{
	if (HIWORD(wParam) == 0)
	{
		int nCol = LOWORD(wParam);
		if ( m_bShowRightMenu && nCol >= 0 && nCol < GetColumnStateCount() )
		{
			ShowColumn(nCol, !IsColumnVisible(nCol));
		}
	}

	return CListCtrl::OnCommand(wParam, lParam);
}

void CListCtrlColumnEx::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// 	switch(nChar)
// 	{
// 		case VK_ADD:	// CTRL + NumPlus (Auto size all columns)
// 		{
// 			if (GetKeyState(VK_CONTROL) < 0)
// 			{
// 				// Special handling to avoid showing "hidden" columns
// 				SetColumnWidthAuto(-1);
// 				return;
// 			}
// 		} break;
// 	}
	CListCtrl::OnKeyDown(nChar, nRepCnt, nFlags);
}

BOOL CListCtrlColumnEx::OnHeaderBeginResize(UINT id, NMHDR* pNMHDR, LRESULT* pResult)
{
	// Check that column is allowed to be resized
	NMHEADER* pNMH = (NMHEADER*)pNMHDR;
	int nCol = (int)pNMH->iItem;
	if (!IsColumnVisible(nCol))
	{
		*pResult = TRUE;	// Block resize
		return TRUE;		// Block event
	}
	return DefWindowProc(HDN_BEGINTRACK, (WPARAM)id, (LPARAM)pNMHDR);
}

BOOL CListCtrlColumnEx::OnHeaderEndResize( UINT id, NMHDR* pNmhdr, LRESULT* pResult )
{
	NMHEADER* pNMH = (NMHEADER*)pNmhdr;
	int nCol = (int)pNMH->iItem;
	
	m_ColumnStates[nCol].m_OrgWidth = pNMH->pitem->cxy;
	
	*pResult = TRUE;
	return DefWindowProc(HDN_ENDTRACK, (WPARAM)id, (LPARAM)pNmhdr);
}

LRESULT CListCtrlColumnEx::OnSetColumnWidth(WPARAM wParam, LPARAM lParam)
{
	// Check that column is allowed to be resized
	int nCol = (int)wParam;
	if (!IsColumnVisible(nCol))
	{
		return FALSE;
	}
	// Let the CListCtrl handle the event
	return DefWindowProc(LVM_SETCOLUMNWIDTH, wParam, lParam);
}

BOOL CListCtrlColumnEx::OnHeaderEndDrag(UINT id, NMHDR* pNMHDR, LRESULT* pResult)
{
	NMHEADER* pNMH = (NMHEADER*)pNMHDR;
	if (pNMH->pitem->mask & HDI_ORDER)
	{
		// Correct iOrder so it is just after the last hidden column
		int nColCount = GetHeaderCtrl()->GetItemCount();
		int* pOrderArray = new int[nColCount];
		VERIFY( GetColumnOrderArray(pOrderArray, nColCount) );

		for(int i = 0; i < nColCount ; ++i)
		{
			if (IsColumnVisible(pOrderArray[i]))
			{
                pNMH->pitem->iOrder = max(pNMH->pitem->iOrder,i);
				break;
			}
		}
		delete [] pOrderArray;
	}
	return DefWindowProc(HDN_ENDTRACK, (WPARAM)id, (LPARAM)pNMHDR);
}

BOOL CListCtrlColumnEx::OnHeaderDividerDblClick(UINT id, NMHDR* pNMHDR, LRESULT* pResult)
{
	NMHEADER* pNMH = (NMHEADER*)pNMHDR;
	SetColumnWidthAuto(pNMH->iItem);
	m_ColumnStates[pNMH->iItem].m_OrgWidth = GetColumnWidth(pNMH->iItem);
	return TRUE;	// Don't let parent handle the event
}

LRESULT CListCtrlColumnEx::OnDeleteColumn(WPARAM wParam, LPARAM lParam)
{
	// Let the CListCtrl handle the event
	LRESULT lRet = DefWindowProc(LVM_DELETECOLUMN, wParam, lParam);
	if (lRet == FALSE)
		return FALSE;

	// Book keeping of columns
	DeleteColumnState((int)wParam);
	return lRet;
}

LRESULT CListCtrlColumnEx::OnInsertColumn(WPARAM wParam, LPARAM lParam)
{
	// Let the CListCtrl handle the event
	LRESULT lRet = DefWindowProc(LVM_INSERTCOLUMN, wParam, lParam);
	if (lRet == -1)
		return -1;

	int nCol = (int)lRet;

	// Book keeping of columns
	if (GetColumnStateCount() < GetHeaderCtrl()->GetItemCount())
	{
		HDITEM hdi;
		TCHAR  lpBuffer[256];
		bool   fFound = false;
		
		hdi.mask = HDI_TEXT;
		hdi.pszText = lpBuffer;
		hdi.cchTextMax = 256;
		if ( GetHeaderCtrl()->GetItem(nCol, &hdi) )
		{
			InsertColumnState((int)nCol, true, GetColumnWidth(nCol), hdi.pszText);	// Insert as visible
		}
	}

	return lRet;
}


namespace {
	LRESULT EnableWindowTheme(HWND hwnd, LPCWSTR classList, LPCWSTR subApp, LPCWSTR idlist)
	{
		LRESULT lResult = S_FALSE;
		HMODULE hinstDll;
		BOOL (WINAPI *pIsThemeActive)();
		HRESULT (WINAPI *pSetWindowTheme)(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList);
		HANDLE (WINAPI *pOpenThemeData)(HWND hwnd, LPCWSTR pszClassList);
		HRESULT (WINAPI *pCloseThemeData)(HANDLE hTheme);

		// Check if running on Windows XP or newer
		hinstDll = ::LoadLibrary(_T("UxTheme.dll"));
		if (hinstDll)
		{
			// Check if theme service is running
			(FARPROC&)pIsThemeActive = ::GetProcAddress( hinstDll, "IsThemeActive" );
			if( pIsThemeActive && pIsThemeActive() )
			{
				(FARPROC&)pOpenThemeData = ::GetProcAddress(hinstDll, "OpenThemeData");
				(FARPROC&)pCloseThemeData = ::GetProcAddress(hinstDll, "CloseThemeData");
				(FARPROC&)pSetWindowTheme = ::GetProcAddress(hinstDll, "SetWindowTheme");
				if (pSetWindowTheme && pOpenThemeData && pCloseThemeData)			
				{
					// Check is themes is available for the application
					HANDLE hTheme = pOpenThemeData(hwnd,classList);
					if (hTheme!=NULL)
					{
						VERIFY(pCloseThemeData(hTheme)==S_OK);
						// Enable Windows Theme Style
						lResult = pSetWindowTheme(hwnd, subApp, idlist);
					}
				}
			}
			::FreeLibrary(hinstDll);
		}
		return lResult;
	}
}

void CListCtrlColumnEx::PreSubclassWindow()
{
	CListCtrl::PreSubclassWindow();

	// Focus retangle is not painted properly without double-buffering
#if (_WIN32_WINNT >= 0x501)
	SetExtendedStyle(LVS_EX_DOUBLEBUFFER | GetExtendedStyle());
#endif
//	SetExtendedStyle(GetExtendedStyle() | LVS_EX_FULLROWSELECT);
//	SetExtendedStyle(GetExtendedStyle() | LVS_EX_HEADERDRAGDROP);
//	SetExtendedStyle(GetExtendedStyle() | LVS_EX_GRIDLINES);

	// Enable Vista-look if possible
//	EnableWindowTheme(GetSafeHwnd(), L"ListView", L"Explorer", NULL);
}

void CListCtrlColumnEx::ResetHeaderState(BOOL bNoHideFirstColumn, CString strIdentifier)
{
	m_bNoChangedFirstColumn = bNoHideFirstColumn;
	m_strIdentifier = strIdentifier;
	m_bNeedSaveStatus = TRUE;

	TCHAR szName[1024];
	GetModuleFileName(NULL, szName, 1023);
	TCHAR *pFileName = _tcsrchr(szName, '\\') + 1;

	int nCtrlId = GetDlgCtrlID();

	CString strIdentifierInReg;
	strIdentifierInReg.Format(_T("%s_%d_%s"), pFileName, nCtrlId, m_strIdentifier);

	CString strRegValue = GetRegister(strIdentifierInReg);

	if ( strRegValue.IsEmpty() )
	{
		return;
	}

	std::map<CString, int> mapHeader;
	std::map<CString, int>::iterator mapIter;
	int nHeaderCount = GetHeaderCtrl()->GetItemCount();

	HDITEM hdi;
	TCHAR  lpBuffer[256];
	bool   fFound = false;
	
	hdi.mask = HDI_TEXT;
	hdi.pszText = lpBuffer;
	hdi.cchTextMax = 256;

	for ( int nIndexHeader = 0; nIndexHeader < nHeaderCount; ++nIndexHeader )
	{
		CString strHeaderText;
		if ( GetHeaderCtrl()->GetItem(nIndexHeader, &hdi) )
		{
			strHeaderText = hdi.pszText;
		}

		mapHeader.insert(std::make_pair(strHeaderText, nIndexHeader));
	}

	CStringArray sDestination;
	SplitString(strRegValue, sDestination, '\1');

	if ( sDestination.GetSize() != nHeaderCount )
	{
		return;
	}

	CSimpleArray<ColumnState> newArray;
	int i = 0;
	for ( i = 0; i < m_ColumnStates.GetSize(); ++i )
	{
		ColumnState columnState = m_ColumnStates[i];
		newArray.Add(columnState);
	}

	for ( i = 0; i < sDestination.GetSize(); ++i )
	{
		CStringArray sDestination2;
		SplitString(sDestination.GetAt(i), sDestination2, '\2');
		if ( sDestination2.GetSize() != 4 )
		{
			continue;
		}

		mapIter = mapHeader.find(sDestination2.GetAt(0));
		if ( mapIter != mapHeader.end() && mapIter->second < newArray.GetSize() )
		{
			newArray[mapIter->second].m_OrgPosition	 = _ttoi(sDestination2.GetAt(1));
			newArray[mapIter->second].m_OrgWidth	 = _ttoi(sDestination2.GetAt(2));
			newArray[mapIter->second].m_Visible		 = _ttoi(sDestination2.GetAt(3));
		}
		else
		{
			return;
		}
	}

	m_ColumnStates.RemoveAll();
	for ( i = 0; i < newArray.GetSize(); ++i )
	{
		ColumnState columnState = newArray[i];
		m_ColumnStates.Add(columnState);
	}

	int* pOrderArray = new int[nHeaderCount];
	int nIndexState = 0;
	for ( nIndexState = 0; nIndexState < m_ColumnStates.GetSize(); ++nIndexState )
	{
		pOrderArray[m_ColumnStates[nIndexState].m_OrgPosition] = nIndexState;
	}

	for ( nIndexState = 0; nIndexState < nHeaderCount; ++nIndexState )
	{
		if ( !m_ColumnStates[nIndexState].m_Visible )
		{
			m_ColumnStates[nIndexState].m_Visible = true;
			ShowColumn(nIndexState, false);
		}
		else
		{
			ShowColumn(nIndexState, true);
		}
	}

	SetColumnOrderArray(nHeaderCount, pOrderArray);
	delete []pOrderArray;

	if ( m_bNoChangedFirstColumn )
	{
		AddReadOnlyShowState(m_ColumnStates[0].m_strText);
	}
}

void CListCtrlColumnEx::OnDestroy()
{
	CListCtrl::OnDestroy();

	if ( !m_bNeedSaveStatus ) return;
	
	CString strText, strTemp;
	
	int nHeaderCount = m_ColumnStates.GetSize();
	int* pOrderArray = new int[nHeaderCount];
	
	if ( GetColumnOrderArray(pOrderArray, nHeaderCount) )
	{
		for ( int nIndexState = 0; nIndexState < nHeaderCount; ++nIndexState )
		{
			m_ColumnStates[pOrderArray[nIndexState]].m_OrgPosition = nIndexState;
		}
	}
	delete []pOrderArray;
	
	for( int i = 0 ; i < nHeaderCount; ++i )
	{
		strTemp.Format(_T("%s\2%d\2%d\2%d"), m_ColumnStates[i].m_strText, 
			m_ColumnStates[i].m_OrgPosition, 
			m_ColumnStates[i].m_OrgWidth, 
			m_ColumnStates[i].m_Visible);
		
		strText += strTemp;
		if ( i < (nHeaderCount - 1) )
		{
			strText += _T("\1");
		}
	}	
	
	TCHAR szName[1024];
	GetModuleFileName(NULL, szName, 1023);
	TCHAR *pFileName = _tcsrchr(szName, '\\') + 1;
	
	int nCtrlId = GetDlgCtrlID();
	
	CString strIdentifierInReg;
	strIdentifierInReg.Format(_T("%s_%d_%s"), pFileName, nCtrlId, m_strIdentifier);
	
	SetRegister(strIdentifierInReg, strText);
}

void CListCtrlColumnEx::SplitString(LPCTSTR sSource, CStringArray &sDestination, TCHAR cToken)
{
	int     nIndex  = 0;
	BOOL    ret;
	CString subStr;
	sDestination.RemoveAll ();
	while ( TRUE )
	{
		ret = AfxExtractSubString ( subStr , sSource , nIndex , cToken );
		if ( ! ret )
		{
			return;
		}
		if ( ! subStr.IsEmpty () )
		{
			sDestination.Add ( subStr );
		}
		nIndex++;
	}
}

// 功能描述:读取注册表信息
CString CListCtrlColumnEx::GetRegister(LPCTSTR lpszName,LPCTSTR lpszPath)
{
	CString strValue = _T("");
	CString strName = lpszName;
	if(strName.IsEmpty())
		return strValue;
	
	CRegKey regKey;
	try
	{
		if(regKey.Open(HKEY_CURRENT_USER,lpszPath) != ERROR_SUCCESS)
		{//如果不能打开,则退出
			return strValue;
		}
		TCHAR szValue[1024];
		DWORD sl = 1023;
		LONG lReturn = regKey.QueryValue(szValue,lpszName,&sl);
		if( lReturn == ERROR_SUCCESS)
		{
			strValue = szValue;
		}
		regKey.Close();
	}
	catch (...)
	{
		UINT unErro = GetLastError();
		return strValue;
	}
	return strValue;
}

// 功能描述:保存注册表信息
BOOL CListCtrlColumnEx::SetRegister(LPCTSTR lpszName, LPCTSTR lpszValue,LPCTSTR lpszKeyName)
{
	CString strName = lpszName;
	if(strName.IsEmpty())
		return FALSE;
	
	CRegKey regKey;
	try
	{
		if(regKey.Open(HKEY_CURRENT_USER,lpszKeyName) != ERROR_SUCCESS)
		{//如果不能打开,则新建一个
			if(regKey.Create(HKEY_CURRENT_USER,lpszKeyName) != ERROR_SUCCESS)
			{
				//如果建立不成功,返回FALSE
				return FALSE;
			}
		}
		if(regKey.SetValue(lpszValue,lpszName) != ERROR_SUCCESS)
		{
			return FALSE;
		}
		regKey.Close();
	}
	catch (...)
	{
		regKey.Close();
		return FALSE;
	}
	return TRUE;
}

void CListCtrlColumnEx::AddReadOnlyShowState(LPCTSTR pszColumnName)
{
	m_setReadOnlyShowState.insert(pszColumnName);
}

BOOL CListCtrlColumnEx::GetReadOnlyShowState(int i)
{
	std::set<CString>::iterator setIter = m_setReadOnlyShowState.find(GetColumnState(i).m_strText);
	return (setIter != m_setReadOnlyShowState.end());
}

void CListCtrlColumnEx::SetShowRightMenu( BOOL bShowRightMenu )
{
	m_bShowRightMenu = bShowRightMenu;
}

BOOL CListCtrlColumnEx::IsShowRightMenu()
{
	return 	m_bShowRightMenu;
}

int CListCtrlColumnEx::OnToolHitTest(CPoint point, TOOLINFO * pTI) const
{

   //See if the point falls onto a list item
	//UINT nFlags = 0;

	LVHITTESTINFO lvhitTestInfo;

	lvhitTestInfo.pt	= point;

	int nItem = ListView_SubItemHitTest(
			this->m_hWnd,
			&lvhitTestInfo);
	int nSubItem = lvhitTestInfo.iSubItem;

	UINT nFlags =   lvhitTestInfo.flags;

   //nFlags is 0 if the SubItemHitTest fails
   //Therefore, 0 & <anything> will equal false
   if (nFlags & LVHT_ONITEMLABEL){
      //If it did fall on a list item,
      //and it was also hit one of the
      //item specific sub-areas we wish to show tool tips for

      //Get the client (area occupied by this control
      RECT rcClient;
      GetClientRect( &rcClient );

      //Fill in the TOOLINFO structure
      pTI->hwnd = m_hWnd;
      pTI->uId = (UINT) (nItem * 100 + nSubItem);
      pTI->lpszText = LPSTR_TEXTCALLBACK;
      pTI->rect = rcClient;

      return pTI->uId; //By returning a unique value per listItem,
                       //we ensure that when the mouse moves over another list item,
                       //the tooltip will change
   }else{
      //Otherwise, we aren't interested, so let the message propagate
      return -1;
   }
}

BOOL CListCtrlColumnEx::OnToolTipText( UINT id, NMHDR * pNMHDR, LRESULT * pResult )
{
	//Handle both ANSI and UNICODE versions of the message
	TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
	TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;

	//Ignore messages from the built in tooltip, we are processing them internally
	if( (pNMHDR->idFrom == (UINT)m_hWnd) &&
		 ( ((pNMHDR->code == TTN_NEEDTEXTA) && (pTTTA->uFlags & TTF_IDISHWND)) ||
         ((pNMHDR->code == TTN_NEEDTEXTW) && (pTTTW->uFlags & TTF_IDISHWND)) ) ){
      return FALSE;
   }


   *pResult = 0;

	CString strTipText;

	//Get the mouse position
	const MSG* pMessage;
   pMessage = GetCurrentMessage();
	ASSERT ( pMessage );
   CPoint pt;
   pt = pMessage->pt; //Get the point from the message
	ScreenToClient( &pt ); //Convert the point's coords to be relative to this control

   //See if the point falls onto a list item

	LVHITTESTINFO lvhitTestInfo;

	lvhitTestInfo.pt	= pt;

	int nItem = SubItemHitTest(&lvhitTestInfo);
	int nSubItem = lvhitTestInfo.iSubItem;

	UINT nFlags =   lvhitTestInfo.flags;

   //nFlags is 0 if the SubItemHitTest fails
   //Therefore, 0 & <anything> will equal false
	if( nFlags & LVHT_ONITEMLABEL )
	{
	  //If it did fall on a list item,
	  //and it was also hit one of the
	  //item specific sub-areas we wish to show tool tips for

		strTipText = GetItemToolTipText(nItem, nSubItem);
		if( !strTipText.IsEmpty() )
		{
		 //If there was a CString associated with the list item,
		 //copy it's text (up to 80 characters worth, limitation of the TOOLTIPTEXT structure)
		 //into the TOOLTIPTEXT structure's szText member

		 //Deal with UNICODE
			#ifndef _UNICODE
			if (pNMHDR->code == TTN_NEEDTEXTA)
				lstrcpyn(pTTTA->szText, strTipText, 80);
			else
				_mbstowcsz(pTTTW->szText, strTipText, 80);
			#else
			if (pNMHDR->code == TTN_NEEDTEXTA)
				_wcstombsz(pTTTA->szText, strTipText, 80);
			else
				lstrcpyn(pTTTW->szText, strTipText, 80);
			#endif
			return FALSE;    //We found a tool tip,
						 //tell the framework this message has been handled						
		}
	}

   return FALSE; //We didn't handle the message,
                 //let the framework continue propagating the message
}

CString CListCtrlColumnEx::GetItemToolTipText( int nItem, int nSubItem )
{
	if ( nItem < 0 || nSubItem < 0 )
	{
		return _T("");
	}

	return GetItemText(nItem, nSubItem);
}


列头检索功能实例:
BOOL C***::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) 
{
	CHeaderCtrl	*pHeader = NULL;
	NMHEADER	*pNMHdr = (NMHEADER*)lParam;
	int			iFC = HDN_FILTERCHANGE;
	int			iFBC = HDN_FILTERBTNCLICK;

	if( pNMHdr -> hdr.idFrom == IDC_LIST1 )	
	{
		if( pNMHdr -> hdr.code == HDN_FILTERCHANGE )
		{
			pHeader = (CHeaderCtrl *)m_cList.GetHeaderCtrl();

			if( pNMHdr -> iItem == -1 )
			{
				TRACE( _T( "FilterChanged On ALL Columns...\n" ) );
			}
			else											
			{
				TRACE1( _T( "FilterChanged On Column %d...  " ), pNMHdr -> iItem );
			}
			PopulateControlBasedOnFilter();				
		}
		else if( pNMHdr -> hdr.code == HDN_FILTERBTNCLICK )	
		{
			LPNMHDFILTERBTNCLICK	pNMBClick = (LPNMHDFILTERBTNCLICK)pNMHdr;

			TRACE1( _T( "FilterButtonClicked On Column %d...\n" ), pNMBClick -> iItem );

			return( TRUE );
		}
	}
	*pResult = 0;

	return CDialog::OnNotify(wParam, lParam, pResult);
}

void C***::PopulateControlBasedOnFilter()
{
	HDTEXTFILTER	hdTFilter;
	CWaitCursor		WAIT_CURSOR;
	CHeaderCtrl		*pHeader = (CHeaderCtrl *)m_cList.GetHeaderCtrl();
	HDITEM			hdItem;
	int				iColumns = 0;

	_ASSERTE( pHeader );								
	iColumns = pHeader -> GetItemCount();
	
	int nSortColumn = m_cList.GetSortedColumn();
	BOOL bIsAsc = m_cList.IsSortAscending();

	m_cList.SetRedraw( FALSE );
	UpdateListData();

	BOOL bIsAllSpace = TRUE;
	char cFilter[512];
	for( int iCol = 0; iCol < iColumns; iCol++ )		
	{
		hdItem.mask = ( HDI_FILTER );					
		ZeroMemory( cFilter, sizeof( cFilter ) );		
		hdTFilter.pszText = cFilter;					
		hdTFilter.cchTextMax = sizeof( cFilter );		
		hdItem.type = HDFT_ISSTRING;					
		hdItem.pvFilter = &hdTFilter;					

		pHeader -> GetItem( iCol, &hdItem );			
		if( strlen( cFilter ) )							
		{
			bIsAllSpace = FALSE;
			strupr(cFilter);
			for ( int loop = m_cList.GetItemCount() - 1; loop >= 0; --loop )
			{
				CString strValue = m_cList.GetItemText(loop, iCol);
				strValue.MakeUpper();
				
				CString strValuePinYin = strValue;
				CHzBarCode::ChangeHZBarCode(strValuePinYin);
				strValuePinYin.MakeUpper();

				if ( strValue.Find(cFilter) < 0 && strValuePinYin.Find(cFilter) < 0 )
				{
					m_cList.DeleteItem(loop);
				}
			}
		}
	}

	m_cList.SetRedraw( TRUE );
	m_cList.SortItems(nSortColumn, bIsAsc);

	if ( m_cList.GetItemCount() > 0 ) 
	{
		m_cList.SetItemState(0, LVIS_SELECTED, LVIS_SELECTED);
	}
}

General[MFC]当Explorer进程重启后,确保程序的托盘图标还正常显示在任务栏中 Pin
onlinewan26-Apr-10 20:09
onlinewan26-Apr-10 20:09 
General用VC开发截图小工具 Pin
onlinewan20-Apr-10 21:56
onlinewan20-Apr-10 21:56 
GeneralCComboBox的下拉列表实现自适应宽度 Pin
onlinewan13-Apr-10 17:02
onlinewan13-Apr-10 17:02 
General给刚刚毕业或即将毕业的同学一封信---从自身经历谈找工作 Pin
onlinewan30-Mar-09 17:48
onlinewan30-Mar-09 17:48 
General计划为什么不能如期完成? Pin
onlinewan26-Mar-09 17:29
onlinewan26-Mar-09 17:29 
General在ARX中利于Automation打开、新建DWG文件 Pin
onlinewan22-Mar-09 15:33
onlinewan22-Mar-09 15:33 
General收藏 [modified] Pin
onlinewan15-Mar-09 16:19
onlinewan15-Mar-09 16:19 
General企业制度与文化 Pin
onlinewan10-Mar-09 23:06
onlinewan10-Mar-09 23:06 
General2005年百度之星程序设计大赛试题初赛题目三 Pin
onlinewan3-Nov-08 19:06
onlinewan3-Nov-08 19:06 
General2005年百度之星程序设计大赛试题初赛题目二 Pin
onlinewan3-Nov-08 19:02
onlinewan3-Nov-08 19:02 
General2005年百度之星程序设计大赛试题初赛题目一 Pin
onlinewan3-Nov-08 18:47
onlinewan3-Nov-08 18:47 
General实现在Word中查找指定内容,将其替换为指定图片 Pin
onlinewan23-Oct-08 15:12
onlinewan23-Oct-08 15:12 
General实现在Excel中查找指定内容,将其替换为指定图片 Pin
onlinewan23-Oct-08 15:04
onlinewan23-Oct-08 15:04 
General【某公司C++笔试题】 [modified] Pin
onlinewan12-Oct-08 22:57
onlinewan12-Oct-08 22:57 
General四道微软面试算法题 [modified] Pin
onlinewan12-Oct-08 16:28
onlinewan12-Oct-08 16:28 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.