📄 progressctrlx.cpp
字号:
#include "stdafx.h"
#include "ProgressCtrlX.h"
#include "MemDC.h"
#include "DrawGdiX.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CProgressCtrlX
CProgressCtrlX::CProgressCtrlX()
: m_rcBorders(0,0,0,0)
{
// Init colors
m_clrBk = ::GetSysColor(COLOR_3DFACE);
m_clrTextOnBar = ::GetSysColor(COLOR_CAPTIONTEXT);
m_clrTextOnBk = ::GetSysColor(COLOR_BTNTEXT);
// set gradient colors
COLORREF clrStart, clrEnd;
clrStart = clrEnd = ::GetSysColor(COLOR_ACTIVECAPTION);
#if(WINVER >= 0x0500)
BOOL fGradientCaption = FALSE;
if(SystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, &fGradientCaption, 0) &&
fGradientCaption)
clrEnd = ::GetSysColor(COLOR_GRADIENTACTIVECAPTION);
#endif /* WINVER >= 0x0500 */
SetGradientColors(clrStart, clrEnd);
m_nStep = 10; // according msdn
m_nTail = 0;
m_nTailSize = 40;
m_pbrBk = m_pbrBar = NULL;
}
BEGIN_MESSAGE_MAP(CProgressCtrlX, CProgressCtrl)
//{{AFX_MSG_MAP(CProgressCtrlX)
ON_WM_ERASEBKGND()
ON_WM_PAINT()
ON_MESSAGE(PBM_SETBARCOLOR, OnSetBarColor)
ON_MESSAGE(PBM_SETBKCOLOR, OnSetBkColor)
ON_MESSAGE(PBM_SETPOS, OnSetPos)
ON_MESSAGE(PBM_DELTAPOS, OnDeltaPos)
ON_MESSAGE(PBM_STEPIT, OnStepIt)
ON_MESSAGE(PBM_SETSTEP, OnSetStep)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CProgressCtrlX message handlers
BOOL CProgressCtrlX::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
return TRUE; // erase in OnPaint()
}
void CProgressCtrlX::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
CDrawInfo info;
GetClientRect(&info.rcClient);
// retrieve current position and range
info.nCurPos = GetPos();
GetRange(info.nLower, info.nUpper);
// Draw to memory DC
CMemDC memDC(&dc);
info.pDC = &memDC;
// fill background
if(m_pbrBk)
memDC.FillRect(&info.rcClient, m_pbrBk);
else
memDC.FillSolidRect(&info.rcClient, m_clrBk);
// apply borders
info.rcClient.DeflateRect(m_rcBorders);
// if current pos is out of range return
if (info.nCurPos < info.nLower || info.nCurPos > info.nUpper)
return;
info.dwStyle = GetStyle();
BOOL fVert = info.dwStyle&PBS_VERTICAL;
BOOL fSnake = info.dwStyle&PBS_SNAKE;
BOOL fRubberBar = info.dwStyle&PBS_RUBBER_BAR;
// calculate visible gradient width
CRect rcBar(0,0,0,0);
CRect rcMax(0,0,0,0);
rcMax.right = fVert ? info.rcClient.Height() : info.rcClient.Width();
rcBar.right = (int)((float)(info.nCurPos-info.nLower) * rcMax.right / (info.nUpper-info.nLower));
if(fSnake)
rcBar.left = (int)((float)(m_nTail-info.nLower) * rcMax.right / (info.nUpper-info.nLower));
// draw bar
if(m_pbrBar)
memDC.FillRect(&ConvertToReal(info, rcBar), m_pbrBar);
else
DrawMultiGradient(info, fRubberBar ? rcBar : rcMax, rcBar);
// Draw text
DrawText(info, rcMax, rcBar);
// Do not call CProgressCtrl::OnPaint() for painting messages
}
void CProgressCtrlX::DrawMultiGradient(const CDrawInfo& info, const CRect &rcGrad, const CRect &rcClip)
{
int nSteps = m_ardwGradColors.GetSize()-1;
float nWidthPerStep = (float)rcGrad.Width() / nSteps;
CRect rcGradBand(rcGrad);
for (int i = 0; i < nSteps; i++)
{
rcGradBand.left = rcGrad.left + (int)(nWidthPerStep * i);
rcGradBand.right = rcGrad.left + (int)(nWidthPerStep * (i+1));
if(i == nSteps-1) //last step (because of problems with float)
rcGradBand.right = rcGrad.right;
if(rcGradBand.right < rcClip.left)
continue; // skip - band before cliping rect
CRect rcClipBand(rcGradBand);
if(rcClipBand.left < rcClip.left)
rcClipBand.left = rcClip.left;
if(rcClipBand.right > rcClip.right)
rcClipBand.right = rcClip.right;
DrawGradient(info, rcGradBand, rcClipBand, m_ardwGradColors[i], m_ardwGradColors[i+1]);
if(rcClipBand.right == rcClip.right)
break; // stop filling - next band is out of clipping rect
}
}
void CProgressCtrlX::DrawGradient(const CDrawInfo& info, const CRect &rcGrad, const CRect &rcClip, COLORREF clrStart, COLORREF clrEnd)
{
// Split colors to RGB chanels, find chanel with maximum difference
// between the start and end colors. This distance will determine
// number of steps of gradient
int r = (GetRValue(clrEnd) - GetRValue(clrStart));
int g = (GetGValue(clrEnd) - GetGValue(clrStart));
int b = (GetBValue(clrEnd) - GetBValue(clrStart));
int nSteps = max(abs(r), max(abs(g), abs(b)));
// if number of pixels in gradient less than number of steps -
// use it as numberof steps
int nPixels = rcGrad.Width();
nSteps = min(nPixels, nSteps);
if(nSteps == 0) nSteps = 1;
float rStep = (float)r/nSteps;
float gStep = (float)g/nSteps;
float bStep = (float)b/nSteps;
r = GetRValue(clrStart);
g = GetGValue(clrStart);
b = GetBValue(clrStart);
BOOL fLowColor = info.pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE;
if(!fLowColor && nSteps > 1)
if(info.pDC->GetDeviceCaps(BITSPIXEL)*info.pDC->GetDeviceCaps(PLANES) < 8)
nSteps = 1; // for 16 colors no gradient
float nWidthPerStep = (float)rcGrad.Width() / nSteps;
CRect rcFill(rcGrad);
CBrush br;
// Start filling
for (int i = 0; i < nSteps; i++)
{
rcFill.left = rcGrad.left + (int)(nWidthPerStep * i);
rcFill.right = rcGrad.left + (int)(nWidthPerStep * (i+1));
if(i == nSteps-1) //last step (because of problems with float)
rcFill.right = rcGrad.right;
if(rcFill.right < rcClip.left)
continue; // skip - band before cliping rect
// clip it
if(rcFill.left < rcClip.left)
rcFill.left = rcClip.left;
if(rcFill.right > rcClip.right)
rcFill.right = rcClip.right;
COLORREF clrFill = RGB(r + (int)(i * rStep),
g + (int)(i * gStep),
b + (int)(i * bStep));
if(fLowColor)
{
br.CreateSolidBrush(clrFill);
// CDC::FillSolidRect is faster, but it does not handle 8-bit color depth
info.pDC->FillRect(&ConvertToReal(info, rcFill), &br);
br.DeleteObject();
}
else
info.pDC->FillSolidRect(&ConvertToReal(info, rcFill), clrFill);
if(rcFill.right >= rcClip.right)
break; // stop filling if we reach current position
}
}
void CProgressCtrlX::DrawText(const CDrawInfo& info, const CRect &rcMax, const CRect &rcBar)
{
if(!(info.dwStyle&PBS_TEXTMASK))
return;
BOOL fVert = info.dwStyle&PBS_VERTICAL;
CDC *pDC = info.pDC;
int nValue = 0;
CString sFormat;
GetWindowText(sFormat);
switch(info.dwStyle&PBS_TEXTMASK)
{
case PBS_SHOW_PERCENT:
if(sFormat.IsEmpty())
sFormat = "%d%%";
// retrieve current position and range
nValue = (int)((float)(info.nCurPos-info.nLower) * 100 / (info.nUpper-info.nLower));
break;
case PBS_SHOW_POSITION:
if(sFormat.IsEmpty())
sFormat = "%d";
// retrieve current position
nValue = info.nCurPos;
break;
}
if (sFormat.IsEmpty())
return;
CFont* pFont = GetFont();
CSelFont sf(pDC, pFont);
CSelTextColor tc(pDC, m_clrTextOnBar);
CSelBkMode bm(pDC, TRANSPARENT);
CSelTextAlign ta(pDC, TA_BOTTOM|TA_CENTER);
CPoint ptOrg = pDC->GetWindowOrg();
CString sText;
sText.Format(sFormat, nValue);
LONG grad = 0;
if(pFont)
{
LOGFONT lf;
pFont->GetLogFont(&lf);
grad = lf.lfEscapement/10;
}
int x = 0, y = 0, dx = 0, dy = 0;
CSize sizText = pDC->GetTextExtent(sText);
if(grad == 0) { x = sizText.cx; y = sizText.cy; dx = 0; dy = sizText.cy;}
else if(grad == 90) { x = sizText.cy; y = sizText.cx; dx = sizText.cy; dy = 0;}
else if(grad == 180) { x = sizText.cx; y = sizText.cy; dx = 0; dy = -sizText.cy;}
else if(grad == 270) { x = sizText.cy; y = sizText.cx; dx = -sizText.cy; dy = 0;}
else ASSERT(0); // angle not supported
#if 0
// required "math.h"
double pi = 3.1415926535;
double rad = grad*pi/180;
dx = sz.cy*sin(rad);
dy = sz.cy*cos(rad);
#endif
CPoint pt = pDC->GetViewportOrg();
if(info.dwStyle&PBS_TIED_TEXT)
{
CRect rcFill(ConvertToReal(info, rcBar));
if((fVert ? y : x) <= rcBar.Width())
{
pDC->SetViewportOrg(rcFill.left + (rcFill.Width() + dx)/2,
rcFill.top + (rcFill.Height() + dy)/2);
DrawClippedText(info, rcBar, sText, ptOrg);
}
}
else
{
pDC->SetViewportOrg(info.rcClient.left + (info.rcClient.Width() + dx)/2,
info.rcClient.top + (info.rcClient.Height() + dy)/2);
if(m_clrTextOnBar == m_clrTextOnBk)
// if the same color for bar and background draw text once
DrawClippedText(info, rcMax, sText, ptOrg);
else
{
// else, draw clipped parts of text
// draw text on gradient
if(rcBar.left != rcBar.right)
DrawClippedText(info, rcBar, sText, ptOrg);
// draw text out of gradient
if(rcMax.right > rcBar.right)
{
tc.Select(m_clrTextOnBk);
CRect rc(rcMax);
rc.left = rcBar.right;
DrawClippedText(info, rc, sText, ptOrg);
}
if(rcMax.left < rcBar.left)
{
tc.Select(m_clrTextOnBk);
CRect rc(rcMax);
rc.right = rcBar.left;
DrawClippedText(info, rc, sText, ptOrg);
}
}
}
pDC->SetViewportOrg(pt);
}
void CProgressCtrlX::DrawClippedText(const CDrawInfo& info, const CRect& rcClip, CString& sText, const CPoint& ptWndOrg)
{
CDC *pDC = info.pDC;
CRgn rgn;
CRect rc = ConvertToReal(info, rcClip);
rc.OffsetRect(-ptWndOrg);
rgn.CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
pDC->SelectClipRgn(&rgn);
pDC->TextOut (0, 0, sText);
rgn.DeleteObject();
}
LRESULT CProgressCtrlX::OnSetBarColor(WPARAM clrEnd, LPARAM clrStart)
{
SetGradientColors(clrStart, clrEnd ? clrEnd : clrStart);
return CLR_DEFAULT;
}
LRESULT CProgressCtrlX::OnSetBkColor(WPARAM, LPARAM clrBk)
{
m_clrBk = clrBk;
return CLR_DEFAULT;
}
LRESULT CProgressCtrlX::OnSetStep(WPARAM nStepInc, LPARAM)
{
m_nStep = nStepInc;
return Default();
}
LRESULT CProgressCtrlX::OnSetPos(WPARAM newPos, LPARAM)
{
int nOldPos;
if(SetSnakePos(nOldPos, newPos))
return nOldPos;
return Default();
}
LRESULT CProgressCtrlX::OnDeltaPos(WPARAM nIncrement, LPARAM)
{
int nOldPos;
if(SetSnakePos(nOldPos, nIncrement, TRUE))
return nOldPos;
return Default();
}
LRESULT CProgressCtrlX::OnStepIt(WPARAM, LPARAM)
{
int nOldPos;
if(SetSnakePos(nOldPos, m_nStep, TRUE))
return nOldPos;
return Default();
}
/////////////////////////////////////////////////////////////////////////////
// CProgressCtrlX implementation
BOOL CProgressCtrlX::SetSnakePos(int& nOldPos, int nNewPos, BOOL fIncrement)
{
DWORD dwStyle = GetStyle();
if(!(dwStyle&PBS_SNAKE))
return FALSE;
int nLower, nUpper;
GetRange(nLower, nUpper);
if(fIncrement)
{
int nCurPos = GetPos();
if(nCurPos == nUpper && nCurPos - m_nTail < m_nTailSize )
nCurPos = m_nTail + m_nTailSize;
nNewPos = nCurPos + abs(nNewPos);
}
if(nNewPos > nUpper+m_nTailSize)
{
nNewPos -= nUpper-nLower + m_nTailSize;
if(nNewPos > nUpper + m_nTailSize)
{
ASSERT(0); // too far - reset
nNewPos = nUpper + m_nTailSize;
}
if(dwStyle&PBS_REVERSE)
ModifyStyle(PBS_REVERSE, 0);
else
ModifyStyle(0, PBS_REVERSE);
}
else if(nNewPos >= nUpper)
Invalidate();
m_nTail = nNewPos - m_nTailSize;
if(m_nTail < nLower)
m_nTail = nLower;
nOldPos = DefWindowProc(PBM_SETPOS, nNewPos, 0);
return TRUE;
}
void CProgressCtrlX::SetTextFormat(LPCTSTR szFormat, DWORD ffFormat)
{
ASSERT(::IsWindow(m_hWnd));
if(!szFormat || !szFormat[0] || !ffFormat)
{
ModifyStyle(PBS_TEXTMASK, 0);
SetWindowText("");
}
else
{
ModifyStyle(PBS_TEXTMASK, ffFormat);
SetWindowText(szFormat);
}
}
CRect CProgressCtrlX::ConvertToReal(const CDrawInfo& info, const CRect& rcVirt)
{
BOOL fReverse = info.dwStyle&PBS_REVERSE;
BOOL fVert = info.dwStyle&PBS_VERTICAL;
CRect rc(info.rcClient);
if(fVert)
{
rc.top = info.rcClient.top +
(fReverse ? rcVirt.left : (info.rcClient.Height() - rcVirt.right));
rc.bottom = rc.top + rcVirt.Width();
}
else
{
rc.left = info.rcClient.left +
(fReverse ? (info.rcClient.Width() - rcVirt.right) : rcVirt.left);
rc.right = rc.left + rcVirt.Width();
}
return rc;
}
void CProgressCtrlX::SetGradientColorsX(int nCount, COLORREF clrFirst, COLORREF clrNext, ...)
{
m_ardwGradColors.SetSize(nCount);
m_ardwGradColors.SetAt(0, clrFirst);
m_ardwGradColors.SetAt(1, clrNext);
va_list pArgs;
va_start(pArgs, clrNext);
for(int i = 2; i < nCount; i++)
m_ardwGradColors.SetAt(i, va_arg(pArgs, COLORREF));
va_end( pArgs );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -