www.pudn.com > PercentageCtrl.rar > PercentageCtrl.cpp


// PercentageCtrl1.cpp : implementation file 
// 
 
#include "stdafx.h" 
#include "PercentageCtrl.h" 
#include "MemDC.h" 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
extern const UINT PERCENTAGE_CHANGED = ::RegisterWindowMessage(_T("PERCENTAGE_CHANGED")); 
 
///////////////////////////////////////////////////////////////////////////// 
// PercentageData 
 
PercentageData::PercentageData(UINT pc, CString txt, COLORREF c): 
	original(pc), text(txt), color(c)  
{ 
	//Check for color brightness 
	if (0.3*GetRValue(color)+0.6*GetGValue(color)+0.11*GetBValue(color)<128) 
		textcolor=0x00FFFFFF; 
	else 
		textcolor=0x00000000; 
}; 
 
///////////////////////////////////////////////////////////////////////////// 
// CPercentageCtrl 
 
CPercentageCtrl::CPercentageCtrl() 
{ 
	m_Tracking=FALSE; 
	m_options = PC_HORIZONTAL | PC_ALWAYSSHOWTEXT | PC_HORIZONTALTEXT; 
	m_multiplier = 1; 
} 
 
CPercentageCtrl::~CPercentageCtrl() 
{ 
} 
 
BEGIN_MESSAGE_MAP(CPercentageCtrl, CWnd) 
	//{{AFX_MSG_MAP(CPercentageCtrl) 
	ON_WM_ERASEBKGND() 
	ON_WM_PAINT() 
	ON_WM_MOUSEMOVE() 
	ON_WM_LBUTTONDOWN() 
	ON_WM_LBUTTONUP() 
	//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
///////////////////////////////////////////////////////////////////////////// 
// CPercentageCtrl message handlers 
 
BOOL CPercentageCtrl::InsertBlock(UINT weight, CString format, int pos, COLORREF c) 
{ 
	if (weight < 0) 
		return FALSE; 
 
	if (pos == m_percentagedata.GetSize()) 
		pos = m_percentagedata.Add(PercentageData(weight,format,c)); 
	else 
	{ 
		if (pos > m_percentagedata.GetSize() || pos <0) 
			return FALSE; 
 
		try 
		{ 
			m_percentagedata.InsertAt(pos, PercentageData(weight,format,c)); 
		} 
		catch (CMemoryException *) 
		{ 
			return FALSE; 
		} 
	} 
 
	Normalize(); 
 
	return TRUE; 
} 
 
int CPercentageCtrl::AddBlock(UINT weight, CString format, COLORREF c) 
{ 
	if (InsertBlock(weight, format, m_percentagedata.GetSize(), c)) 
		return m_percentagedata.GetSize()-1; 
	else 
		return -1; 
} 
 
BOOL CPercentageCtrl::ReplaceBlock(UINT weight, CString format, int pos, COLORREF c) 
{  
	if (weight < 0) 
		return FALSE; 
 
	if (pos >= m_percentagedata.GetSize() || pos <0) 
		return FALSE; 
 
	try 
	{ 
		m_percentagedata.SetAt(pos, PercentageData(weight,format,c)); 
	} 
	catch (CMemoryException *) 
	{ 
		return FALSE; 
	} 
 
	Normalize(); 
 
	return TRUE; 
} 
 
void CPercentageCtrl::OnPaint()  
{ 
	CPaintDC PaintDC(this); // device context for painting 
	CMemDC dc(&PaintDC); 
	dc.SetBkMode(TRANSPARENT); 
	 
	int i; 
	double total=0, temp; 
	int tot = m_percentagedata.GetSize(); 
 
	CPoint textorigin; 
	CString text; 
	CSize textExt; 
 
	CRect ClientRect; 
	GetClientRect(&ClientRect); 
 
	CRgn blockrgn; 
	CRect blockrect; 
 
	CBrush brush, *oldbrush; 
 
	LOGFONT lf; 
	CFont font, *oldfont; 
 
	oldfont = dc.GetCurrentFont(); 
	oldfont->GetLogFont(&lf); 
	 
	if (m_options & PC_VERTICALTEXT) 
		lf.lfEscapement = lf.lfOrientation = 900; 
	else 
		lf.lfEscapement = lf.lfOrientation = 0; 
	 
	lf.lfQuality = ANTIALIASED_QUALITY; 
	lf.lfOutPrecision = OUT_TT_ONLY_PRECIS; 
	 
	font.CreateFontIndirect(&lf); 
	 
	dc.SelectObject(&font); 
 
	int x, oldx=0; 
	int size = (m_options & PC_VERTICAL) ? ClientRect.bottom : ClientRect.right; 
	 
	for (i = 0; i < tot; i++) 
	{ 
		if (i!=tot-1) 
		{ 
			total +=m_percentagedata[i].normalized; 
			temp = total  * size; 
			 
			// Looking for nearest int 
			x = (int) temp;		 
			if (temp - x > 0.5) 
				x++; 
		} 
		else 
			x = size; 
		 
		brush.CreateSolidBrush(m_percentagedata[i].color); 
 
		oldbrush = dc.SelectObject(&brush); 
		 
		if (m_options & PC_VERTICAL) 
			blockrect.SetRect(0, oldx, ClientRect.right, x); 
		else 
			blockrect.SetRect(oldx, 0, x, ClientRect.bottom); 
		 
		dc.Rectangle(blockrect); 
		dc.SelectObject(oldbrush); 
		brush.DeleteObject(); 
		 
		if (!m_percentagedata[i].text.IsEmpty()) 
		{ 
			dc.SetTextColor(m_percentagedata[i].textcolor); 
			 
			text.Format(m_percentagedata[i].text, m_percentagedata[i].original * m_multiplier); 
			 
			textExt = dc.GetTextExtent(text); 
			 
			if (m_options & PC_TEXTELLIPSIS) 
			{ 
				int len = text.GetLength(); 
 
				// -4 'cause we must consider the rect border. 
				while (len && (textExt.cx > x-oldx - 4)) 
				{ 
					text=text.Left(--len); 
					 
					//This is to avoid that, e.g., "15.3" becomes "15...." 
					if (len && text[len-1]=='.') 
						text+=".."; 
					else 
						text+="..."; 
 
					textExt = dc.GetTextExtent(text); 
				} 
			} 
 
			if (m_options & PC_VERTICALTEXT) 
			{ 
				textorigin.x = (blockrect.Width() - textExt.cy)/2 + blockrect.left; 
				textorigin.y = -(blockrect.Height()- textExt.cx)/2+ blockrect.bottom; 
			} 
			else 
			{ 
				textorigin.x = (blockrect.Width() - textExt.cx)/2 + blockrect.left; 
				textorigin.y = (blockrect.Height() - textExt.cy)/2+ blockrect.top; 
			} 
			  
			// +1 'cause we must consider the rect border. 
			if (((m_options & PC_VERTICALTEXT) && (textorigin.x > blockrect.left + 1 && textorigin.y < blockrect.bottom)) || (!(m_options & PC_VERTICALTEXT) && (textorigin.x > blockrect.left + 1 && textorigin.y > blockrect.top + 1))) 
				dc.TextOut(textorigin.x, textorigin.y, text); 
			else if (m_options & PC_ALWAYSSHOWTEXT) 
			{ 
				blockrgn.CreateRectRgn(blockrect.left,blockrect.top,blockrect.right,blockrect.bottom); 
				dc.SelectClipRgn(&blockrgn); 
				dc.TextOut(textorigin.x, textorigin.y, text); 
				dc.SelectClipRgn(NULL); 
				blockrgn.DeleteObject(); 
			} 
		} 
		 
		oldx=x; 
	} 
 
	dc.SelectObject(oldfont); 
 
	font.DeleteObject(); 
} 
 
void CPercentageCtrl::Normalize() 
{ 
	double total = 0; 
	int i, tot = m_percentagedata.GetSize(); 
	 
	for (i = 0; i < tot; i++) 
		total+=m_percentagedata[i].original; 
	 
	for (i = 0; i < tot; i++) 
		m_percentagedata[i].normalized = m_percentagedata[i].original/total; 
} 
 
void CPercentageCtrl::OnMouseMove(UINT nFlags, CPoint point)  
{ 
	if (!(m_options & PC_READONLY)) 
	{ 
		CRect ClientRect; 
		GetClientRect(&ClientRect); 
		 
		if (m_Tracking) 
		{ 
			double delta, actpos, offset=0; 
			 
			if (!(m_options & PC_VERTICAL)) 
			{ 
				 
				if (m_TrackIndex ==0 && point.x < 0) 
					point.x = 0; 
				 
				if (m_TrackIndex == m_percentagedata.GetSize()-2 && point.x > ClientRect.right) 
					point.x = ClientRect.right; 
				 
				actpos=(double)point.x / (double) ClientRect.right; 
				 
			} 
			else 
			{ 
				if (m_TrackIndex ==0 && point.y < 0) 
					point.y = 0; 
				 
				if (m_TrackIndex == m_percentagedata.GetSize()-2 && point.y > ClientRect.bottom) 
					point.y = ClientRect.bottom; 
				 
				actpos=(double)point.y / (double) ClientRect.bottom; 
			} 
			 
			int i; 
			for (i =0 ; i< m_TrackIndex; i++) 
				offset+=m_percentagedata[i].normalized; 
			 
			actpos-=offset; 
			 
			delta=actpos-m_TrackOrigNormalizedData1; 
			 
			if (delta>m_TrackOrigNormalizedData2) 
			{ 
				m_percentagedata[m_TrackIndex].normalized = m_TrackOrigNormalizedData1+m_TrackOrigNormalizedData2; 
				m_percentagedata[m_TrackIndex].original = m_TrackOrigData1 + m_TrackOrigData2; 
				m_percentagedata[m_TrackIndex+1].normalized = 0; 
				m_percentagedata[m_TrackIndex+1].original = 0; 
			} 
			else if (delta < -m_TrackOrigNormalizedData1) 
			{ 
				m_percentagedata[m_TrackIndex].normalized = 0; 
				m_percentagedata[m_TrackIndex].original = 0; 
				m_percentagedata[m_TrackIndex+1].normalized = m_TrackOrigNormalizedData1+m_TrackOrigNormalizedData2; 
				m_percentagedata[m_TrackIndex+1].original = m_TrackOrigData1 + m_TrackOrigData2; 
			} 
			else 
			{ 
				m_percentagedata[m_TrackIndex].normalized = m_TrackOrigNormalizedData1+delta; 
				if(m_TrackOrigNormalizedData1) 
					m_percentagedata[m_TrackIndex].original = m_TrackOrigData1 + (UINT)(m_TrackOrigData1 * delta / m_TrackOrigNormalizedData1); 
				else 
				{ 
					int tot=m_percentagedata.GetSize(); 
					double total=0; 
					for (i=0; i< tot; i++) 
						total+=m_percentagedata[i].original; 
					 
					m_percentagedata[m_TrackIndex].original = (UINT)(delta*total); 
				} 
				 
				/* This is to avoid approximation errors */ 
				if (m_percentagedata[m_TrackIndex].original<0) 
				{ 
					m_percentagedata[m_TrackIndex].original = 0; 
					m_percentagedata[m_TrackIndex].normalized = 0; 
					 
					delta = -m_TrackOrigNormalizedData1; 
				} 
				 
				m_percentagedata[m_TrackIndex+1].normalized = m_TrackOrigNormalizedData2-delta; 
				m_percentagedata[m_TrackIndex+1].original = (m_TrackOrigData2 + m_TrackOrigData1) - m_percentagedata[m_TrackIndex].original; 
			} 
			 
			if (!(m_options & PC_VERTICAL)) 
				::SetCursor(LoadCursor(NULL, IDC_SIZEWE)); 
			else 
				::SetCursor(LoadCursor(NULL, IDC_SIZENS)); 
			 
			GetParent()->SendMessage(PERCENTAGE_CHANGED,(LPARAM)m_hWnd,m_TrackIndex); 
			 
			InvalidateRect(NULL); 
		} 
		else 
		{ 
			if (!(m_options & PC_VERTICAL)) 
			{ 
				/* Check if cursor is over a division */ 
				int i, x, oldx=0, tot = m_percentagedata.GetSize(); 
				 
				for (i = 0; i < tot-1; i++) 
				{ 
					x = (int)(oldx + m_percentagedata[i].normalized * ClientRect.right); 
					 
					if (point.x >= x-3 && point.x <= x+3) 
					{ 
						::SetCursor(LoadCursor(NULL, IDC_SIZEWE)); 
						return; 
					} 
					 
					oldx=x; 
				} 
			} 
			else 
			{ 
				/* Check if cursor is over a division */ 
				int i, y, oldy=0, tot = m_percentagedata.GetSize(); 
				 
				for (i = 0; i < tot-1; i++) 
				{ 
					y = (int)(oldy + m_percentagedata[i].normalized * ClientRect.bottom); 
					 
					if (point.y >= y-3 && point.y <= y+3) 
					{ 
						::SetCursor(LoadCursor(NULL, IDC_SIZENS)); 
						return; 
					} 
					 
					oldy=y; 
				} 
			} 
		} 
	} 
	CWnd::OnMouseMove(nFlags, point); 
} 
 
void CPercentageCtrl::OnLButtonDown(UINT nFlags, CPoint point)  
{ 
	if (!(m_options & PC_READONLY)) 
	{ 
		 
		if (!m_Tracking) 
		{ 
			if (!(m_options & PC_VERTICAL)) 
			{ 
				int i, tot=m_percentagedata.GetSize(), x, oldx=0; 
				 
				CRect ClientRect; 
				GetClientRect(&ClientRect); 
				 
				for (i = 0; i < tot-1; i++) 
				{ 
					x = (int)(oldx + m_percentagedata[i].normalized * ClientRect.right); 
					 
					if (point.x >= x-3 && point.x <= x+3) 
					{ 
						m_Tracking=TRUE; 
						m_TrackIndex=i; 
						 
						m_TrackOrigData1=m_percentagedata[i].original; 
						m_TrackOrigNormalizedData1=m_percentagedata[i].normalized; 
						 
						m_TrackOrigData2=m_percentagedata[i+1].original; 
						m_TrackOrigNormalizedData2=m_percentagedata[i+1].normalized; 
						 
						SetCapture(); 
						 
						::SetCursor(LoadCursor(NULL, IDC_SIZEWE)); 
 
						if (m_percentagedata[i+1].original!=0 || point.x <= x) 
							break; 
					} 
					oldx=x; 
				} 
			} 
			else 
			{ 
				int i, tot=m_percentagedata.GetSize(), y, oldy=0; 
				 
				CRect ClientRect; 
				GetClientRect(&ClientRect); 
				 
				for (i = 0; i < tot-1; i++) 
				{ 
					y = (int)(oldy + m_percentagedata[i].normalized * ClientRect.bottom); 
					 
					if (point.y >= y-3 && point.y <= y+3) 
					{ 
						m_Tracking=TRUE; 
						m_TrackIndex=i; 
						 
						m_TrackOrigData1=m_percentagedata[i].original; 
						m_TrackOrigNormalizedData1=m_percentagedata[i].normalized; 
						 
						m_TrackOrigData2=m_percentagedata[i+1].original; 
						m_TrackOrigNormalizedData2=m_percentagedata[i+1].normalized; 
						 
						SetCapture(); 
						 
						::SetCursor(LoadCursor(NULL, IDC_SIZENS)); 
						if (m_percentagedata[i+1].original!=0 || point.y <= y) 
							break; 
					} 
					oldy=y; 
				} 
			} 
		} 
	} 
	CWnd::OnLButtonDown(nFlags, point); 
} 
 
void CPercentageCtrl::OnLButtonUp(UINT nFlags, CPoint point)  
{ 
	if (m_Tracking && !(m_options & PC_READONLY)) 
	{ 
		ReleaseCapture(); 
		m_Tracking=FALSE; 
		InvalidateRect(NULL); 
	} 
 
	CWnd::OnLButtonUp(nFlags, point); 
} 
 
void CPercentageCtrl::SetOptions(int opt) 
{ 
	m_options = opt; 
} 
 
BOOL CPercentageCtrl::OnEraseBkgnd( CDC* /* pDC */) 
{ 
	/*Background will be erased by OnPaint*/ 
	return TRUE; 
} 
 
UINT CPercentageCtrl::GetWeight(int index) 
{ 
	ASSERT(index < m_percentagedata.GetSize()); 
	ASSERT(index >= 0); 
 
	if (index >= m_percentagedata.GetSize() || index < 0) 
		return 0; 
 
	return m_percentagedata[index].original; 
} 
 
double CPercentageCtrl::GetPercentage(int index) 
{ 
	ASSERT(index < m_percentagedata.GetSize()); 
	ASSERT(index >= 0); 
 
	if (index >= m_percentagedata.GetSize() || index < 0) 
		return -1; 
 
	return m_percentagedata[index].normalized; 
} 
 
void CPercentageCtrl::SetMultiplier(double multiplier) 
{ 
	m_multiplier = multiplier; 
} 
 
BOOL CPercentageCtrl::RemoveBlock(int index) 
{ 
	if (index >= m_percentagedata.GetSize() || index < 0) 
		return FALSE; 
 
	try 
	{ 
		m_percentagedata.RemoveAt(index); 
	} 
	catch (CMemoryException *) 
	{ 
		return FALSE; 
	} 
 
	Normalize(); 
	InvalidateRect(NULL); 
	return TRUE; 
} 
 
double CPercentageCtrl::GetMultiplier() 
{ 
	return m_multiplier; 
} 
 
int CPercentageCtrl::GetBlockCount() 
{ 
	return m_percentagedata.GetSize(); 
}