www.pudn.com > CListCtrlfjs.rar > SortHeaderCtrl.cpp
#include "stdafx.h"
#include "SortHeaderCtrl.h"
#include "Resource.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
static const CString strOfficeFontName = _T("Tahoma");
static const CString strDefaultFontName = _T("MS Sans Serif");
static const CString strVertFontName = _T("Arial");
static const CString strMarlettFontName = _T("Marlett");
static int CALLBACK FontFamalyProcFonts (const LOGFONT FAR* lplf,
const TEXTMETRIC FAR* /*lptm*/,
ULONG /*ulFontType*/,
LPARAM /*lParam*/)
{
ASSERT (lplf != NULL);
CString strFont = lplf->lfFaceName;
return strFont.CollateNoCase (strOfficeFontName) == 0 ? 0 : 1;
}
CSortHeaderCtrl::CSortHeaderCtrl()
{
m_bIsMousePressed = FALSE;
m_bMultipleSort = FALSE;
m_bAscending = TRUE;
m_nHighlightedItem = -1;
m_nNoTrack=0;
clrBtnFace = ::GetSysColor(COLOR_BTNFACE);
clrBtnShadow = ::GetSysColor(COLOR_BTNSHADOW);
clrBtnHilite = ::GetSysColor(COLOR_BTNHIGHLIGHT);
clrBtnDkShadow = ::GetSysColor(COLOR_3DDKSHADOW);
clrBtnLight = ::GetSysColor(COLOR_3DLIGHT);
clrWindowFrame = ::GetSysColor(COLOR_WINDOWFRAME);
clrBtnText = ::GetSysColor(COLOR_BTNTEXT);
brBtnFace.Detach ();
brBtnFace.CreateSysColorBrush (COLOR_BTNFACE);
//------------------
// Initialize fonts:
//------------------
NONCLIENTMETRICS info;
info.cbSize = sizeof(info);
::SystemParametersInfo (SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);
LOGFONT lf;
memset (&lf, 0, sizeof (LOGFONT));
CWindowDC dc (NULL);
lf.lfCharSet = (BYTE) GetTextCharsetInfo (dc.GetSafeHdc (), NULL, 0);
lf.lfHeight = info.lfMenuFont.lfHeight;
lf.lfWeight = info.lfMenuFont.lfWeight;
lf.lfItalic = info.lfMenuFont.lfItalic;
_tcscpy (lf.lfFaceName, info.lfMenuFont.lfFaceName);
BOOL fUseSystemFont = (info.lfMenuFont.lfCharSet > SYMBOL_CHARSET);
if (!fUseSystemFont)
{
if (::EnumFontFamilies (dc.GetSafeHdc (), NULL, FontFamalyProcFonts, 0) == 0)
_tcscpy (lf.lfFaceName, strOfficeFontName);
else _tcscpy (lf.lfFaceName, strDefaultFontName);
}
fontRegular.CreateFontIndirect (&lf);
}
CSortHeaderCtrl::~CSortHeaderCtrl()
{
}
BEGIN_MESSAGE_MAP(CSortHeaderCtrl, CHeaderCtrl)
//{{AFX_MSG_MAP(CSortHeaderCtrl)
ON_WM_ERASEBKGND()
ON_WM_PAINT()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_CANCELMODE()
ON_WM_SETCURSOR()
//}}AFX_MSG_MAP
ON_MESSAGE(HDM_LAYOUT, OnLayout)
ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CSortHeaderCtrl message handlers
void CSortHeaderCtrl::OnDrawItem (CDC* pDC, int iItem, CRect rect, BOOL bIsPressed, BOOL bIsHighlighted)
{
ASSERT_VALID (this);
ASSERT_VALID (pDC);
const int nTextMargin = 5;
//-------------
// Draw border:
//-------------
if (bIsPressed)
{
pDC->Draw3dRect (rect,clrBtnShadow,clrBtnShadow);
rect.left++; rect.top++;
}else pDC->Draw3dRect (rect,clrBtnHilite,clrBtnShadow);
if (iItem < 0) return;
int nSortVal = 0;
if (m_mapColumnsStatus.Lookup (iItem, nSortVal) && nSortVal != 0)
{
//-----------------
// Draw sort arrow:
//-----------------
CRect rectArrow = rect;
rectArrow.left = rectArrow.right - rectArrow.Height ();
rect.right = rectArrow.left+1;
m_bAscending = nSortVal > 0;
OnDrawSortArrow (pDC, rectArrow);
}
HD_ITEM hdItem;
memset (&hdItem, 0, sizeof (hdItem));
hdItem.mask = HDI_FORMAT | HDI_BITMAP | HDI_TEXT | HDI_IMAGE;
TCHAR szText [256];
hdItem.pszText = szText;
hdItem.cchTextMax = 255;
if (!GetItem (iItem, &hdItem)) return;
CBitmap bmp;
bmp.LoadBitmap(IDB_BACK);
BITMAP bmpInfo;
bmp.GetBitmap(&bmpInfo);
CDC dcMem;
dcMem.CreateCompatibleDC(pDC);
CBitmap *pOldBitmap=dcMem.SelectObject(&bmp);
pDC->StretchBlt(rect.left,rect.top,rect.Width()-1,rect.Height(),&dcMem,0,0,bmpInfo.bmWidth,bmpInfo.bmHeight,SRCCOPY);
dcMem.SelectObject(pOldBitmap);
//-----------------------
// Draw bitmap and image:
//-----------------------
if ((hdItem.fmt & HDF_IMAGE) && hdItem.iImage >= 0)
{
//---------------------------------------
// The column has a image from imagelist:
//---------------------------------------
CImageList* pImageList = GetImageList ();
if (pImageList != NULL)
{
int cx = 0;
int cy = 0;
VERIFY (::ImageList_GetIconSize (*pImageList, &cx, &cy));
CPoint pt = rect.TopLeft ();
pt.x ++;
pt.y = (rect.top + rect.bottom - cy) / 2;
VERIFY (pImageList->Draw (pDC, hdItem.iImage, pt, ILD_NORMAL));
rect.left += cx;
}
}
if ((hdItem.fmt & (HDF_BITMAP | HDF_BITMAP_ON_RIGHT)) && hdItem.hbm != NULL)
{
CBitmap* pBmp = CBitmap::FromHandle (hdItem.hbm);
ASSERT_VALID (pBmp);
BITMAP bmp;
pBmp->GetBitmap (&bmp);
CRect rectBitmap = rect;
if (hdItem.fmt & HDF_BITMAP_ON_RIGHT)
{
rectBitmap.right--;
rect.right = rectBitmap.left = rectBitmap.right - bmp.bmWidth;
}else
{
rectBitmap.left++;
rect.left = rectBitmap.right = rectBitmap.left + bmp.bmWidth;
}
rectBitmap.top += max (0, (rectBitmap.Height () - bmp.bmHeight) / 2);
rectBitmap.bottom = rectBitmap.top + bmp.bmHeight;
pDC->DrawState (rectBitmap.TopLeft (), rectBitmap.Size (), pBmp, DSS_NORMAL);
}
//-----------
// Draw text:
//-----------
if (hdItem.fmt & HDF_STRING)
{
CRect rectLabel = rect;
rectLabel.DeflateRect (nTextMargin, 0);
CString strLabel = hdItem.pszText;
UINT uiTextFlags = DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOPREFIX;
if (hdItem.fmt & HDF_CENTER) uiTextFlags |= DT_CENTER;
else if (hdItem.fmt & HDF_RIGHT) uiTextFlags |= DT_RIGHT;
pDC->SetTextColor(RGB(255,255,255));
pDC->DrawText (strLabel, rectLabel, uiTextFlags);
}
}
//***************************************************************************************
void CSortHeaderCtrl::SetSortColumn (int iColumn, BOOL bAscending, BOOL bAdd)
{
ASSERT_VALID (this);
if (iColumn < 0)
{
m_mapColumnsStatus.RemoveAll ();
return;
}
if (bAdd)
{
if (!m_bMultipleSort)
{
ASSERT (FALSE);
bAdd = FALSE;
}
}
if (!bAdd)
{
m_mapColumnsStatus.RemoveAll ();
}
m_mapColumnsStatus.SetAt (iColumn, bAscending ? 1 : -1);
RedrawWindow ();
}
//***************************************************************************************
void CSortHeaderCtrl::RemoveSortColumn (int iColumn)
{
ASSERT_VALID (this);
m_mapColumnsStatus.RemoveKey (iColumn);
RedrawWindow ();
}
//***************************************************************************************
BOOL CSortHeaderCtrl::OnEraseBkgnd(CDC* /*pDC*/)
{
return TRUE;
}
//***************************************************************************************
void CSortHeaderCtrl::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect rectClip;
dc.GetClipBox (rectClip);
CRect rectClient;
GetClientRect (rectClient);
CDC* pDC = &dc;
BOOL bMemDC = FALSE;
CDC dcMem;
CBitmap bmp;
CBitmap* pOldBmp = NULL;
if (dcMem.CreateCompatibleDC (&dc) &&bmp.CreateCompatibleBitmap (&dc, rectClient.Width (),rectClient.Height ()))
{
//-------------------------------------------------------------
// Off-screen DC successfully created. Better paint to it then!
//-------------------------------------------------------------
bMemDC = TRUE;
pOldBmp = dcMem.SelectObject (&bmp);
pDC = &dcMem;
}
OnFillBackground (pDC);
CFont* pOldFont = (CFont*) pDC->SelectObject (&fontRegular);
ASSERT_VALID (pOldFont);
pDC->SetTextColor (clrBtnText);
pDC->SetBkMode (TRANSPARENT);
CRect rect;
GetClientRect(rect);
CRect rectItem;
int nCount = GetItemCount ();
for (int i = 0; i < nCount; i++)
{
//------------------
// Is item pressed?
//------------------
CPoint ptCursor;
::GetCursorPos (&ptCursor);
ScreenToClient (&ptCursor);
HDHITTESTINFO hdHitTestInfo;
hdHitTestInfo.pt = ptCursor;
int iHit = SendMessage (HDM_HITTEST, 0, (LPARAM) &hdHitTestInfo);
BOOL bIsHighlighted = iHit == i && (hdHitTestInfo.flags & HHT_ONHEADER);
BOOL bIsPressed = m_bIsMousePressed && bIsHighlighted;
GetItemRect (i, rectItem);
CRgn rgnClip;
rgnClip.CreateRectRgnIndirect (&rectItem);
pDC->SelectClipRgn (&rgnClip);
//-----------
// Draw item:
//-----------
OnDrawItem (pDC, i, rectItem, bIsPressed, m_nHighlightedItem == i);
pDC->SelectClipRgn (NULL);
}
//--------------------
// Draw "tail border":
//--------------------
if (nCount == 0)
{
rectItem = rect;
rectItem.right++;
}
else
{
rectItem.left = rectItem.right;
rectItem.right = rect.right + 1;
}
OnDrawItem (pDC, -1, rectItem, FALSE, FALSE);
pDC->SelectObject (pOldFont);
if (bMemDC)
{
//--------------------------------------
// Copy the results to the on-screen DC:
//--------------------------------------
dc.BitBlt (rectClip.left, rectClip.top, rectClip.Width(), rectClip.Height(),
&dcMem, rectClip.left, rectClip.top, SRCCOPY);
dcMem.SelectObject(pOldBmp);
}
}
//***************************************************************************************
void CSortHeaderCtrl::OnFillBackground (CDC* pDC)
{
ASSERT_VALID (this);
ASSERT_VALID (pDC);
CRect rectClient;
GetClientRect (rectClient);
pDC->FillRect (rectClient, &brBtnFace);
}
//***************************************************************************************
void CSortHeaderCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
m_bIsMousePressed = TRUE;
CHeaderCtrl::OnLButtonDown(nFlags, point);
}
//***************************************************************************************
void CSortHeaderCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
m_bIsMousePressed = FALSE;
CHeaderCtrl::OnLButtonUp(nFlags, point);
}
//***************************************************************************************
void CSortHeaderCtrl::OnDrawSortArrow (CDC* pDC, CRect rectArrow)
{
ASSERT_VALID (pDC);
ASSERT_VALID (this);
CPen penLight (1, PS_SOLID,clrBtnHilite);
CPen penDark (1, PS_SOLID,clrBtnDkShadow);
CPen* pPenOld = pDC->SelectObject (&penLight);
ASSERT_VALID (pPenOld);
CBitmap bmp;
if (!m_bAscending) bmp.LoadBitmap(IDB_SORT1);
else bmp.LoadBitmap(IDB_SORT2);
BITMAP bmpInfo;
bmp.GetBitmap(&bmpInfo);
CDC dcMem;
dcMem.CreateCompatibleDC(pDC);
CBitmap *pOldBitmap=dcMem.SelectObject(&bmp);
pDC->StretchBlt(rectArrow.left-1,rectArrow.top,rectArrow.Width(),rectArrow.Height(),&dcMem,0,0,bmpInfo.bmWidth,bmpInfo.bmHeight,SRCCOPY);
dcMem.SelectObject(pOldBitmap);
pDC->SelectObject (pPenOld);
}
//*********************************************************************************
void CSortHeaderCtrl::EnableMultipleSort (BOOL bEnable)
{
ASSERT_VALID (this);
if (m_bMultipleSort == bEnable) return;
m_bMultipleSort = bEnable;
if (!m_bMultipleSort)
{
m_mapColumnsStatus.RemoveAll ();
if (GetSafeHwnd () != NULL) RedrawWindow ();
}
}
//*********************************************************************************
int CSortHeaderCtrl::GetSortColumn () const
{
ASSERT_VALID (this);
if (m_bMultipleSort)
{
TRACE0("Call CSortHeaderCtrl::GetColumnState for muliple sort\n");
ASSERT (FALSE);
return -1;
}
int nCount = GetItemCount ();
for (int i = 0; i < nCount; i++)
{
int nSortVal = 0;
if (m_mapColumnsStatus.Lookup (i, nSortVal) && nSortVal != 0)
{
return i;
}
}
return -1;
}
//*********************************************************************************
BOOL CSortHeaderCtrl::IsAscending () const
{
ASSERT_VALID (this);
if (m_bMultipleSort)
{
TRACE0("Call CBCGHeaderCtrl::GetColumnState for muliple sort\n");
ASSERT (FALSE);
return -1;
}
int nCount = GetItemCount ();
for (int i = 0; i < nCount; i++)
{
int nSortVal = 0;
if (m_mapColumnsStatus.Lookup (i, nSortVal) &&
nSortVal != 0)
{
return nSortVal > 0;
}
}
return i;
}
//*********************************************************************************
int CSortHeaderCtrl::GetColumnState (int iColumn) const
{
int nSortVal = 0;
m_mapColumnsStatus.Lookup (iColumn, nSortVal);
return nSortVal;
}
//**********************************************************************************
void CSortHeaderCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
HDHITTESTINFO hdHitTestInfo;
hdHitTestInfo.pt = point;
int nPrevHighlightedItem = m_nHighlightedItem;
m_nHighlightedItem = SendMessage (HDM_HITTEST, 0, (LPARAM) &hdHitTestInfo);
m_nMouseInItem=hdHitTestInfo.iItem;
if ((nFlags & MK_LBUTTON) == 0)
{
if ((hdHitTestInfo.flags & HHT_ONHEADER) == 0)
{
m_nHighlightedItem = -1;
}
if (nPrevHighlightedItem != m_nHighlightedItem)
{
RedrawWindow ();
}
}
CHeaderCtrl::OnMouseMove(nFlags, point);
}
//*****************************************************************************************
LRESULT CSortHeaderCtrl::OnMouseLeave(WPARAM,LPARAM)
{
if (m_nHighlightedItem >= 0)
{
m_nHighlightedItem = -1;
RedrawWindow ();
}
return 0;
}
//*****************************************************************************************
void CSortHeaderCtrl::OnCancelMode()
{
CHeaderCtrl::OnCancelMode();
if (m_nHighlightedItem >= 0)
{
m_nHighlightedItem = -1;
RedrawWindow ();
}
}
LRESULT CSortHeaderCtrl::OnLayout(WPARAM, LPARAM lParam)
{
LPHDLAYOUT lphdlayout = (LPHDLAYOUT)lParam;
if (m_bStaticBorder)
lphdlayout->prc->right += GetSystemMetrics(SM_CXBORDER)*2;
return CHeaderCtrl::DefWindowProc(HDM_LAYOUT, 0, lParam);
}
void CSortHeaderCtrl::Serialize( CArchive& ar )
{
if( ar.IsStoring() )
{
const int iItemCount = GetItemCount();
if( iItemCount != -1 )
{
ar << iItemCount;
HD_ITEM hdItem = { 0 };
hdItem.mask = HDI_WIDTH;
for( int i = 0; i < iItemCount; i++ )
{
VERIFY( GetItem( i, &hdItem ) );
ar << hdItem.cxy;
}
}
}
else
{
int iItemCount;
ar >> iItemCount;
if( GetItemCount() != iItemCount )
TRACE0( _T("Different number of columns in registry.") );
else
{
HD_ITEM hdItem = { 0 };
hdItem.mask = HDI_WIDTH;
for( int i = 0; i < iItemCount; i++ )
{
ar >> hdItem.cxy;
VERIFY( SetItem( i, &hdItem ) );
}
}
}
}
void CSortHeaderCtrl::SetSortArrow( const int iSortColumn, const BOOL bSortAscending )
{
SetSortColumn (iSortColumn,bSortAscending);
}
BOOL CSortHeaderCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT msg)
{
return m_nMouseInItemhDC ) );
// save the device context.
const int iSavedDC = dc.SaveDC();
// get the column rect.
CRect rc( lpDrawItemStruct->rcItem );
// set the clipping region to limit drawing within the column.
CRgn rgn;
VERIFY( rgn.CreateRectRgnIndirect( &rc ) );
(void)dc.SelectObject( &rgn );
VERIFY( rgn.DeleteObject() );
// draw the background,
// CBrush brush(RGB(100,100,10));
// CBrush brush( GetSysColor( COLOR_3DFACE ) );
// dc.FillRect( rc, &brush );
if(m_bOK)
dc.StretchBlt(rc.left,rc.top,rc.right,rc.bottom,&m_dcBack,0,0,17,17,SRCCOPY);
// get the column text and format.
TCHAR szText[ 256 ];
HD_ITEM hditem;
hditem.mask = HDI_TEXT | HDI_FORMAT;
hditem.pszText = szText;
hditem.cchTextMax = 255;
VERIFY( GetItem( lpDrawItemStruct->itemID, &hditem ) );
// determine the format for drawing the column label.
UINT uFormat = DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP | DT_VCENTER | DT_END_ELLIPSIS ;
if( hditem.fmt & HDF_CENTER)
uFormat |= DT_CENTER;
else if( hditem.fmt & HDF_RIGHT)
uFormat |= DT_RIGHT;
else
uFormat |= DT_LEFT;
// adjust the rect if the mouse button is pressed on it.
if( lpDrawItemStruct->itemState == ODS_SELECTED )
{
rc.left++;
rc.top += 2;
rc.right++;
}
CRect rcIcon( lpDrawItemStruct->rcItem );
const int iOffset = ( rcIcon.bottom - rcIcon.top ) / 4;
// adjust the rect further if the sort arrow is to be displayed.
if( lpDrawItemStruct->itemID == (UINT)m_iSortColumn )
rc.right -= 3 * iOffset;
rc.left += iOffset;
rc.right -= iOffset;
// draw the column label.
if( rc.left < rc.right )
{
dc.SetBkMode(TRANSPARENT);
dc.SetTextColor(RGB(10,10,55));
(void)dc.DrawText( szText, -1, rc, uFormat );
}
// draw the sort arrow.
if( lpDrawItemStruct->itemID == (UINT)m_iSortColumn )
{
// set up the pens to use for drawing the arrow.
if(m_bOK)
{
if( m_bSortAscending )
{
dc.BitBlt(rcIcon.right - 3 * iOffset,0,17,17,&m_dcAsc,0,0,SRCCOPY);
}
else
{
dc.BitBlt(rcIcon.right - 3 * iOffset,0,17,17,&m_dcDec,0,0,SRCCOPY);
}
}
else
{
CPen penLight( PS_SOLID, 1, GetSysColor( COLOR_3DHILIGHT ) );
CPen penShadow( PS_SOLID, 1, GetSysColor( COLOR_3DSHADOW ) );
CPen* pOldPen = dc.SelectObject( &penLight );
if( m_bSortAscending )
{
// draw the arrow pointing upwards.
dc.MoveTo( rcIcon.right - 2 * iOffset, iOffset);
dc.LineTo( rcIcon.right - iOffset, rcIcon.bottom - iOffset - 1 );
dc.LineTo( rcIcon.right - 3 * iOffset - 2, rcIcon.bottom - iOffset - 1 );
(void)dc.SelectObject( &penShadow );
dc.MoveTo( rcIcon.right - 3 * iOffset - 1, rcIcon.bottom - iOffset - 1 );
dc.LineTo( rcIcon.right - 2 * iOffset, iOffset - 1);
}
else
{
// draw the arrow pointing downwards.
dc.MoveTo( rcIcon.right - iOffset - 1, iOffset );
dc.LineTo( rcIcon.right - 2 * iOffset - 1, rcIcon.bottom - iOffset );
(void)dc.SelectObject( &penShadow );
dc.MoveTo( rcIcon.right - 2 * iOffset - 2, rcIcon.bottom - iOffset );
dc.LineTo( rcIcon.right - 3 * iOffset - 1, iOffset );
dc.LineTo( rcIcon.right - iOffset - 1, iOffset );
}
// restore the pen.
(void)dc.SelectObject( pOldPen );
}
}
// restore the previous device context.
VERIFY( dc.RestoreDC( iSavedDC ) );
// detach the device context before returning.
(void)dc.Detach();
}
void CSortHeaderCtrl::Serialize( CArchive& ar )
{
if( ar.IsStoring() )
{
const int iItemCount = GetItemCount();
if( iItemCount != -1 )
{
ar << iItemCount;
HD_ITEM hdItem = { 0 };
hdItem.mask = HDI_WIDTH;
for( int i = 0; i < iItemCount; i++ )
{
VERIFY( GetItem( i, &hdItem ) );
ar << hdItem.cxy;
}
}
}
else
{
int iItemCount;
ar >> iItemCount;
if( GetItemCount() != iItemCount )
TRACE0( _T("Different number of columns in registry.") );
else
{
HD_ITEM hdItem = { 0 };
hdItem.mask = HDI_WIDTH;
for( int i = 0; i < iItemCount; i++ )
{
ar >> hdItem.cxy;
VERIFY( SetItem( i, &hdItem ) );
}
}
}
}
void CSortHeaderCtrl::SetImage(UINT back, UINT asc, UINT dec)
{
m_bOK=TRUE;
CBitmap bmBack,bmAsc,bmDec;
bmBack.LoadBitmap(back);
bmAsc.LoadBitmap(asc);
bmDec.LoadBitmap(dec);
m_dcBack.CreateCompatibleDC(GetDC());
m_dcAsc.CreateCompatibleDC(GetDC());
m_dcDec.CreateCompatibleDC(GetDC());
m_dcBack.SelectObject(&bmBack);
m_dcAsc.SelectObject(&bmAsc);
m_dcDec.SelectObject(&bmDec);
}
*/