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();
}