📄 sortlistctrl.cpp
字号:
/*----------------------------------------------------------------------
Copyright (C)2001 MJSoft. All Rights Reserved.
This source may be used freely as long as it is not sold for
profit and this copyright information is not altered or removed.
Visit the web-site at www.mjsoft.co.uk
e-mail comments to info@mjsoft.co.uk
File: SortListCtrl.cpp
Purpose: Provides a sortable list control, it will sort text, numbers
and dates, ascending or descending, and will even draw the
arrows just like windows explorer!
----------------------------------------------------------------------*/
#include "stdafx.h"
#include "..\global.h"
#include "SortListCtrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//#defines
#define FIRST_COLUMN 0
#define MIN_COLUMN_WIDTH 10
#define MAX_DROP_DOWN_ITEM_COUNT 10
CSortListCtrl::CSortListCtrl()
: m_iNumColumns( 0 )
, m_iSortColumn( -1 )
, m_bSortAscending( TRUE )
, m_nLastSelItem ( -1 )
, m_nCurSelItem ( -1 )
, m_nCurSelSubItem ( -1 )
, m_Callback_ListCtrl_AddListString ( NULL )
, m_pCombo ( NULL )
, m_pEdit ( NULL )
, m_wHitMask ( LVHT_ONITEM )
, m_nImageNum ( 0 )
, m_bSaveTypeMask ( FALSE )
, m_dwInitTypeMask ( LVS_REPORT )
, m_nNotSortTailItem ( -1 )
, m_Proc_SortListCtrl_FreeItemMemory ( NULL )
{
}
CSortListCtrl::~CSortListCtrl()
{
m_UIntAry_ColumnType.RemoveAll();
m_StrAry_CommonText.RemoveAll();
DeleteImageList ();
}
BEGIN_MESSAGE_MAP(CSortListCtrl, CListCtrl)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
//{{AFX_MSG_MAP(CSortListCtrl)
ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomdrawList)
ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick)
ON_WM_DESTROY()
ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
ON_WM_HSCROLL()
ON_WM_VSCROLL()
ON_WM_KEYDOWN()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CSortListCtrl message handlers
// the heading text is in the format column 1 text,column 1 width;column 2 text,column 3 width;etc.
BOOL CSortListCtrl::SetHeadings( const CString& strHeadings )
{
int iStart = 0;
SetRedraw(FALSE);
for( ;; )
{
const int iComma = strHeadings.Find( _T(','), iStart );
if( iComma == -1 )
break;
const CString strHeading = strHeadings.Mid( iStart, iComma - iStart );
iStart = iComma + 1;
int iSemiColon = strHeadings.Find( _T(';'), iStart );
if( iSemiColon == -1 )
iSemiColon = strHeadings.GetLength();
const int iWidth = atoi( GetMultiByteChar(strHeadings.Mid( iStart, iSemiColon - iStart )) );
iStart = iSemiColon + 1;
if( InsertColumn( m_iNumColumns, strHeading, LVCFMT_LEFT, iWidth ) == -1 )
{
SetRedraw(TRUE);
return FALSE;
}
m_UIntAry_ColumnType.Add ( E_COLUMN_TYPE_NORMAL );
m_iNumColumns ++;
}
SetRedraw(TRUE);
return TRUE;
}
void CSortListCtrl::FreeItemMemory( const int iItem, BOOL bCallExteriorFreeFunc/*=TRUE*/ )
{
if ( bCallExteriorFreeFunc && m_Proc_SortListCtrl_FreeItemMemory )
m_Proc_SortListCtrl_FreeItemMemory ( this, iItem );
ItemData* pid = reinterpret_cast<ItemData*>( CListCtrl::GetItemData( iItem ) );
if ( !pid ) return;
DELETE_HEAP ( &pid->pAppItemData );
DELETE_HEAP ( &pid->lpToolTipText );
CWnd *pWnd = (CWnd*)pid->pShowRecordWnd;
if ( pWnd && ::IsWindow ( pWnd->m_hWnd ) )
pWnd->DestroyWindow();
DELETE_HEAP ( &pWnd );
pid->pShowRecordWnd = NULL;
if ( pid->pcspUserText )
{
delete pid->pcspUserText;
pid->pcspUserText = NULL;
}
LPCTSTR* arrpsz = pid->arrpsz;
if ( arrpsz )
{
for( int i = 0; i < m_iNumColumns; i++ )
{
if ( arrpsz[ i ] ) delete[] arrpsz[ i ];
}
delete[] arrpsz;
}
delete pid;
VERIFY( CListCtrl::SetItemData( iItem, NULL ) );
}
BOOL CSortListCtrl::DeleteItem( int iItem, BOOL bCallExteriorFreeFunc/*=TRUE*/ )
{
if ( iItem < 0 || iItem >= GetItemCount() )
return FALSE;
// 是禁止编辑的项
if ( !CanEdit ( iItem ) ) return FALSE;
FreeItemMemory( iItem, bCallExteriorFreeFunc );
// Call the base class method
BOOL bRet = CListCtrl::DeleteItem( iItem );
if ( GetItemCount() < 1 ) ClearShowRecordView ();
SyncSRWItem ( iItem );
return bRet;
}
BOOL CSortListCtrl::DeleteAllItems()
{
for( int iItem = 0; iItem < GetItemCount(); iItem ++ )
FreeItemMemory( iItem );
ClearShowRecordView ();
return CListCtrl::DeleteAllItems();
}
BOOL IsNumber( LPCTSTR pszText )
{
ASSERT_VALID_STRING( pszText );
int nLen = lstrlen( pszText );
for( int i = 0; i < nLen; i++ )
if( !_istdigit( pszText[ i ] ) && pszText[ i ]!= '.' )
return FALSE;
return TRUE;
}
int NumberCompare( LPCTSTR pszNumber1, LPCTSTR pszNumber2 )
{
ASSERT_VALID_STRING( pszNumber1 );
ASSERT_VALID_STRING( pszNumber2 );
const double iNumber1 = atof( GetMultiByteChar(pszNumber1) );
const double iNumber2 = atof( GetMultiByteChar(pszNumber2) );
if( iNumber1 < iNumber2 )
return -1;
if( iNumber1 > iNumber2 )
return 1;
return 0;
}
BOOL IsDate( LPCTSTR pszText )
{
ASSERT_VALID_STRING( pszText );
// format should be 99/99/9999.
if( lstrlen( pszText ) != 10 )
return FALSE;
return _istdigit( pszText[ 0 ] )
&& _istdigit( pszText[ 1 ] )
&& pszText[ 2 ] == _T('/')
&& _istdigit( pszText[ 3 ] )
&& _istdigit( pszText[ 4 ] )
&& pszText[ 5 ] == _T('/')
&& _istdigit( pszText[ 6 ] )
&& _istdigit( pszText[ 7 ] )
&& _istdigit( pszText[ 8 ] )
&& _istdigit( pszText[ 9 ] );
}
int DateCompare( CTime cTime1, CTime cTime2 )
{
if( cTime1 < cTime2 )
return -1;
if( cTime1 > cTime2 )
return 1;
return 0;
}
int CALLBACK CSortListCtrl::CompareFunction( LPARAM lParam1, LPARAM lParam2, LPARAM lParamData )
{
CSortListCtrl* pListCtrl = reinterpret_cast<CSortListCtrl*>( lParamData );
ASSERT( pListCtrl->IsKindOf( RUNTIME_CLASS( CListCtrl ) ) );
ItemData* pid1 = reinterpret_cast<ItemData*>( lParam1 );
if ( !pid1 ) return 0;
ItemData* pid2 = reinterpret_cast<ItemData*>( lParam2 );
if ( !pid2 ) return 0;
ASSERT( pid1 );
ASSERT( pid2 );
if ( pListCtrl->m_nNotSortTailItem > 0 )
{
for ( int i=0; i<pListCtrl->m_nNotSortTailItem; i++ )
{
int nNotSortItem = (pListCtrl->GetItemCount()-1) - i;
if ( nNotSortItem >= 0 )
{
if ( pListCtrl->CListCtrl::GetItemData ( nNotSortItem ) == (DWORD)pid2 )
{
return 0;
}
}
}
}
LPCTSTR pszText1 = pid1->arrpsz[ pListCtrl->m_iSortColumn ];
LPCTSTR pszText2 = pid2->arrpsz[ pListCtrl->m_iSortColumn ];
ASSERT_VALID_STRING( pszText1 );
ASSERT_VALID_STRING( pszText2 );
// digit
if( IsNumber( pszText1 ) )
return pListCtrl->m_bSortAscending ? NumberCompare( pszText1, pszText2 ) : NumberCompare( pszText2, pszText1 );
else
{
CTime cTime1, cTime2;
// date or time
if ( ConvertStrToCTime ( (TCHAR*)pszText1, cTime1 ) != 0 &&
ConvertStrToCTime ( (TCHAR*)pszText2, cTime2 ) != 0 )
return pListCtrl->m_bSortAscending ? DateCompare( cTime1, cTime2 ) : DateCompare( cTime2, cTime1 );
// text.
else
return pListCtrl->m_bSortAscending ? lstrcmp( pszText1, pszText2 ) : lstrcmp( pszText2, pszText1 );
}
}
void CSortListCtrl::OnColumnClick( NMHDR* pNMHDR, LRESULT* pResult )
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
const int iColumn = pNMListView->iSubItem;
// if it's a second click on the same column then reverse the sort order,
// otherwise sort the new column in ascending order.
Sort( iColumn, iColumn == m_iSortColumn ? !m_bSortAscending : TRUE );
*pResult = 0;
}
void CSortListCtrl::Sort( int iColumn, BOOL bAscending )
{
m_iSortColumn = iColumn;
m_bSortAscending = bAscending;
// show the appropriate arrow in the header control.
m_ctlHeader.SetSortArrow( m_iSortColumn, m_bSortAscending );
VERIFY( SortItems( CompareFunction, reinterpret_cast<DWORD>( this ) ) );
}
void CSortListCtrl::LoadColumnInfo()
{
DWORD dwInitTypeMask_Old = m_dwInitTypeMask;
ASSERT ( m_csSection.GetLength() > 0 );
// you must call this after setting the column headings.
ASSERT( m_iNumColumns > 0 );
CString strKey;
strKey.Format( _T("%d"), GetDlgCtrlID() );
UINT nBytes = 0;
BYTE* buf = NULL;
if( AfxGetApp()->GetProfileBinary( m_csSection, strKey, &buf, &nBytes ) )
{
if( nBytes > 0 )
{
CMemFile memFile( buf, nBytes );
CArchive ar( &memFile, CArchive::load );
m_ctlHeader.Serialize( ar );
Serialize( ar );
ar.Close();
m_ctlHeader.Invalidate();
}
delete[] buf;
}
if ( m_bSaveTypeMask )
dwInitTypeMask_Old = m_dwInitTypeMask;
SetListView ( dwInitTypeMask_Old );
}
void CSortListCtrl::SaveColumnInfo()
{
if ( m_csSection.GetLength() < 1 ) return;
if ( m_iNumColumns < 1 ) return;
CString strKey;
strKey.Format( _T("%d"), GetDlgCtrlID() );
CMemFile memFile;
CArchive ar( &memFile, CArchive::store );
m_ctlHeader.Serialize( ar );
Serialize( ar );
ar.Close();
DWORD dwLen = (DWORD)memFile.GetLength();
BYTE* buf = memFile.Detach();
VERIFY( AfxGetApp()->WriteProfileBinary( m_csSection, strKey, buf, dwLen ) );
free( buf );
}
void CSortListCtrl::OnDestroy()
{
SaveColumnInfo ();
for( int iItem = 0; iItem < GetItemCount(); iItem ++ )
FreeItemMemory( iItem );
CListCtrl::OnDestroy();
}
BOOL CSortListCtrl::SetItemData( int iItem, DWORD dwData, int nIndex/*=0*/ )
{
ASSERT ( nIndex < MAX_ITEMDATA_COUNT );
if( iItem >= GetItemCount() )
return FALSE;
ItemData* pid = reinterpret_cast<ItemData*>( CListCtrl::GetItemData( iItem ) );
if ( !pid ) return FALSE;
ASSERT ( nIndex >= 0 && nIndex < LENGTH(pid->dwData) );
pid->dwData[nIndex] = dwData;
return TRUE;
}
DWORD CSortListCtrl::GetItemData( int iItem, int nIndex/*=0*/ ) const
{
ASSERT ( nIndex < MAX_ITEMDATA_COUNT );
ASSERT( iItem < GetItemCount() );
ItemData* pid = reinterpret_cast<ItemData*>( CListCtrl::GetItemData( iItem ) );
ASSERT( pid );
ASSERT ( nIndex >= 0 && nIndex < LENGTH(pid->dwData) );
return pid->dwData[nIndex];
}
CSortListCtrl::t_ItemAppData* CSortListCtrl::GetItemAppData(int iItem)
{
ASSERT ( iItem >= 0 && iItem < GetItemCount() );
ItemData* pid = reinterpret_cast<ItemData*>( CListCtrl::GetItemData( iItem ) );
if ( !pid ) return NULL;
return pid->pAppItemData;
}
LPVOID CSortListCtrl::GetItemShowRecordWnd(int iItem)
{
ASSERT ( iItem >= 0 && iItem < GetItemCount() );
ItemData* pid = reinterpret_cast<ItemData*>( CListCtrl::GetItemData( iItem ) );
if ( !pid ) return NULL;
return pid->pShowRecordWnd;
}
BOOL CSortListCtrl::SetItemShowRecordWnd(int iItem, LPVOID pShowRecordWnd)
{
ASSERT ( iItem >= 0 && iItem < GetItemCount() );
ItemData* pid = reinterpret_cast<ItemData*>( CListCtrl::GetItemData( iItem ) );
if ( !pid ) return FALSE;
pid->pShowRecordWnd = pShowRecordWnd;
return TRUE;
}
CString CSortListCtrl::GetItemUserText(int iItem)
{
ASSERT ( iItem >= 0 && iItem < GetItemCount() );
ItemData* pid = reinterpret_cast<ItemData*>( CListCtrl::GetItemData( iItem ) );
if ( !pid || !(pid->pcspUserText) ) return _T("");
return *pid->pcspUserText;
}
BOOL CSortListCtrl::SetItemUserText(int iItem, LPCTSTR lpszUserText)
{
ASSERT ( iItem >= 0 && iItem < GetItemCount() );
ItemData* pid = reinterpret_cast<ItemData*>( CListCtrl::GetItemData( iItem ) );
if ( !pid ) return FALSE;
if ( !pid->pcspUserText ) pid->pcspUserText = new CString;
if ( !pid->pcspUserText ) return FALSE;
*pid->pcspUserText = GET_SAFE_STRING ( lpszUserText );
return TRUE;
}
BOOL CSortListCtrl::GetItemAppData(int iItem, CSortListCtrl::t_ItemAppData &ItemAppData )
{
ASSERT ( iItem >= 0 && iItem < GetItemCount() );
CSortListCtrl::t_ItemAppData *pItemAppData = GetItemAppData ( iItem );
if ( pItemAppData )
{
memcpy ( &ItemAppData, pItemAppData, sizeof(CSortListCtrl::t_ItemAppData) );
return TRUE;
}
memset ( &ItemAppData, 0, sizeof(CSortListCtrl::t_ItemAppData) );
ItemAppData.clrItemBK = RGB(255,255,255);
return FALSE;
}
BOOL CSortListCtrl::SetItemAppData(int iItem, CSortListCtrl::t_ItemAppData &ItemAppData)
{
ASSERT ( iItem >= 0 && iItem < GetItemCount() );
ItemData* pid = reinterpret_cast<ItemData*>( CListCtrl::GetItemData( iItem ) );
if ( !pid ) return FALSE;
if ( !pid->pAppItemData )
{
pid->pAppItemData = new CSortListCtrl::t_ItemAppData;
}
if ( !pid->pAppItemData )
return FALSE;
memcpy ( pid->pAppItemData, &ItemAppData, sizeof(CSortListCtrl::t_ItemAppData) );
return TRUE;
}
CString CSortListCtrl::GetItemToolTipText ( int iItem )
{
ASSERT ( iItem >= 0 && iItem < GetItemCount() );
ItemData* pid = reinterpret_cast<ItemData*>( CListCtrl::GetItemData( iItem ) );
if ( !pid ) return _T("");
return pid->lpToolTipText;
}
BOOL CSortListCtrl::SetItemToolTipText ( int iItem, LPCTSTR lpszToolTipText )
{
ASSERT ( iItem >= 0 && iItem < GetItemCount() );
ASSERT ( lpszToolTipText );
ItemData* pid = reinterpret_cast<ItemData*>( CListCtrl::GetItemData( iItem ) );
if ( !pid ) return FALSE;
if ( pid->lpToolTipText ) delete[] pid->lpToolTipText;
int nStrLen = lstrlen(lpszToolTipText);
if ( nStrLen < 1 ) return TRUE;
pid->lpToolTipText = new TCHAR[nStrLen+1];
if ( !pid->lpToolTipText ) return FALSE;
lstrcpy ( pid->lpToolTipText, lpszToolTipText );
return TRUE;
}
BOOL CSortListCtrl::SetTextArray( int iItem, LPCTSTR* arrpsz )
{
ASSERT( CListCtrl::GetItemData( iItem ) == NULL );
ItemData* pid = new ItemData;
pid->arrpsz = arrpsz;
return CListCtrl::SetItemData( iItem, reinterpret_cast<DWORD>( pid ) );
}
LPCTSTR* CSortListCtrl::GetTextArray( int iItem ) const
{
if( iItem < 0 || iItem >= GetItemCount() )
return NULL;
ItemData* pid = reinterpret_cast<ItemData*>( CListCtrl::GetItemData( iItem ) );
if ( !pid ) return NULL;
return pid->arrpsz;
}
BOOL CSortListCtrl::InitImageList(UINT nNormalBitmapID, UINT nSmallBitmapID/*=0*/, COLORREF crMask/*=RGB(255,255,0)*/)
{
ASSERT ( nNormalBitmapID != 0 && nSmallBitmapID != 0 );
// Load the bitmap
CBitmap bmpNormal, bmpSmall;
bmpNormal.LoadBitmap ( nNormalBitmapID );
bmpSmall.LoadBitmap ( nSmallBitmapID );
BOOL bRet = InitImageListByBmp ( &bmpNormal, &bmpSmall, crMask );
return bRet;
}
BOOL CSortListCtrl::InitImageList ( LPCTSTR lpszNormalBitmapFile, LPCTSTR lpszSmallBitmapFile/*=NULL*/, COLORREF crMask/*=RGB(255,255,0)*/)
{
// Load the bitmap
CBitmap bmpNormal, bmpSmall;
if ( lpszNormalBitmapFile )
::LoadBitmapFromFile ( bmpNormal, lpszNormalBitmapFile );
if ( lpszSmallBitmapFile )
::LoadBitmapFromFile ( bmpSmall, lpszSmallBitmapFile );
BOOL bRet = InitImageListByBmp ( &bmpNormal, &bmpSmall, crMask );
return bRet;
}
BOOL CSortListCtrl::InitImageListByBmp(CBitmap *pbmpNormal, CBitmap *pbmpSmall/*=NULL*/, COLORREF crMask/*=RGB(255,255,0)*/)
{
if ( !SetOrCreateImageList () ) return FALSE;
ASSERT ( pbmpNormal || pbmpSmall );
BOOL bInsertNorOK = InsertBmpToImageList ( m_ImageListNor, pbmpNormal, 32, crMask );
BOOL bInsertSmalOK = InsertBmpToImageList ( m_ImageListSma, pbmpSmall, 16, crMask );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -