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