boinc/client/win/wingui_piectrl.cpp

371 lines
11 KiB
C++
Executable File

// The contents of this file are subject to the BOINC Public License
// Version 1.0 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://boinc.berkeley.edu/license_1.0.txt
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations
// under the License.
//
// The Original Code is the Berkeley Open Infrastructure for Network Computing.
//
// The Initial Developer of the Original Code is the SETI@home project.
// Portions created by the SETI@home project are Copyright (C) 2002
// University of California at Berkeley. All Rights Reserved.
//
// Contributor(s):
//
#include "boinc_win.h"
#include "wingui_piectrl.h"
/////////////////////////////////////////////////////////////////////////
// CPieChartCtrl member functions
BEGIN_MESSAGE_MAP(CPieChartCtrl, CWnd)
ON_WM_PAINT()
END_MESSAGE_MAP()
//////////
// CPieChartCtrl::CPieChartCtrl
// arguments: void
// returns: void
// function: initializes members
CPieChartCtrl::CPieChartCtrl()
{
m_xTotal = 0;
m_pFont = NULL;
}
//////////
// CPieChartCtrl::AddPiece
// arguments: szLabel: label for the piece
// clr: color of the piece
// xValue: the initial value for the piece
// returns: void
// function: adds a piece to the pie
void CPieChartCtrl::AddPiece(LPTSTR szLabel, COLORREF clr, double xValue)
{
if(xValue < 0) xValue = 0;
m_xValues.Add(xValue);
m_colors.Add(clr);
CString strLabel;
strLabel.Format("%s", szLabel);
m_strLabels.Add(strLabel);
m_dwData.Add(0);
}
void CPieChartCtrl::RemovePiece(int nItem)
{
if(nItem >= 0 && nItem < GetItemCount()) {
m_xValues.RemoveAt(nItem);
m_colors.RemoveAt(nItem);
m_strLabels.RemoveAt(nItem);
m_dwData.RemoveAt(nItem);
}
}
//////////
// CPieChartCtrl::SetPiece
// arguments: nIndex: index of piece to change
// xValue: the new value for the piece
// returns: void
// function: changes the piece's value
void CPieChartCtrl::SetPiece(int nIndex, double xValue)
{
if(nIndex < 0 || nIndex >= GetItemCount()) return;
if(xValue < 0) xValue = 0;
m_xValues.SetAt(nIndex, xValue);
}
//////////
// CPieChartCtrl::SetPieceLabel
// arguments: nIndex: index of label to set
// szLabel: the new label
// returns: void
// function: gives the pie piece at the given index the given label
void CPieChartCtrl::SetPieceLabel(int nIndex, LPTSTR szLabel)
{
if(nIndex < 0 || nIndex >= GetItemCount()) return;
CString strLabel;
strLabel.Format("%s", szLabel);
m_strLabels.SetAt(nIndex, strLabel);
}
//////////
// CPieChartCtrl::GetItemCount
// arguments: void
// returns: the number of pieces in this pie
// function: finds number of pieces in the pie
int CPieChartCtrl::GetItemCount()
{
return m_xValues.GetSize();
}
//////////
// CPieChartCtrl::Create
// arguments: dwStyle: the style of control to create
// rect: size and position
// pParentWnd: control's parent window
// nID: control's id
// returns: true if successful, otherwise false
// function: creates this control
BOOL CPieChartCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
CString strWndClass = AfxRegisterWndClass (0, NULL, (HBRUSH)GetStockObject(WHITE_BRUSH), NULL);
return CWnd::Create(strWndClass, NULL, dwStyle, rect, pParentWnd, nID);
}
//////////
// CPieChartCtrl::DrawPiece
// arguments: pDC: pointer to dc to draw in
// xStartAngle: starting angle of piece
// xEndAngle: ending angle of piece
// returns: void
// function: draws a pie piece in the dc
void CPieChartCtrl::DrawPiePiece(CDC* pDC, double xStartAngle, double xEndAngle)
{
if(xEndAngle - xStartAngle <= 1.00) return;
// gdi objects needed
CRect rt, rt2;
CRgn rellipsehi, rellipselow, rrect, rdepthcurve, rdepth;
CPoint pt1, pt2, pt3, pt4;
CRgn rpie, rangle, rellipse;
CPoint poly[8];
// set up coordinates
GetWindowRect(&rt);
ScreenToClient(&rt);
CPoint cp;
int major = (int)((rt.Width() - 2 * PIE_BUFFER) * 0.5);
int minor = (int)((rt.Height() - 2 * PIE_BUFFER) * 0.25);
int maxmajor = (int)(pDC->GetDeviceCaps(HORZRES) * PIE_MAJOR_MAX);
int maxminor = (int)(pDC->GetDeviceCaps(VERTRES) * PIE_MINOR_MAX);
if(major > maxmajor) major = maxmajor;
if(minor > maxminor) minor = maxminor;
int depth = (int)(minor * PIE_DEPTH);
cp.x = (int)(rt.Width() * 0.5);
cp.y = rt.Height() - minor - PIE_BUFFER - depth;
rt.SetRect(cp.x - major, cp.y - minor, cp.x + major, cp.y + minor);
rt2.SetRect(cp.x - major, cp.y - minor + depth, cp.x + major, cp.y + minor + depth);
// draw elliptical part of piece
// set up coordinates
poly[0].x = cp.x; poly[0].y = cp.y;
EllipsePoint(&rt, xStartAngle + (xEndAngle - xStartAngle) * 0.00f, &poly[1]);
CirclePoint(&cp, major * 5, xStartAngle + (xEndAngle - xStartAngle) * 0.00f, &poly[2]);
CirclePoint(&cp, major * 5, xStartAngle + (xEndAngle - xStartAngle) * 0.25f, &poly[3]);
CirclePoint(&cp, major * 5, xStartAngle + (xEndAngle - xStartAngle) * 0.50f, &poly[4]);
CirclePoint(&cp, major * 5, xStartAngle + (xEndAngle - xStartAngle) * 0.75f, &poly[5]);
CirclePoint(&cp, major * 5, xStartAngle + (xEndAngle - xStartAngle) * 1.00f, &poly[6]);
EllipsePoint(&rt, xStartAngle + (xEndAngle - xStartAngle) * 1.00f, &poly[7]);
// filled part
rellipse.CreateEllipticRgnIndirect(&rt);
rangle.CreatePolygonRgn(poly, 8, ALTERNATE);
rpie.CreateRectRgnIndirect(&rt);
rpie.CombineRgn(&rellipse, &rangle, RGN_AND);
pDC->FillRgn(&rpie, pDC->GetCurrentBrush());
// outline
pDC->MoveTo(rt.CenterPoint());
pDC->LineTo(poly[1]);
pDC->Arc(&rt, poly[1], poly[7]);
pDC->MoveTo(poly[7]);
pDC->LineTo(rt.CenterPoint());
// clean up
rellipse.DeleteObject();
rangle.DeleteObject();
rpie.DeleteObject();
// draw depth part of pie piece if needed
if(xStartAngle >= 180 || xEndAngle >= 180) {
// set up coordinates
double xLowerAngle = 180;
if(xStartAngle > 180) xLowerAngle = xStartAngle;
double xHigherAngle = 360;
if(xEndAngle < 360) xHigherAngle = xEndAngle;
EllipsePoint(&rt, xLowerAngle, &pt1);
EllipsePoint(&rt, xHigherAngle, &pt2);
EllipsePoint(&rt2, xLowerAngle, &pt3);
EllipsePoint(&rt2, xHigherAngle, &pt4);
if(xStartAngle > 180) {
pt1.x = poly[1].x;
pt3.x = poly[1].x;
}
if(xEndAngle < 360) {
pt2.x = poly[7].x;
pt4.x = poly[7].x;
}
// filled part
rellipsehi.CreateEllipticRgnIndirect(&rt);
rellipselow.CreateEllipticRgnIndirect(&rt2);
rrect.CreateRectRgn(pt1.x, rt.top, pt4.x, rt2.bottom);
rdepthcurve.CreateRectRgnIndirect(&rt2);
rdepthcurve.CombineRgn(&rellipselow, &rellipsehi, RGN_DIFF);
rdepth.CreateRectRgnIndirect(&rt2);
rdepth.CombineRgn(&rdepthcurve, &rrect, RGN_AND);
pDC->FillRgn(&rdepth, pDC->GetCurrentBrush());
// ouline
pDC->Arc(&rt, pt1, pt2);
pDC->Arc(&rt2, pt3, pt4);
pDC->MoveTo(pt1);
pDC->LineTo(pt3);
pDC->MoveTo(pt4);
pDC->LineTo(pt2);
// clean up
rellipsehi.DeleteObject();
rellipselow.DeleteObject();
rdepthcurve.DeleteObject();
rdepth.DeleteObject();
}
}
//////////
// CPieChartCtrl::CirclePoint
// arguments: center: center point of circle
// nRadius: radius of circle
// xAngle: angle of radius
// pResult: pointer to CPoint to put result in
// returns: void
// function: calculates the point on the circle at the given angle
void CPieChartCtrl::CirclePoint(CPoint* center, int nRadius, double xAngle, CPoint* pResult)
{
pResult->x = (long)(center->x + nRadius * cos(xAngle * (PI / 180)));
pResult->y = (long)(center->y - nRadius * sin(xAngle * (PI / 180)));
}
//////////
// CPieChartCtrl::EllipsePoint
// arguments: rt: pointer to rect of ellipse
// xAngle: angle of radius
// pResult: pointer to CPoint to put result in
// returns: void
// function: calculates the point on the ellipse in the given rect at
// the given angle
void CPieChartCtrl::EllipsePoint(CRect* rt, double xAngle, CPoint* pResult)
{
pResult->x = (int)(rt->CenterPoint().x + (rt->Width() / 2) * cos(xAngle * (PI / 180)));
pResult->y = (int)(rt->CenterPoint().y - (rt->Height() / 2) * sin(xAngle * (PI / 180)));
}
//////////
// CPieChartCtrl::SetFont
// arguments: pFont: pointer to font to set
// returns: void
// function: sets this control's font
void CPieChartCtrl::SetFont(CFont* pFont)
{
m_pFont = pFont;
}
//////////
// CPieChartCtrl::SetTotal
// arguments: xTotal: the total amount of the pie chart
// returns: void
// function: sets the tag for data, which is a string that will be displayed
// after the numbers in the label
void CPieChartCtrl::SetTotal(double xTotal)
{
if(xTotal >= 0) m_xTotal = xTotal;
}
//////////
// CPieChartCtrl::OnPaint
// arguments: void
// returns: void
// function: draws the control by drawing the labels and pie pieces for
// each piece of the pie
void CPieChartCtrl::OnPaint()
{
CWnd::OnPaint();
// no pieces, so dont do anything
if(m_xValues.GetSize() == 0) return;
// gdi objects needed
CClientDC cdc(this);
CRect rt;
CDC MemDC;
CBitmap MemBmp;
CBrush MemBrush;
CPen MemPen;
CBitmap* pOldBmp = NULL;
CBrush* pOldBrush = NULL;
CPen* pOldPen = NULL;
CFont* pOldFont = NULL;
// create offscreen buffer
GetClientRect(&rt);
MemDC.CreateCompatibleDC(&cdc);
MemBmp.CreateCompatibleBitmap(&cdc, rt.Width(), rt.Height());
MemPen.CreatePen(PS_SOLID, 0, RGB(0, 0, 0));
// select gdi objects
pOldBmp = MemDC.SelectObject(&MemBmp);
pOldPen = MemDC.SelectObject(&MemPen);
pOldFont = MemDC.SelectObject(m_pFont);
MemDC.FillSolidRect(&rt, RGB(255, 255, 255));
// go through each xPercent and draw its label and pie
double xSoFar = 0;
CRect wndrect;
CRect textrect;
GetWindowRect(&wndrect);
for(int i = 0; i < m_xValues.GetSize(); i ++) {
MemBrush.CreateSolidBrush(m_colors.GetAt(i));
pOldBrush = MemDC.SelectObject(&MemBrush);
// display color box and label
if(PIE_BUFFER + 20 + i * 20 < wndrect.Height() / 2) {
textrect.SetRect(PIE_BUFFER + 0, PIE_BUFFER + i * 20 + 4, PIE_BUFFER + 11, PIE_BUFFER + 20 + i * 20 - 4);
MemDC.FillRect(&textrect, &MemBrush);
MemDC.MoveTo(textrect.left, textrect.top);
MemDC.LineTo(textrect.right, textrect.top);
MemDC.LineTo(textrect.right, textrect.bottom);
MemDC.LineTo(textrect.left, textrect.bottom);
MemDC.LineTo(textrect.left, textrect.top);
textrect.SetRect(PIE_BUFFER + 16, PIE_BUFFER + i * 20, wndrect.Width() - PIE_BUFFER, PIE_BUFFER + 20 + i * 20);
char size_buf[256];
nbytes_to_string(m_xValues.GetAt(i), 0, size_buf, 256);
CString strBuf;
strBuf.Format("%s (%s)", m_strLabels.GetAt(i).GetBuffer(0), size_buf);
MemDC.DrawText(strBuf, textrect, DT_SINGLELINE|DT_VCENTER|DT_LEFT|DT_END_ELLIPSIS);
}
// display pie piece
double xPercent = 0;
if(m_xTotal > 0) xPercent = m_xValues.GetAt(i) / m_xTotal;
DrawPiePiece(&MemDC, xSoFar * 360, (xSoFar + xPercent) * 360);
xSoFar += xPercent;
MemDC.SelectObject(pOldBrush);
MemBrush.DeleteObject();
}
// copy offscreen buffer to screen
cdc.BitBlt(0, 0, rt.Width(), rt.Height(), &MemDC, 0, 0, SRCCOPY);
// clean up
MemDC.SelectObject(pOldBmp);
MemDC.SelectObject(pOldPen);
MemDC.SelectObject(pOldFont);
MemPen.DeleteObject();
MemBmp.DeleteObject();
MemBrush.DeleteObject();
MemDC.DeleteDC();
}