/*
	
	Usage:
		You generally should not use this class directly, though it
		is possible. You need to do two things to use it directly.
		Set m_strUniqueName to someting, and set m_strColumnWidthSection
		to where you want the column widths to be saved.

		The purpose of m_strUniqueName is to allow for saving of
		multiple instances of listview objects. So obviously you would
		need to set this differently for each instance. SetUniqueName must be called
		before calling InsertColumn() or LoadColumnWidths().

		If you are deriving from this class, you need to do the following:
		Add a class to your project derived from CListCtrl, then go into the
		header file and include MultiColumnSortListView.h and change all
		references to CListCtrl to CSortListCtrl. Then in the .cpp
		file of your class, change all the message maps to CSortListCtrl
		instead of CListCtrl. That should do it.

  Compiling:
		One problem you will have is finding IDB_ARROWUP and IDB_ARROWDOWN.
		Those bitmaps will be included with this set of classes, You should
		add them to your project or add your own bitmaps named correctly.
		These are the bitmaps that show the sort order on the header control.

        I hope this is simple enough, kind of a pain to get started but after
		that it should be cake and hopefully it will be useful.
  
  Things to be aware of:
		
		Multithreading:
		     If you delete all the items from another thread
			 in the middle of a sort, it will crash. This is the only
			 bug i have found.
		
		Column Widths:
			 
			MINCOLWIDTH - Minimum width a column can be.
			MAXCOLWIDTH - Maximum width a column can be.
			These are hard coded in the header file. Be aware.
			
			MAXCOLUMNS  - The most columns you can have right
			now is 64, that is only because im use __int64 to
			hold the column sort states. Who needs more than
			64 columns anyway? If you do, you can change it to
			CUIntArray, i just didnt see the need for a whole int
			when all i needed was a bit. 
		        

  Credits:
		
		Iuri Apollonio -- Sorting Class ( great class )
		Zafir Anjum    -- CSortListCtrl::GetColumnCount
		Roger Onslow   -- CSortListCtrl::AutoSizeColumns
		Zafir Anjum    -- CSortableHeaderCtrl::SetSortImage
		Me             -- The Rest, I think.

*/
// MultiColumnSortListView.cpp : implementation file
//

#include "stdafx.h"
#include "apl_gui.h"
#include "SortClass.h"
#include "HtmlGen.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

bool CSortListCtrl::m_bSortingEx = false;

/////////////////////////////////////////////////////////////////////////////
// CSortListCtrl

IMPLEMENT_DYNCREATE(CSortListCtrl, CListCtrl)
/*
When deriving from this class you must set m_strUniqueName to something
this name is used to save each instances column widths to the registry
*/
CSortListCtrl::CSortListCtrl():m_iFillSortData(0)
{	
	m_strUniqueName.Empty();
	m_strColumnWidthSection = _T("ColumnWidths");
	m_bSorting = false;
	m_lColumnSortStates = 0;
	m_AutoSaveColumnsWidth = false;	
	m_SortableList = true;
	
	m_ToolTip.m_text_x_paragr=4;
	m_ToolTip.m_text_y_paragr=1;
	
	// по умолчанию высота строк 17
	m_iRowHeight = 17;
	m_bNoRegColData = false ;
	m_bAutoSizeIfNoRegData = true ;
}

CSortListCtrl::~CSortListCtrl()
{
}


BEGIN_MESSAGE_MAP(CSortListCtrl, CListCtrl)
	//{{AFX_MSG_MAP(CSortListCtrl)
	ON_WM_CREATE()
	ON_WM_DESTROY()
	ON_WM_MOUSEMOVE()
	ON_WM_CONTEXTMENU()
	ON_WM_PAINT()
	ON_WM_KILLFOCUS()
	ON_WM_SETFOCUS()
	ON_MESSAGE(WM_SETFONT, OnSetFont)
	//}}AFX_MSG_MAP
	//ON_NOTIFY(HDN_ITEMCLICKA, 0, OnHeaderClicked) 
	//ON_NOTIFY(HDN_ITEMCLICKW, 0, OnHeaderClicked)
	ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnHeaderClicked)	
	ON_WM_MEASUREITEM_REFLECT()
	ON_WM_MEASUREITEM()
	ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSortListCtrl drawing

void CSortListCtrl::OnDraw(CDC* pDC)
{
}
/////////////////////////////////////////////////////////////////////////////
// CSortListCtrl diagnostics
#ifdef _DEBUG
void CSortListCtrl::AssertValid() const
{
	CListCtrl::AssertValid();
}
void CSortListCtrl::Dump(CDumpContext& dc) const
{
	CListCtrl::Dump(dc);
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CSortListCtrl message handlers

/*
This function saves the columns widths of the listctrl to the registry.
This is called in two places, OnDestroy, and OnEndTrack in the headerCtrl class.
*/
/*
BOOL CSortListCtrl::SetItemDouble( int nItem, int nSubItem, double val, LPCSTR format)
{
	CString buf;
	buf.Format(format,val);
	return SetItemText(nItem,nSubItem,LPCSTR(buf));
}
*/

void CSortListCtrl::SaveColumnWidths()
{	//You must set a unique name for every listctrl
	ASSERT( m_strUniqueName.GetLength() );
	CString strEntry( m_strUniqueName );
	CString strValue;
	int iNumColumns = GetColumnCount();
	for( int i = 0; i < iNumColumns; i++ )
	{
		CString strTemp;
		strTemp.Format(_T("%d,"), GetColumnWidth( i ) );
		strValue += strTemp;
	}
	CWinApp *app=AfxGetApp(); // При вызове из ActiveX возвращает 0
	if(0!=app) app->WriteProfileString( m_strColumnWidthSection, strEntry, strValue );
}

/*
This function loads all the column widths for each column that was saved and applies the width
to the column. This function should be called when you are done inserting data. Or you 
can call SetColumnWidth to set the column width right after you InsertColumn(), If you call 
my InsertColumn it will do this for you.
*/
int CSortListCtrl::LoadColumnWidths()
{	//This function will load all the column widths and apply the widths to each respective column
	int iNumColumns = GetColumnCount();
	for( int i = 0; i < iNumColumns; i++ )
	{
		SetColumnWidth( i );
	}
	return 1;
}

/*
This function is the heart of the class. This will get called automatically
when you click a header, and if you press control while clicking the header,
a multi column sort will take place (ie: sorting the current columns within all the
previously control+clicked columns). The multi column sort saves all the previosuly
control+clicked columns in an array and sorts them in reverse order. So if you click 
column 0, 2, 3, 5 while holding control, it will sort 5, 3, 2, 0. ( this acheives a
muli-column sort).
*/
void CSortListCtrl::SortColumn( int iSubItem, bool bSortingMultipleColumns )
{	
	int iNumCombinedSortedCols = GetNumCombinedSortedColumns();
	m_bSorting = true;
	m_bSortingEx = true;
	m_ctlHeaderCtrl.m_nSortCol = iSubItem;

	if( bSortingMultipleColumns )
	{
		if( NotInCombinedSortedColumnList( iSubItem ) )
			m_aCombinedSortedColumns[ iNumCombinedSortedCols++ ] = iSubItem;
		else
			MoveItemInCombinedSortedListToEnd( iSubItem );

		for( int i = iNumCombinedSortedCols - 1; i >= 0 ; i-- )
		{
			SORT_STATE ssEachItem = GetItemSortState( m_aCombinedSortedColumns[i] );
			if( iNumCombinedSortedCols - 1 != i )
				ssEachItem = (SORT_STATE)!ssEachItem;
			
			CSortClass csc(this, m_aCombinedSortedColumns[i], IsColumnNumeric( m_aCombinedSortedColumns[i] ) );	
			if(ssEachItem) csc.Sort(true); else csc.Sort(false);
			if( i == iNumCombinedSortedCols - 1 )
			{	//Only swap the last column's sort order.
				m_ctlHeaderCtrl.SetSortImage( m_aCombinedSortedColumns[i], ssEachItem );
				SetItemSortState( m_aCombinedSortedColumns[i] , (SORT_STATE)!ssEachItem );			
			}
		}
	}
	else
	{
		m_ctlHeaderCtrl.RemoveAllSortImages();
		EmptyArray(m_aCombinedSortedColumns);
		m_aCombinedSortedColumns[ 0 ] = iSubItem;
		SORT_STATE ssEachItem = GetItemSortState( iSubItem );
		
		CSortClass csc(this, iSubItem, IsColumnNumeric( iSubItem ) );	
		if(ssEachItem) csc.Sort(true); else csc.Sort(false);
		m_ctlHeaderCtrl.SetSortImage( iSubItem, ssEachItem );
		SetItemSortState( iSubItem , (SORT_STATE)!ssEachItem );
	}

	m_bSorting = false;
	m_bSortingEx = false;
}

/*
My version of InsertColumn that will automatically load the last column
width from the registry. 
*/
int CSortListCtrl::InsertColumn(int nCol, LPCTSTR lpszColumnHeading, int nFormat, int nWidth, int nSubItem)
{
	int res=CListCtrl::InsertColumn( nCol, lpszColumnHeading, nFormat, nWidth, nSubItem );
	//SetColumnWidth( nCol );
	//return 1;
	return res;
}
BOOL CSortListCtrl::DeleteColumn( int nCol )
{
	int i;
	for(  i = 0; i < m_aNumericColumns.GetSize(); i++ )
	{	
		if( m_aNumericColumns.GetAt( i ) == (UINT)nCol )
			{m_aNumericColumns.RemoveAt(i); break;}
	}
	for(  i = 0; i < m_aDateColumns.GetSize(); i++ )
	{	
		if( m_aDateColumns.GetAt( i ) == (UINT)nCol )
			{m_aDateColumns.RemoveAt(i); break;}
	}
	for(  i = 0; i < m_aDateTimeColumns.GetSize(); i++ )
	{	
		if( m_aDateTimeColumns.GetAt( i ) == (UINT)nCol )
		{m_aDateTimeColumns.RemoveAt(i); break;}
	}
	for(  i = 0; i < m_aTimeColumns.GetSize(); i++ )
	{	
		if( m_aTimeColumns.GetAt( i ) == (UINT)nCol )
		{m_aTimeColumns.RemoveAt(i); break;}
	}

	return CListCtrl::DeleteColumn( nCol);
}


/*
Utility function to size columns based on its data.
written by Roger Onslow
*/
void CSortListCtrl::AutoSizeColumn( int iColumn )
{
	SetRedraw(false);
	
	CListCtrl::SetColumnWidth(iColumn,LVSCW_AUTOSIZE);
	int wc1 = GetColumnWidth( iColumn );
	CListCtrl::SetColumnWidth(iColumn,LVSCW_AUTOSIZE_USEHEADER);
	int wc2 = GetColumnWidth( iColumn );
	int wc = max(MINCOLWIDTH,max( wc1,wc2 ));
	
	if( wc > MAXCOLWIDTH )
		wc = MAXCOLWIDTH;
	
	CListCtrl::SetColumnWidth( iColumn,wc );  
	SetRedraw(true);
}

/*
Utility function to get rid of all the columns
*/
void CSortListCtrl::DeleteAllColumns()
{
	int iNumCols = GetColumnCount();
	for ( int i = 0; i < iNumCols; i++ )
		CListCtrl::DeleteColumn(0);

	m_aNumericColumns.RemoveAll();
	m_aDateColumns.RemoveAll();
	m_aDateTimeColumns.RemoveAll();
	m_aTimeColumns.RemoveAll();
}

/*
Utility function to get rid of all items.
*/
void CSortListCtrl::DeleteAllItems()
{
	if( CListCtrl::GetItemCount() > 0 )
		CListCtrl::DeleteAllItems();
}

/*
Utility function to get the number of columns
written by Zafir Anjum
*/
UINT CSortListCtrl::GetColumnCount()
{
	CHeaderCtrl *pHeaderCtrl = (CHeaderCtrl*)CListCtrl::GetDlgItem(0);
	return pHeaderCtrl->GetItemCount();
}

/*
Just add some extended styles from the new IE4 stuff.
Of course you can either change the code or change your
derived class's OnCreate to call CListCtrl::OnCreate
*/
int CSortListCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CListCtrl::OnCreate(lpCreateStruct) == -1)
		return -1;
	// set list control's style to hilight the entire row
	DWORD dwStyle = SendMessage(LVM_GETEXTENDEDLISTVIEWSTYLE);
	dwStyle |= LVS_EX_FULLROWSELECT /*| LVS_EX_INFOTIP*/| LVS_EX_GRIDLINES | LVS_EX_HEADERDRAGDROP;
	SendMessage(LVM_SETEXTENDEDLISTVIEWSTYLE, 0, (LPARAM)dwStyle);

	CWnd *pWnd = GetDlgItem(0);

	if(!::IsWindow(m_ctlHeaderCtrl.m_hWnd))
	{
		m_ctlHeaderCtrl.SubclassWindow( pWnd->m_hWnd );		

		m_ctlHeaderCtrl.m_ToolTip=&m_ToolTip;
		m_ToolTip.m_pFontWnd=this;
	}

	if(CaplDefWindowParam::m_win_bk_color!=RGB(0,0,0))
	{
		SetBkColor(CaplDefWindowParam::m_win_bk_color);
		SetTextBkColor(CaplDefWindowParam::m_win_bk_color);
	}
	if(CaplDefWindowParam::m_font_size!=0 || CaplDefWindowParam::m_font_face!=_T(""))
	{
		LOGFONT LogFont;
		CFont *old_font=GetFont();
		if(0!=old_font)
		{
			old_font->GetLogFont(&LogFont);
			if(CaplDefWindowParam::m_font_face!=_T("")) CaplDefWindowParam::CopyFontFaceName(CaplDefWindowParam::m_font_face,LogFont.lfFaceName); 
			if(CaplDefWindowParam::m_font_size!=0)   LogFont.lfHeight=CaplDefWindowParam::m_font_size;
		}
		else CaplDefWindowParam::GetDefaultFont(LogFont);

		//m_my_font.DeleteObject();
		m_my_font.CreateFontIndirect(&LogFont);
		SetFont(&m_my_font,FALSE);
	}	


	return 0;
}

/*
We are only sorting in report view so far.
*/
BOOL CSortListCtrl::PreCreateWindow(CREATESTRUCT& cs) 
{
	cs.style |= LVS_REPORT;
	return CListCtrl::PreCreateWindow(cs);
}

/*
Utility function to tell you if a sort is taking place
*/
const bool CSortListCtrl::IsSorting() const
{
	return CSortListCtrl::m_bSorting;
}

bool& CSortListCtrl::IsSortingEx()
{
	return m_bSortingEx;
}
/*
Utility function to tell you if the control key is being pressed
*/
const int CSortListCtrl::IsControlPressed() const
{
	return (::GetKeyState( VK_CONTROL ) < 0 );
}

/*
Message handler for when a header is clicked.
*/
void CSortListCtrl::OnHeaderClicked(NMHDR* pNMHDR, LRESULT* pResult) 
{
	/*
	HD_NOTIFY *pHDN = (HD_NOTIFY *) pNMHDR;
	if( pHDN->iButton == 0 )
	{
		if( IsControlPressed() )
			SortColumn( pHDN->iItem, MULTI_COLUMN_SORT );
		else
			SortColumn( pHDN->iItem, SINGLE_COLUMN_SORT );
	}
	*pResult = 0;
	*/
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	const int iColumn = pNMListView->iSubItem;

	if(m_SortableList)
	{
		if( IsControlPressed() )
				SortColumn( iColumn, MULTI_COLUMN_SORT );
			else
				SortColumn( iColumn, SINGLE_COLUMN_SORT );
	}
	
	*pResult = 0;
}

/*
Message handler for when control is about to be destroyed.
This is where the column widths are saved.
*/
void CSortListCtrl::OnDestroy() 
{
	if(m_AutoSaveColumnsWidth)
		SaveColumnWidths();	
	CListCtrl::OnDestroy();
}

/*
Utility function to tell you if a column is in the combined sorted list.
*/
bool CSortListCtrl::NotInCombinedSortedColumnList(int iItem) const
{
	int iNumCombinedSortedColumns = GetNumCombinedSortedColumns();
	for( int i = 0; i < iNumCombinedSortedColumns; i++ )
	{
		if( m_aCombinedSortedColumns[i] == iItem )
			return false;
	}
	return true;
}

/*
Utility function to get you the sort state of a column
*/
const SORT_STATE CSortListCtrl::GetItemSortState( int iItem ) const
{
	return (SORT_STATE)((m_lColumnSortStates) & (__int64)( 1 << iItem ));
}

/*
Utility function to set the sort state of a column
*/
void CSortListCtrl::SetItemSortState(int iItem, SORT_STATE bSortState)
{
	if( bSortState != GetItemSortState( iItem ) )
		m_lColumnSortStates ^= ((__int64)1 << iItem);
}

/*
Utility function to get you the number of combined sorted columns
*/
const int CSortListCtrl::GetNumCombinedSortedColumns() const
{
	for( int i = 0; i < MAX_COLUMNS; i++ )
		if( m_aCombinedSortedColumns[i] == -1 )
			return i;
	return 0;
}

/*
Utility function clear some internal arrays
*/
void CSortListCtrl::EmptyArray( int *pArray )
{
	memset( pArray, -1, MAX_COLUMNS );
}

/*
This function will move a clicked column to the end of the combined
column list. This is useful when you move backwards through column clicks.
Like click columns: 0, 1, 2, 1. The array will hold [0,1,2] after the first 3
clicks, this function will change it to [0,2,1] after the 4th click.
*/
void CSortListCtrl::MoveItemInCombinedSortedListToEnd(int iItem)
{
	int iNumCombinedSortedColumns = GetNumCombinedSortedColumns();
	int aCombinedSortedColumns[MAX_COLUMNS];
	memset( aCombinedSortedColumns, -1, MAX_COLUMNS * sizeof(int));
	int iItemIndex = FindItemInCombedSortedList( iItem );
	if( iItemIndex != -1 )
	{
		if( iItemIndex > 0 )
		{
			memcpy( aCombinedSortedColumns, m_aCombinedSortedColumns, iItemIndex * sizeof( int ) );
			memcpy( &aCombinedSortedColumns[iItemIndex], &m_aCombinedSortedColumns[iItemIndex + 1], (iNumCombinedSortedColumns - iItemIndex - 1) * sizeof(int) );
		}
	}
	aCombinedSortedColumns[ iNumCombinedSortedColumns - 1 ] = iItem;
	memcpy( m_aCombinedSortedColumns, aCombinedSortedColumns, MAX_COLUMNS * sizeof(int) );
	for( int i = 0; i < MAX_COLUMNS ; i++ )
	{
		if( aCombinedSortedColumns[i] == -1 )
			break;
	}
}

/*
Utility function to find an item in the combined sorted list.
*/
int CSortListCtrl::FindItemInCombedSortedList( int iItem )
{
	int iNumCombinedSortedColumns = GetNumCombinedSortedColumns();
	for( int i = 0; i < iNumCombinedSortedColumns; i++ )
	{
		if(m_aCombinedSortedColumns[i] == iItem )
			return i;
	}
	return -1;
}

/*
Utility function to look up a columns width in the registry.
*/
const int CSortListCtrl::GetRegColumnWidth( int iColumn ) const
{	//You must set a unique name for each 
	ASSERT( m_strUniqueName.GetLength() );
	
	CString strEntry( m_strUniqueName );
	CString strValue, strSubString;
	CString strSection( m_strColumnWidthSection );
	CWinApp *app=AfxGetApp(); // При вызове из ActiveX возвращает 0
	if(0!=app) strValue =app->GetProfileString( strSection, strEntry, _T("") );
	AfxExtractSubString(strSubString, strValue, iColumn, _T(','));
	return _atoi( (LPCTSTR )strSubString );
}

/*
Utility function to Autosize all columns in the case there is no registry entry.
*/
void CSortListCtrl::AutoSizeAllColumns()
{
	int iNumCols = GetColumnCount();
	for( int i = 0; i < iNumCols; i++ )
	{
		AutoSizeColumn( i );
	}
}

/*
Utility function to set the width on the column based on the registry
value and a set minimum column width.
*/
void CSortListCtrl::SetColumnWidth( int nCol )
{
	int iWidth = GetRegColumnWidth( nCol );
	if( iWidth < MINCOLWIDTH )
	{
		if (m_bAutoSizeIfNoRegData)
		{
			AutoSizeColumn( nCol );
		}
		m_bNoRegColData = true ;
	}
	else
		CListCtrl::SetColumnWidth( nCol, iWidth );
}

/*
Utility function to set a column that will contain only numeric values.
Speeds up the sorting if this is set on the right columns.
*/
void CSortListCtrl::SetColumnNumeric( int iCol )
{
	m_aNumericColumns.Add( iCol );
}
void CSortListCtrl::SetColumnDate( int iCol )
{
	m_aDateColumns.Add( iCol );
}

void CSortListCtrl::SetColumnDateTime( int iCol )
{
	m_aDateTimeColumns.Add( iCol );
}

void CSortListCtrl::SetColumnTime( int iCol )
{
	m_aTimeColumns.Add( iCol );
}

/*
Utility function to tell you if the given column is set as numeric.
*/
const int CSortListCtrl::IsColumnNumeric( int iCol ) const
{
	int i;
	for(  i = 0; i < m_aNumericColumns.GetSize(); i++ )
	{	
		if( m_aNumericColumns.GetAt( i ) == (UINT)iCol )
			return 1;
	}
	for(  i = 0; i < m_aDateColumns.GetSize(); i++ )
	{	
		if( m_aDateColumns.GetAt( i ) == (UINT)iCol )
			return 2;
	}
	for(  i = 0; i < m_aDateTimeColumns.GetSize(); i++ )
	{	
		if( m_aDateTimeColumns.GetAt( i ) == (UINT)iCol )
			return 3;
	}
	for(  i = 0; i < m_aTimeColumns.GetSize(); i++ )
	{	
		if( m_aTimeColumns.GetAt( i ) == (UINT)iCol )
			return 4;
	}
	return 0;
}

/*
Utility function to remove the numeric status of a column
*/
void CSortListCtrl::UnsetColumnNumeric(int iCol)
{
	int iIndex = FindNumericColumnIndex( iCol );
	if( iIndex >= 0 )
		m_aNumericColumns.RemoveAt( iIndex );
}

/*
Utility function to find a numeric column in the array.
*/
int CSortListCtrl::FindNumericColumnIndex( int iCol )
{
	for( int i = 0; i < m_aNumericColumns.GetSize(); i++ )
	{	
		if( m_aNumericColumns.GetAt( i ) == (UINT)iCol )
			return i;
	}
	return -1;
}

void CSortListCtrl::SetUniqueName(LPCTSTR lpszUniqueName)
{
	m_strUniqueName = lpszUniqueName;
	m_AutoSaveColumnsWidth = true;
}

CString CSortListCtrl::GetItemTextVirtual(const CPoint &point, int i, int j)
{
	if(i == -1 || j == -1)
	{
		LVHITTESTINFO lvhti;
		lvhti.pt = point;

		SubItemHitTest(&lvhti);
		i = lvhti.iItem;
		j = lvhti.iSubItem;
	}

	return GetItemText(i, j);
}

void CSortListCtrl::OnMouseMove(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	bool bNeedTestToolTip=true;
	if(m_ToolTip.IsShowed())
	{
		if(!m_TitledRect.PtInRect(point)) m_ToolTip.Hide();
		else 
			bNeedTestToolTip=false;
	}

	CListCtrl::OnMouseMove(nFlags, point);

	if(bNeedTestToolTip)
	{
		int i,j, idx=-1;

		LVHITTESTINFO lvhti;
		lvhti.pt = point;
		SubItemHitTest(&lvhti);
		i=lvhti.iItem;
		j=lvhti.iSubItem;
		if(i>=0 && j>=0)
		{
			CRect rect;
			GetSubItemRect(i,j,LVIR_LABEL,rect);
			//rect.right = rect.left + GetColumnWidth(j);

			rect.left+=1;
			
			ClientToScreen(rect);

			CString text = GetItemTextVirtual(point, i, j);

			if(!text.IsEmpty()) 
			{
				CDC *pDC = GetDC();
				CSize size = pDC->GetTextExtent(text);
//				CPoint pt = point;
//				ClientToScreen(&pt);
				if(-1 != text.Find(_T('\n')) || size.cx>(rect.Width()-m_ToolTip.m_text_x_paragr)/* && rect.PtInRect(pt)*/)
				{
					//m_ToolTip.m_pFont=GetFont();
					m_ToolTip.Show(rect.left,rect.top, text);
					m_TitledRect=rect;
					ScreenToClient(m_TitledRect);
				}
				else
				{
					//Invalidate();
				}

				ReleaseDC(pDC);
			}
		}
	}	
	
	TRACKMOUSEEVENT		csTME;
	csTME.cbSize = sizeof(csTME);
	csTME.dwFlags = TME_LEAVE;
	csTME.hwndTrack = m_hWnd;
	::_TrackMouseEvent(&csTME);		
}

void CSortListCtrl::OnContextMenu(CWnd* pWnd, CPoint pos )
{
	m_ToolTip.Hide();
	// функция предназначена для перегрузки в потомках
	GetParent()->SendMessage(WM_CONTEXTMENU, (WPARAM)pWnd, MAKELPARAM(pos.x, pos.y) ); 
}

void CSortListCtrl::PreSubclassWindow() 
{
	ModifyStyle(0, LVS_REPORT);
	
	CListCtrl::PreSubclassWindow();

	DWORD dwStyle = SendMessage(LVM_GETEXTENDEDLISTVIEWSTYLE);
	dwStyle |= LVS_EX_FULLROWSELECT /*| LVS_EX_INFOTIP*/| LVS_EX_GRIDLINES | LVS_EX_HEADERDRAGDROP;
	SendMessage(LVM_SETEXTENDEDLISTVIEWSTYLE, 0, (LPARAM)dwStyle);

	if(m_ctlHeaderCtrl.m_ToolTip!=&m_ToolTip)
	{
		CWnd* pWnd = GetHeaderCtrl();//GetDlgItem(0);
		if(pWnd!=NULL)
		{
			if(FromHandlePermanent(pWnd->m_hWnd))
				m_ctlHeaderCtrl.SubclassWindow( pWnd->UnsubclassWindow());
			else
				m_ctlHeaderCtrl.SubclassWindow( pWnd->m_hWnd );
		}

		m_ctlHeaderCtrl.m_ToolTip=&m_ToolTip;
		m_ToolTip.m_pFontWnd=this;
	}	
}

BOOL CSortListCtrl::PreTranslateMessage(MSG* pMsg) 
{
	if(pMsg!=0)
	{
		if(m_ToolTip.IsNeedHideOnMessage(pMsg->message))
			m_ToolTip.Hide();
	}
	return CListCtrl::PreTranslateMessage(pMsg);
}

void CSortListCtrl::SetHeaderAutoHigh(bool bUseMultiLine)
{
	m_ctlHeaderCtrl.SetAutoHigh(bUseMultiLine);
}

void CSortListCtrl::ReSort()
{
	int iNumCombinedSortedCols = GetNumCombinedSortedColumns();
	m_bSorting = true;
	
	for( int i = iNumCombinedSortedCols - 1; i >= 0 ; i-- )
	{
		SORT_STATE ssEachItem = GetItemSortState( m_aCombinedSortedColumns[i] );
		ssEachItem = (SORT_STATE)!ssEachItem;
		
		CSortClass csc(this, m_aCombinedSortedColumns[i], IsColumnNumeric( m_aCombinedSortedColumns[i] ) );	
		if(ssEachItem) csc.Sort(true); else csc.Sort(false);
		if( i == iNumCombinedSortedCols - 1 )
		{	//Only swap the last column's sort order.
			m_ctlHeaderCtrl.SetSortImage( m_aCombinedSortedColumns[i], ssEachItem );
		}
	}
}


//*******************************************************************
//*******************************************************************
//*******************************************************************

bool AFX_EXT_API apl_SaveListCtrlToFile(CListCtrl *list, const TCHAR *file, const TCHAR *report_header,bool force_save_all,
										bool force_save_selected,bool showSaveDlg,const TCHAR *fieldSeparator,
										const TCHAR *initPath,bool saveHeader, CString *filePath, int iOpenMode)
{
	if(list==0) return false;
	CString file_name=file;
	file_name.Replace(_T(':'),_T(' '));
	file_name.Replace(_T('*'),_T(' '));
	file_name.Replace(_T('/'),_T(' '));

	CString FileName = _T("");
	CString ext=_T("");
	if (!iOpenMode)
	{
		if(file && file_name!=_T(""))
		{
			CFileFind ff;
			CString full_file_path;
			if(initPath) full_file_path=initPath+file_name;
			else full_file_path=file_name;
			BOOL fileFound=ff.FindFile(full_file_path);
			if(fileFound) ff.FindNextFile();
			if(fileFound && !ff.IsDirectory())
			{
				FileName=ff.GetFilePath();
				int dotIndex=FileName.ReverseFind(_T('.'));
				if(dotIndex>-1) ext=FileName.Right(FileName.GetLength()-dotIndex-1);
				else showSaveDlg=true;
			}
			else 
			{
				showSaveDlg=true;
			}
		}
		if(showSaveDlg)
		{
			CFileDialog save_dialog(FALSE, _T("txt"), LPCTSTR(file_name), OFN_EXPLORER|OFN_OVERWRITEPROMPT,
				APL_T("Текстовые файлы CSV (*.csv)|*.csv|Текстовые файлы (*.txt)|*.txt|Файлы HTML (*.html; *.htm)|*.html; *.htm|Все файлы (*.*)|*.*||"));
			save_dialog.m_ofn.lpstrInitialDir=initPath;
			if (save_dialog.DoModal() == IDOK)		
			{
				FileName = save_dialog.GetPathName();
				ext = save_dialog.GetFileExt();

				if(filePath)
					*filePath = save_dialog.GetPathName();			
			}
			else
			{
				return false;
			}
		}
	}
	else if (iOpenMode == 1)
	{
		FileName=file;
		int dotIndex=FileName.ReverseFind(_T('.'));
		if(dotIndex>-1) ext=FileName.Right(FileName.GetLength()-dotIndex-1);
	}
	else
	{
		ASSERT(FALSE);
	}
	
	BOOL res;
	if(list->GetSelectedCount()>0)
	{
		if((!force_save_all && !force_save_selected) || 
							(force_save_selected && force_save_all))
		{
			if(AfxGetMainWnd()==0)return false;
			res = MessageBox(AfxGetMainWnd()->m_hWnd,  APL_T("Сохранить только выделенные строки?\n Для сохранения всей таблицы нажмите \"Нет\""),  APL_T("Сохранение таблицы"), MB_YESNOCANCEL|MB_ICONQUESTION);
			if(res==IDCANCEL) return false;
			if(res==IDNO){force_save_all = true;force_save_selected=false;}
			if(res==IDYES){force_save_all = false;force_save_selected=true;}
		}
	}else if(force_save_selected){
		res = AfxMessageBox( APL_T("Нет выделенных строк!"));
		return false;
	}
	ext.MakeLower();
	if(ext==_T("html") || ext==_T("htm"))
	{
		if(CHtmlGen::CreateEmptyTitle(FileName)<0) return false;
		BYTE flag = HG_ADD_TO_FILE;
		if(force_save_selected) flag|=HG_SELECTED_ITEMS;
		CHtmlGen::GenerateByListCtrl(list,FileName,flag);
		CHtmlGen::CreateEmptyPS(FileName);
		return true;
	}

	UINT nOpenFlags = 0 ;
	if (iOpenMode == 0)
	{
		nOpenFlags = CFile::modeCreate | CFile::modeWrite | CFile::typeText;
	}
	else if (iOpenMode == 1)
	{
		nOpenFlags = CFile::modeReadWrite | CFile::typeText | CFile::modeNoTruncate;
	}
	else
	{
		ASSERT(FALSE);
	}

	CaplStringFile save_file(FileName, nOpenFlags);

	if (iOpenMode == 1)
	{
		//save_file.SeekToEnd() ;
		save_file.Load2Memory();
	}

	CString line;
	// запись поясняющего текста
	CString razd=_T("\t");
	if(ext==_T("csv")) 
	{
		if(fieldSeparator) razd=fieldSeparator;
		else
		{
			CRegKey rKey;
			if(rKey.Open(HKEY_CLASSES_ROOT, _T("Applications\\EXCEL.EXE\\shell\\Open\\command"), KEY_READ)==ERROR_SUCCESS)
			{
				ULONG BufSize = 1024;
				CString sVal;
				rKey.QueryStringValue(_T(""), sVal.GetBuffer(1024), &BufSize);
				sVal.ReleaseBufferSetLength(BufSize);
				sVal.MakeLower();
				sVal = sVal.Left(sVal.Find(_T(".exe"))+4);
				sVal.Trim(_T("\""));
				DWORD dwVerSize(GetFileVersionInfoSize(sVal, 0));
				char *pBuf = new char[dwVerSize];
				BOOL bRes = GetFileVersionInfo(sVal, 0, dwVerSize, pBuf);
				VS_FIXEDFILEINFO *vxFFInfo(NULL);
				UINT size = sizeof(VS_FIXEDFILEINFO);
				VerQueryValue(pBuf, _T("\\"), (LPVOID*)&vxFFInfo, &size);
				DWORD dwTmp (11);
				if(vxFFInfo)//если excel.exe не существует, то эта ..ня валится
					dwTmp= HIWORD(vxFFInfo->dwProductVersionMS);
/*				А это позволяет получить полный номер версии вида  XX.XX.XX.XX (например у Excel 11.0.8316.0)
				dwTmp = LOWORD(vxFFInfo->dwProductVersionMS);
				dwTmp = HIWORD(vxFFInfo->dwProductVersionLS);
				dwTmp = LOWORD(vxFFInfo->dwProductVersionLS);
*/
				delete []pBuf;
				if(dwTmp<11)
					razd=_T(",");
				else
					razd=_T(";");
				rKey.Close();
			}
			else
				razd=_T(";");
		}

	}
	
	// запись заголовка файла
	if(report_header!=0)
	{
		if(report_header[0]!=_T('\0'))
		{
			save_file.WriteString(report_header);
			save_file.WriteString(_T("\n\n"));	
		}
	}

	CHeaderCtrl* pHeadC = list->GetHeaderCtrl();
	int SubItemCount = pHeadC->GetItemCount();
	if(saveHeader)
	{
		// запись заголовка таблицы
		HDITEM HeaderItem;
		TCHAR buf[256];
		line = _T("");
		for (int i = 0; i < SubItemCount; i++)
		{
			if (i) line += razd;
			line +=_T('"');
			memset(buf,0,256);
			HeaderItem.mask = HDI_TEXT;
			HeaderItem.pszText = buf;
			HeaderItem.cchTextMax = 256;
			pHeadC->GetItem(i, &HeaderItem);
			line += buf;
			line +=_T('"');
		}
		line += _T("\n\n");
		save_file.WriteString(line);
	}

	// Проверка выделенных
	CString buf;
	int nItem= list->GetNextItem( -1 , LVNI_ALL | LVNI_SELECTED );
	if(nItem==-1 || force_save_all){
		// запись содержимого таблицы
		for (nItem = 0; nItem < list->GetItemCount(); nItem++)
		{
			line = _T("");
			for (int nSubItem = 0; nSubItem < SubItemCount; nSubItem++)
			{
				if (nSubItem) line += razd;
				line +=_T('"');
				buf=list->GetItemText(nItem, nSubItem);
				buf.Replace(_T('\n'),_T(' '));
				buf.Replace(_T('\r'),_T(' '));
				buf.Replace(_T("\""),_T("\"\""));
				line += buf;
				line +=_T('"');
			}
			line += _T("\n");
			save_file.WriteString(line);
		}		
	}else{
		while( nItem!= -1){
			line = _T("");
			for (int nSubItem = 0; nSubItem < SubItemCount; nSubItem++)
			{
				if (nSubItem) line += razd;
				line +=_T('"');
				buf=list->GetItemText(nItem, nSubItem);
				buf.Replace(_T('\n'),_T(' '));
				buf.Replace(_T('\r'),_T(' '));
				buf.Replace(_T("\""),_T("\"\""));
				line += buf;
				line +=_T('"');
			}
			line += _T("\n");
			save_file.WriteString(line);
			nItem= list->GetNextItem( nItem , LVNI_ALL | LVNI_SELECTED );
		}
	}

	save_file.Close();
	return true;
}

int CSortListCtrl::GetMainSortColumn() const
{
	return m_aCombinedSortedColumns[0];
}

void CSortListCtrl::SetRowHeight(int iHeight)
{
	m_iRowHeight = iHeight;
	SetFont(GetFont());
}

void CSortListCtrl::SetRowHeightVisibleColumns(int iColCount)
{
	RECT rc1, rc2;
	GetHeaderCtrl()->GetClientRect(&rc1);
	GetClientRect(&rc2);

	m_iRowHeight = (rc2.bottom - rc1.bottom) / iColCount;
	SetFont(GetFont());
}

void CSortListCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 
{
	int m_nHighlight = HIGHLIGHT_ROW;
	CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
	CRect rcItem(lpDrawItemStruct->rcItem);
	int nItem = lpDrawItemStruct->itemID;
	CImageList* pImageList;

	// Save dc state
	int nSavedDC = pDC->SaveDC();

	// Get item image and state info
	LV_ITEM lvi;
	lvi.mask = LVIF_IMAGE | LVIF_STATE;
	lvi.iItem = nItem;
	lvi.iSubItem = 0;
	lvi.stateMask = 0xFFFF;		// get all state flags
	GetItem(&lvi);

	// Should the item be highlighted
	BOOL bHighlight =((lvi.state & LVIS_DROPHILITED)
				|| ( (lvi.state & LVIS_SELECTED)
					&& (
							(GetFocus() == this) ||
							(GetStyle() & LVS_SHOWSELALWAYS)
						)
					)
				);
	
	BOOL bFocused = lvi.state & LVIS_FOCUSED && (GetFocus() == this);

	/*
	BOOL bHighlight = FALSE;
					)
	BOOL bFocused = TRUE;

	if (lvi.state & LVIS_DROPHILITED)
		bHighlight = TRUE;

	if (GetStyle() & LVS_SHOWSELALWAYS)
	{
		if (lvi.state & LVIS_SELECTED && !(lvi.state & LVIS_FOCUSED))
		{
			bHighlight = TRUE;
			bFocused = FALSE;
		}
		else if (lvi.state & LVIS_SELECTED && (lvi.state & LVIS_FOCUSED))
		{
			bHighlight = TRUE;
			bFocused = TRUE;
		}
	}
	else
	{
		if (lvi.state & LVIS_SELECTED && (lvi.state & LVIS_FOCUSED))
		{
			bHighlight = TRUE;
			bFocused = TRUE;
		}
	}
	*/

	/*
	if (lvi.state & LVIS_SELECTED)
	{
		if (GetFocus() != this)
		{
			if (GetStyle() & LVS_SHOWSELALWAYS)
			{
				bHighlight = TRUE;
				bFocused = FALSE;
			}
			else
			{
				bHighlight = FALSE;
				bFocused = FALSE;
			}
		}
		else
		{
			bHighlight = TRUE;
			bFocused = TRUE;
		}
	}
	*/


	// Get rectangles for drawing
	CRect rcBounds, rcLabel, rcIcon;
	GetItemRect(nItem, rcBounds, LVIR_BOUNDS);
	GetItemRect(nItem, rcLabel, LVIR_LABEL);
	GetItemRect(nItem, rcIcon, LVIR_ICON);
	CRect rcCol( rcBounds );


	CString sLabel = GetItemText( nItem, 0 );

	// Labels are offset by a certain amount  
	// This offset is related to the width of a space character
	int offset = pDC->GetTextExtent(_T(" "), 1 ).cx * 2;

	CRect rcHighlight;
	CRect rcWnd;
	int nExt;
	switch( m_nHighlight )
	{
	case 0:
		nExt = pDC->GetOutputTextExtent(sLabel).cx + offset;
		rcHighlight = rcLabel;
		if( rcLabel.left + nExt < rcLabel.right )
			rcHighlight.right = rcLabel.left + nExt;
		break;
	case 1:
		rcHighlight = rcBounds;
		rcHighlight.left = rcLabel.left;
		break;
	case 2:
		GetClientRect(&rcWnd);
		rcHighlight = rcBounds;
		rcHighlight.left = rcLabel.left;
//		rcHighlight.right = rcWnd.right; //если оставить, перерисовывается с глюками
		break;
	default:
		rcHighlight = rcLabel;
	}
	
	// Draw the background color
	if( bHighlight )
	{
		pDC->SetBkColor(::GetSysColor(COLOR_HIGHLIGHT));

		if (bFocused)
		{
			pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
			pDC->FillRect(rcHighlight, &CBrush(::GetSysColor(COLOR_HIGHLIGHT)));
		}
		else
		{
			pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
			pDC->FillRect(rcHighlight, &CBrush(::GetSysColor(COLOR_BTNFACE)));
		}
	}
	else
		pDC->FillRect(rcHighlight, &CBrush(::GetSysColor(COLOR_WINDOW)));

	// Set clip region
	rcCol.right = rcCol.left + GetColumnWidth(0);
	CRgn rgn;
	rgn.CreateRectRgnIndirect(&rcCol);
	pDC->SelectClipRgn(&rgn);
	rgn.DeleteObject();

	// Draw state icon
	if (lvi.state & LVIS_STATEIMAGEMASK)
	{
		int nImage = ((lvi.state & LVIS_STATEIMAGEMASK)>>12) - 1;
		pImageList = GetImageList(LVSIL_STATE);
		if (pImageList)
		{
			pImageList->Draw(pDC, nImage,
				CPoint(rcCol.left, rcCol.top), ILD_TRANSPARENT);
		}
	}

	// Draw normal and overlay icon
	pImageList = GetImageList(LVSIL_SMALL);
	if (pImageList)
	{
		IMAGEINFO info;
		pImageList->GetImageInfo(0, &info);
		
		UINT nOvlImageMask=lvi.state & LVIS_OVERLAYMASK;
		CPoint ico_point;
		ico_point.x = rcIcon.left;
		//ico_point.y = rcIcon.top;
		ico_point.y = rcIcon.top + (m_iRowHeight - info.rcImage.bottom) / 2;
		pImageList->Draw(pDC, lvi.iImage, ico_point,
			(bHighlight ? ILD_BLEND50:0) | ILD_TRANSPARENT | nOvlImageMask );
	}



	// Draw item label - Column 0
	rcLabel.left += offset >> 1;
	rcLabel.right -= offset;

	CRect tmp(rcLabel);
	tmp.bottom = tmp.top;
	pDC->DrawText(sLabel, tmp, DT_LEFT | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_CALCRECT);
	
	rcLabel.top += (m_iRowHeight - tmp.Height()) >> 1;
	
	pDC->DrawText(sLabel, rcLabel, DT_LEFT | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_VCENTER);

	//pDC->DrawText(sLabel,-1, rcLabel, DT_LEFT | DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP
	//			| DT_VCENTER | DT_END_ELLIPSIS);


	// Draw labels for remaining columns
	LV_COLUMN lvc;
	lvc.mask = LVCF_FMT | LVCF_WIDTH;

	if( m_nHighlight == 0 )		// Highlight only first column
	{
		pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
		pDC->SetBkColor(::GetSysColor(COLOR_WINDOW));
	}

	rcBounds.right = rcHighlight.right > rcBounds.right ? rcHighlight.right :
							rcBounds.right;
	rgn.CreateRectRgnIndirect(&rcBounds);
	pDC->SelectClipRgn(&rgn);

	for(int nColumn = 1; GetColumn(nColumn, &lvc); nColumn++)
	{
		rcCol.left = rcCol.right;
		rcCol.right += lvc.cx;

		// Draw the background if needed
		if( m_nHighlight == 0 )
			pDC->FillRect(rcCol, &CBrush(::GetSysColor(COLOR_WINDOW)));

		sLabel = GetItemText(nItem, nColumn);
		if (sLabel.GetLength() == 0)
			continue;

		// Get the text justification
		UINT nJustify = DT_LEFT;
		switch(lvc.fmt & LVCFMT_JUSTIFYMASK)
		{
		case LVCFMT_RIGHT:
			nJustify = DT_RIGHT;
			break;
		case LVCFMT_CENTER:
			nJustify = DT_CENTER;
			break;
		default:
			break;
		}

		rcLabel = rcCol;
		rcLabel.left += offset;
		rcLabel.right -= offset;
		
		tmp = rcLabel;
		tmp.bottom = tmp.top;
		pDC->DrawText(sLabel, tmp, nJustify | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_CALCRECT);

		rcLabel.top += (m_iRowHeight - tmp.Height()) >> 1;

		pDC->DrawText(sLabel, rcLabel, nJustify | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_VCENTER);
	}

	// Draw focus rectangle if item has focus
	if (lvi.state & LVIS_FOCUSED && (GetFocus() == this))
		pDC->DrawFocusRect(rcHighlight);


	// Restore dc
	pDC->RestoreDC( nSavedDC );
}

void CSortListCtrl::RepaintSelectedItems()
{
	CRect rcBounds, rcLabel;
	
	// Invalidate focused item so it can repaint 
	int nItem = GetNextItem(-1, LVNI_FOCUSED);
	
	if(nItem != -1)
	{
		GetItemRect(nItem, rcBounds, LVIR_BOUNDS);
		GetItemRect(nItem, rcLabel, LVIR_LABEL);
		rcBounds.left = rcLabel.left;
		
		InvalidateRect(rcBounds, FALSE);
	}
	
	// Invalidate selected items depending on LVS_SHOWSELALWAYS
	if(!(GetStyle() & LVS_SHOWSELALWAYS))
	{
		for(nItem = GetNextItem(-1, LVNI_SELECTED);
		nItem != -1; nItem = GetNextItem(nItem, LVNI_SELECTED))
		{
			GetItemRect(nItem, rcBounds, LVIR_BOUNDS);
			GetItemRect(nItem, rcLabel, LVIR_LABEL);
			rcBounds.left = rcLabel.left;
			
			InvalidateRect(rcBounds, FALSE);
		}
	}
	
	UpdateWindow();
}

void CSortListCtrl::OnPaint()
{
	// in full row select mode, we need to extend the clipping region
	// so we can paint a selection all the way to the right
	if (m_nHighlight == HIGHLIGHT_ROW &&
		(GetStyle() & LVS_TYPEMASK) == LVS_REPORT )
	{
		CRect rcBounds;
		GetItemRect(0, rcBounds, LVIR_BOUNDS);
		
		CRect rcClient;
		GetClientRect(&rcClient);
		if(rcBounds.right < rcClient.right)
		{
			CPaintDC dc(this);
			
			CRect rcClip;
			dc.GetClipBox(rcClip);
			
			rcClip.left = min(rcBounds.right - 1, rcClip.left);
			rcClip.right = rcClient.right;
			
			InvalidateRect(rcClip, FALSE);
		}
	}
	
	CListCtrl::OnPaint();
}

void CSortListCtrl::OnKillFocus(CWnd* pNewWnd)
{
	CListCtrl::OnKillFocus(pNewWnd);
	
	// check if we are losing focus to label edit box
	if (pNewWnd != NULL && pNewWnd->GetParent() == this)
		return;
	
	// repaint items that should change appearance
	if ((GetStyle() & LVS_TYPEMASK) == LVS_REPORT)
		RepaintSelectedItems();
}

void CSortListCtrl::OnSetFocus(CWnd* pOldWnd)
{
	CListCtrl::OnSetFocus(pOldWnd);
	
	// check if we are getting focus from label edit box
	if (pOldWnd != NULL && pOldWnd->GetParent() == this)
		return;
	
	// repaint items that should change appearance
	if ((GetStyle() & LVS_TYPEMASK) == LVS_REPORT)
		RepaintSelectedItems();
}

void CSortListCtrl::MeasureItem ( LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
	LOGFONT lf;
	::ZeroMemory(&lf, sizeof(lf));
	
	CFont* pFont = GetFont();
	ASSERT_VALID(pFont);
	
	if (pFont)
		VERIFY(pFont->GetLogFont(&lf));
	
	lpMeasureItemStruct->itemHeight = m_iRowHeight;

}

void CSortListCtrl::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) 
{
	CListCtrl::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}

LRESULT CSortListCtrl::OnSetFont(WPARAM wParam, LPARAM lParam)
{
	CRect rc;
	GetWindowRect(&rc);
	
	WINDOWPOS wp;
	wp.hwnd = this->m_hWnd;
	wp.cx = rc.Width() ;
	wp.cy = rc.Height() ;
	wp.flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER;
	
	LRESULT lrReturn(Default());
	
	SendMessage(WM_WINDOWPOSCHANGED, 0, reinterpret_cast<LPARAM> (&wp));
	
	return lrReturn;
}

int CSortListCtrl::GetRowHeight()
{
	return m_iRowHeight;
}

DWORD_PTR CSortListCtrl::GetItemData(_In_ int nItem) const
{
	while(m_iFillSortData==1)
	{
		PumpTimerPaint();
	}

	if(m_iFillSortData==0)
		return CListCtrl::GetItemData(nItem);
	else if(m_iFillSortData==2)
	{
		CSortClass::CSortItem *pItem = (CSortClass::CSortItem*)CListCtrl::GetItemData(nItem);
		if(pItem)
			return pItem->dw;
	}
	return NULL;
}

BOOL CSortListCtrl::SetItemData(_In_ int nItem, _In_ DWORD_PTR dwData)
{
	while(m_iFillSortData==1)
	{
		PumpTimerPaint();
	}

	if(m_iFillSortData!=2)
		return CListCtrl::SetItemData(nItem, dwData);
	else
	{
		CSortClass::CSortItem *pItem = (CSortClass::CSortItem*)CListCtrl::GetItemData(nItem);
		if(pItem)
		{
			pItem->dw = dwData;
			return TRUE;
		}
	}

	return FALSE;
}

BOOL CSortListCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
	BOOL res;
	res=CListCtrl::Create(dwStyle,rect,pParentWnd,nID);
	if(res==TRUE){
		InsertColumn(0,_T("..."),LVCFMT_CENTER,100);
	}
	if(m_strUniqueName.IsEmpty()){
		CString temp_name;
		temp_name.Format(_T("CInstPropertyCtrl_for_ID_%i"),nID);
		SetUniqueName(temp_name);
	}
	//	CRect rc=rect;
	//	GetWindowRect(&rc);
	//	m_my_width=rc.Width();
	return res;
}


BOOL CSortListCtrl::CreateFromStatic(UINT nID, CWnd* pParent,UINT additional_param)
{
	CStatic wndStatic;
	if (!wndStatic.SubclassDlgItem(nID, pParent))
		return FALSE;

	if(m_strUniqueName.IsEmpty()){
		CString temp_name;
		temp_name.Format(_T("CInstPropertyCtrl_for_ID_%i"),nID);
		SetUniqueName(temp_name);
	}
	// Get static control rect, convert to parent's client coords.
	CRect rc;
	wndStatic.GetWindowRect(&rc);
	pParent->ScreenToClient(&rc);
	//	m_my_width=rc.Width();
	wndStatic.DestroyWindow();
	if(Create(WS_VISIBLE|WS_CHILD|WS_BORDER|LVS_SHOWSELALWAYS|additional_param,rc,pParent,nID)!=TRUE)
		return FALSE;
	DWORD style;
	style=ListView_GetExtendedListViewStyle(m_hWnd);
	style=style | LVS_EX_FULLROWSELECT |LVS_EX_SUBITEMIMAGES /*| LVS_EX_GRIDLINES */;
	//style=style ^LVS_EX_HEADERDRAGDROP/*| LVS_EX_GRIDLINES */;
	ListView_SetExtendedListViewStyle(m_hWnd, style);

	return TRUE;

}

bool CSortListCtrl::SetItemDisabled(int nItem)
{
	DWORD dw = CListCtrl::GetItemData(nItem);

	if(dw == 0)
		return false;

	m_mDisabledItems.Add(dw, 1);
	return true;
}

bool CSortListCtrl::GetItemDisabled(int nItem)
{
	DWORD dw = CListCtrl::GetItemData(nItem);

	if(dw == 0)
		return false;

	return m_mDisabledItems.FindByIn(dw) != -1;
}


void CSortListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
	NMLVCUSTOMDRAW* pLVCD = (NMLVCUSTOMDRAW*)(pNMHDR);
	CString str = GetItemText(pLVCD->nmcd.dwItemSpec, 0);

	switch( pLVCD->nmcd.dwDrawStage)
	{
	case CDDS_PREPAINT:
		*pResult = CDRF_NOTIFYITEMDRAW;
		break;

	case CDDS_ITEMPREPAINT:
		{
			if(GetItemDisabled(pLVCD->nmcd.dwItemSpec))
			{
				pLVCD->clrText = RGB(127, 127, 127);

				if(GetItemState(pLVCD->nmcd.dwItemSpec, LVNI_SELECTED) > 0)
				{
					pLVCD->clrTextBk = RGB(191, 191, 191);
                    // We want to draw a cell-focus-rectangle
                    // instead of row-focus-rectangle
					pLVCD->nmcd.uItemState &= ~CDIS_SELECTED;
				}

				pLVCD->nmcd.uItemState &= ~CDIS_FOCUS;
				*pResult = CDRF_NOTIFYPOSTPAINT;
			}
		}
		break;

	default:
		break;
	}
}