Click here to Skip to main content
Click here to Skip to main content
 
Add your own
alternative version
Go to top

Address Book

, 6 Aug 2000
Address Book application
// GfxHeaderCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "Address.h"


#include "GfxHeaderCtrl.h"

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

/////////////////////////////////////////////////////////////////////////////
// CGfxHeaderCtrl

CGfxHeaderCtrl::CGfxHeaderCtrl()
{
	dwSortUp = 0;
	m_nSortCol = -1;
	pImageList = NULL;
	dwDontShowSortArrow = 0;
	dwNoResizeColumn = 0;
	bIsDragAllowed = true;

// Column dragging support
	m_pWidth = NULL;	
	m_bDragging = FALSE;	
	m_bCheckForDrag = FALSE;
	m_fpDragCol = NULL;	
//	m_pOwnerWnd = NULL;
	bEnableDrag = true;
}

CGfxHeaderCtrl::~CGfxHeaderCtrl()
{
}


BEGIN_MESSAGE_MAP(CGfxHeaderCtrl, CHeaderCtrl)
	//{{AFX_MSG_MAP(CGfxHeaderCtrl)
	ON_WM_SETCURSOR()
	ON_WM_LBUTTONDBLCLK()
	ON_WM_NCHITTEST()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CGfxHeaderCtrl message handlers

int CGfxHeaderCtrl::SetSortImage( int nCol, BOOL bAsc )
{
	int nPrevCol = m_nSortCol;

	m_nSortCol = nCol;
	m_bSortAsc = bAsc;

	// Change the item to ownder drawn
	HD_ITEM hditem;

	hditem.mask = HDI_FORMAT;
	GetItem(nCol, &hditem);
	hditem.fmt |= HDF_OWNERDRAW;
	SetItem(nCol, &hditem);

	// Invalidate header control so that it gets redrawn
	Invalidate();
	SetSortOrder(nCol, bAsc == TRUE);
	return nPrevCol;
}

void CGfxHeaderCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 
{
	CDC dc;
	dc.Attach(lpDrawItemStruct->hDC);

	CRect rcLabel(lpDrawItemStruct->rcItem);

	int nSavedDC = dc.SaveDC();

	CRgn rgn;
	rgn.CreateRectRgnIndirect(&rcLabel);
	dc.SelectObject(&rgn);
	rgn.DeleteObject();

	// Draw the background
//	dc.FillRect(rcLabel, &CBrush(::GetSysColor(COLOR_3DFACE)));
	dc.FillSolidRect(rcLabel, GetSysColor(COLOR_3DFACE));
	// Labels are offset by a certain amount  
	// This offset is related to the width of a space character
	int offset = dc.GetTextExtent(_T(" "), 1).cx*2;
	// Get the column text and format
	static TCHAR buf[256];
	HD_ITEM hditem;
	hditem.mask = HDI_TEXT | HDI_FORMAT | HDI_LPARAM;
	hditem.pszText = buf;
	hditem.cchTextMax = 255;

	GetItem(lpDrawItemStruct->itemID, &hditem);

	int iSubItem = hditem.lParam;

	// Determine format for drawing 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)
	{
		rcLabel.left++;
		rcLabel.top += 2;
		rcLabel.right++;
	}

	// Draw column label
	CString cs(buf);

	if (pImageList && cs.Right(1) == "_")
	{
		int iIdx = atoi(cs);
		IMAGEINFO ii;
		if (pImageList->GetImageInfo(iIdx, &ii))
		{
			CRect rcIma(ii.rcImage);
			if (rcLabel.Width() >= rcIma.Width())
			{
				int x = rcLabel.left + (rcLabel.Width() - rcIma.Width()) / 2;
				int y = rcLabel.top + (rcLabel.Height() - rcIma.Height()) / 2;
				pImageList->Draw(&dc, iIdx, CPoint(x,y), ILD_TRANSPARENT);//ILD_NORMAL);rcLabel.TopLeft()
			}
		}
	}
	else
	{
		// Adjust the rect further if Sort arrow is to be displayed
		if (lpDrawItemStruct->itemID == (UINT)m_nSortCol) rcLabel.right -= 3 * offset;

		rcLabel.left += offset;
		rcLabel.right -= offset;

		if (rcLabel.left < rcLabel.right) 
			dc.DrawText(buf,-1,rcLabel, uFormat);
	}

	// Draw the Sort arrow
	if (lpDrawItemStruct->itemID == (UINT)m_nSortCol && !(dwDontShowSortArrow & 1 << iSubItem))//m_nSortCol))
	{
		CRect rcIcon(lpDrawItemStruct->rcItem);

		// Set up pens to use for drawing the triangle
		CPen penLight(PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT));
		CPen penShadow(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW));
		CPen *pOldPen = dc.SelectObject(&penLight);

		if (m_bSortAsc)
		{
			// Draw triangle pointing upwards
			dc.MoveTo(rcIcon.right - 2*offset, offset-1);
			dc.LineTo(rcIcon.right - 3*offset/2, rcIcon.bottom - offset);
			dc.LineTo(rcIcon.right - 5*offset/2-2, rcIcon.bottom - offset);
			dc.MoveTo(rcIcon.right - 5*offset/2-1, rcIcon.bottom - offset-1);

			dc.SelectObject(&penShadow);
			dc.LineTo(rcIcon.right - 2*offset, offset-2);
		}
		else
		{
			// Draw triangle pointing downwords
			dc.MoveTo(rcIcon.right - 3*offset/2, offset-1);
			dc.LineTo(rcIcon.right - 2*offset-1, rcIcon.bottom - offset + 1);
			dc.MoveTo(rcIcon.right - 2*offset-1, rcIcon.bottom - offset);

			dc.SelectObject(&penShadow);
			dc.LineTo(rcIcon.right - 5*offset/2-1, offset -1);
			dc.LineTo(rcIcon.right - 3*offset/2, offset -1);
		}

		// Restore the pen
		dc.SelectObject(pOldPen);
	}

	if ((m_bDragging) && (int)lpDrawItemStruct->itemID == m_nDragCol)//|| m_bCheckForDrag
	{
		CBrush* pBrush = CBrush::FromHandle((HBRUSH) GetStockObject(WHITE_BRUSH));
		HBRUSH hOldBrush = NULL;
		if (pBrush != NULL) hOldBrush = (HBRUSH)SelectObject(dc.m_hDC, pBrush->m_hObject);
		CRect rcx(lpDrawItemStruct->rcItem);
		dc.PatBlt(rcx.left, rcx.top, rcx.Width(), rcx.Height(), PATINVERT);
		if (hOldBrush != NULL) SelectObject(dc.m_hDC, hOldBrush);
	}

	dc.RestoreDC(nSavedDC);
	dc.Detach();
}

bool CGfxHeaderCtrl::GetSortOrder(const int iCol)
{
	return dwSortUp & (1 << iCol) ? true : false;
}

void CGfxHeaderCtrl::SetSortOrder(const int iCol, const bool bAscending)
{
	if (bAscending) dwSortUp |= (1 << iCol);
	else dwSortUp &= ~ (1 << iCol);
}


BOOL CGfxHeaderCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) 
{	
	if (bIsDragAllowed) return CHeaderCtrl::OnSetCursor(pWnd, nHitTest, message);
	SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
	return TRUE;
}

void CGfxHeaderCtrl::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
	if (IsDragAllowed(point)) CHeaderCtrl::OnLButtonDblClk(nFlags, point);
}

UINT CGfxHeaderCtrl::OnNcHitTest(CPoint point) 
{
	CPoint clientPoint = point;	
	ScreenToClient(&clientPoint);
	bIsDragAllowed = IsDragAllowed(clientPoint);
	return CHeaderCtrl::OnNcHitTest(point);
}

bool CGfxHeaderCtrl::IsDragAllowed(CPoint point)
{
	if (dwNoResizeColumn == 0) return true;
	HD_ITEM hi;	
	hi.mask = HDI_WIDTH|HDI_LPARAM;

	int dividerLocations = 0;
	int dragWidth = GetSystemMetrics(SM_CXCURSOR);

	bool allowDrag = true;

	int i, max = GetItemCount();
	for (i = 0; i < max; ++i) 
	{		
		GetItem(i, &hi);
		dividerLocations += hi.cxy;
		if (dwNoResizeColumn & (1 << hi.lParam))//i)
			if (point.x > dividerLocations - dragWidth/2 && point.x < dividerLocations + dragWidth/2)
				allowDrag = false;
	}
	return allowDrag;
}

void CGfxHeaderCtrl::OnMouseMove(UINT nFlags, CPoint point) 
{
	if (bEnableDrag)
	{
		if( (MK_LBUTTON & nFlags) == 0)
		{
			// The left mouse button is not pressed - so reset flags
			m_bCheckForDrag = FALSE;
			m_bDragging = FALSE;
		}
		else if (m_bDragging)
		{
			// Get column number that falls under the mouse
			int i=0, cx = 0;
			if( point.x > 0 )
			{
				for( i = 0; i < GetItemCount(); i++ )
				{
					if( point.x >= cx && point.x < cx + m_pWidth[i] )
						break;
					cx += m_pWidth[i];
				}
			}

			if (i != m_nDropPos)
			{
				m_nDropPos = i;

				CRect rect;
				//GetWindowRect(&rect);
				GetClientRect(&rect);

				// Invalidate area occupied by previous marker
				InvalidateRect(&marker_rect);

				// Draw a new marker
				CClientDC dc(this);
				POINT pts[3];

				CBrush rbr(RGB(255,0,0)), * obr = dc.SelectObject(&rbr);
				CPen rpn(PS_SOLID,1,RGB(255,0,0)), * opn = dc.SelectObject(&rpn);

				pts[0].x = cx; pts[1].x = cx -3; pts[2].x = cx +3;
				pts[0].y = rect.Height() - 8; pts[1].y = pts[2].y = rect.Height()-1;

				dc.Polygon(pts, 3);

				// save marker information
				marker_rect.left = cx - 4;
				marker_rect.top = rect.bottom - 8;
				marker_rect.right = cx + 4;
				marker_rect.bottom = rect.bottom;//Height();

				dc.SelectObject(obr);
				dc.SelectObject(opn);
			}

			{
				CClientDC dc(GetParent());
				CPoint spt(point);
				ClientToScreen(&spt);
				GetParent()->ScreenToClient(&spt);

				CRect rcNewDragColumn(spt.x - rcDragColumn.Width() / 2, spt.y - rcDragColumn.Height() / 2, spt.x + rcDragColumn.Width() / 2, spt.y + rcDragColumn.Height() / 2);
				dc.DrawDragRect(&rcNewDragColumn, CSize(2,2), rcLastDragColumn.IsRectEmpty() ? NULL : &rcLastDragColumn, CSize(2,2));
				rcLastDragColumn = rcNewDragColumn;
			}

			return;
		}
		else if(m_bCheckForDrag)
		{
			// The mouse button was pressed over a column header
			// and now the mouse has moved - so start drag
			m_bCheckForDrag = FALSE;

			m_bDragging = TRUE;
			m_nDropPos = m_nDragCol;

			SetCapture();

			// Store information for later use
			int iCount = GetItemCount();
			HD_ITEM hd_item;
			m_pWidth = new int[iCount];
			for (int i = 0; i < iCount; i++)
			{
				hd_item.mask = HDI_WIDTH|HDI_FORMAT;
				GetItem(i, &hd_item);
				m_pWidth[i] = hd_item.cxy;

				if (!(hd_item.fmt & HDF_OWNERDRAW))
				{
					hd_item.mask = HDI_FORMAT;
					hd_item.fmt |= HDF_OWNERDRAW;
					SetItem(i, &hd_item);
				}
			}
			rcLastDragColumn.SetRect(0,0,0,0);
			DrawDragColumn(m_nDragCol);
			Invalidate();
			return;
		}
	}
	
	CHeaderCtrl::OnMouseMove(nFlags, point);
}

void CGfxHeaderCtrl::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// Determine if mouse was pressed over a column header
	if (bEnableDrag)
	{
		HD_HITTESTINFO hd_hittestinfo;
		hd_hittestinfo.pt = point;
		SendMessage(HDM_HITTEST, 0, (LPARAM)(&hd_hittestinfo));
		if( hd_hittestinfo.flags == HHT_ONHEADER )
		{
			m_nDragCol = hd_hittestinfo.iItem;
			m_bCheckForDrag = TRUE;
			Invalidate();
		}
	}
	
	CHeaderCtrl::OnLButtonDown(nFlags, point);
}

void CGfxHeaderCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
{
	if (bEnableDrag)
	{
		if (m_bDragging)
		{
			if (!(rcLastDragColumn.IsRectEmpty())) 
			{
				rcLastDragColumn.top --;
				GetParent()->InvalidateRect(rcLastDragColumn);
				GetParent()->UpdateWindow();
				rcLastDragColumn.SetRect(0,0,0,0);
			}
			m_bDragging = FALSE;
			delete[] m_pWidth;
			ReleaseCapture();
			if (m_nDragCol != m_nDropPos && m_nDragCol != m_nDropPos -1)
			{
				(GetParent()->*m_fpDragCol)(m_nDragCol, m_nDropPos);

				if (m_nSortCol == m_nDragCol) m_nSortCol = m_nDropPos;
				else if (m_nSortCol == m_nDropPos) m_nSortCol = m_nDragCol;
			}
			Invalidate();
		}
	}
	CHeaderCtrl::OnLButtonUp(nFlags, point);
}

void CGfxHeaderCtrl::SetCallback(void (CWnd::*fpDragCol)(int &, int &))
{	
	m_fpDragCol = fpDragCol;
}


void CGfxHeaderCtrl::DrawDragColumn(const int iCol)
{
	int cx = 0;
	cx = m_pWidth[iCol];

	CRect rc;
	GetClientRect(&rc);

	HD_ITEM hditem;
	hditem.mask = HDI_WIDTH;
	GetItem(iCol, &hditem);

	rcDragColumn.SetRect(0, 0, hditem.cxy, rc.Height());
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Xavier John
Software Developer
United States United States
No Biography provided

| Advertise | Privacy | Mobile
Web02 | 2.8.140921.1 | Last Updated 7 Aug 2000
Article Copyright 1999 by Xavier John
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid