// CProgress class implementation file
// Copyright 1996 CustomSoft (http://www.concentric.net/~cgalbrai)
// Cecil A. Galbraith
// 3217 Linden Parkway
// Harrisburg, Pennsylvania 17110 USA
// cgalbrai@concentric.net 

#include "stdafx.h"
#include "progress.h" 

BEGIN_MESSAGE_MAP(CProgress,CWnd)
//{{AFX_MSG_MAP(CProgress)
	ON_WM_ERASEBKGND()
	ON_WM_SIZE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

CProgress::CProgress():CWnd()  // constructor  
{
	csClassName = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW | CS_OWNDC); 
	
	Meterrectangle.SetRectEmpty(); 
	point.x = 0; // default point is upper left of client area
	point.y = 0; // default point is upper left of client area
	bLeftBrushCreated = FALSE; // brush not created - used to prevent more than one creation
	bRightBrushCreated = FALSE; // brush not created - used to prevent more than one creation 
	bWindowCreated = FALSE; // window not yet created - used to prevent more than one creation
	oldpercentage = 0; 
	bInternalTextFlag = FALSE; // default is no text displayed 
	bBorderFlag = FALSE; // default is no border
	blockcounter = 0;
	bUseText = FALSE;
	percentage = 0;
	iIsBarType = 1;
}

CProgress::~CProgress() // destructor
{
	
// if brushes were created, delete them
	
	if(bLeftBrushCreated == TRUE) {
		lBrush.DeleteObject();  
	}
	
	if(bRightBrushCreated == TRUE) {
		rBrush.DeleteObject(); 
	}
}

BOOL CProgress::OnEraseBkgnd(CDC* pDC)
{
	COLORREF textcolor;
	COLORREF backcolor; 
	
// the code following comes from Meter() class member, below. It will redraw the progress
// bar if the bar is covered then uncovered by another window
	
	if(bLeftBrushCreated == TRUE
		&& bRightBrushCreated == TRUE
		&& bWindowCreated == TRUE
		&& iIsBarType == 1) {
		
// if text flag is set, set up things for printing
		
		if(bUseText == TRUE && percentage) {

			sprintf(szBuffer,"%d%s",percentage,format);			
			
			oldalign = pDC->SetTextAlign(TA_CENTER | TA_TOP);
			textcolor = pDC->SetTextColor(RightColor);
			backcolor = pDC->SetBkColor(LeftColor);
		}
		
// fill the left side of the meter with forecolor
		
		pDC->FillRect(&leftrectangle,&lBrush);
		
// print percentage text, if required
		
		if(bUseText == TRUE && percentage) {
			pDC->ExtTextOut(hposition,vposition,ETO_CLIPPED,&leftrectangle,szBuffer,strlen(szBuffer),NULL);  
			pDC->SetTextColor(LeftColor);
			pDC->SetBkColor(RightColor);
		}
		
// fill right side of the meter with backcolor
		
		pDC->FillRect(&rightrectangle,&rBrush);
		
// reset text to default
		
		if(bUseText == TRUE && percentage) {
			pDC->ExtTextOut(hposition,vposition,ETO_CLIPPED,&rightrectangle,szBuffer,strlen(szBuffer),NULL); 
			pDC->SetTextColor(textcolor);
			pDC->SetBkColor(backcolor);
			pDC->SetTextAlign(oldalign);
		}  
	}
	
// redraw background and current state of meter when using block type meter
	
	if(bLeftBrushCreated == TRUE
		&& bRightBrushCreated == TRUE
		&& bWindowCreated == TRUE
		&& iIsBarType == 0) {
		
		int counter;		
		
// sets the size of the sum of the left and right rectangle to the size of the window client area
// which may have changed due to window resizing by user...This also updates private data Meterrectangle
		
		GetClientRect(&Meterrectangle);		
		
// set the size of the right rectangle to Meterrectangle and color with
// backcolor
		
		rightrectangle = Meterrectangle;
		pDC->FillRect(&rightrectangle,&rBrush);
		
// width of each block including offset portion
		
		iBlockRectWidth = Meterrectangle.Width() / iBlocks;  
		
// offset is always a percentage of iBlockRectWidth
		
		iOffset = iBlockRectWidth / 5;
		if(iOffset == 0) iOffset = 1;   
		
// actual color block is...
		
		iBlockWidth = iBlockRectWidth - iOffset;  
		
// leftrectangle has same width each time, but different offset
		
		leftrectangle.top = 2;
		leftrectangle.bottom = Meterrectangle.Height() - 2;
		iInitialOffset = (Meterrectangle.Width() - (iBlocks * (iOffset + iBlockWidth))) / 2;
		
// restore blocked meter appearance		
		
		counter = 0;
		
		while(counter < blockcounter) {

// adjust blocks locations
			
			leftrectangle.left = (counter * iBlockRectWidth) + iOffset + iInitialOffset;
			leftrectangle.right = leftrectangle.left + iBlockWidth;
			
// paint the blocks - one at a time
			
			pDC->FillRect(&leftrectangle,&lBrush);
			
			counter++;			
		}		
	}	
	return 0; 
}

int CProgress::SetForeColor(COLORREF lcolor)
{
	LeftColor = lcolor;
	
// if brush is not yet created, create it. If creation fails, return 
	
	if(bLeftBrushCreated == FALSE) {
		if(!lBrush.CreateSolidBrush(LeftColor)) return 0;
		
// if creation is successful, set flag
		
		bLeftBrushCreated = TRUE;
	}
	return 1;
}

int CProgress::SetBackColor(COLORREF rcolor)
{
	RightColor = rcolor; 
	if(bRightBrushCreated == FALSE) {  
		if(!rBrush.CreateSolidBrush(RightColor)) return 0;
		bRightBrushCreated = TRUE;
	}
	return 1;
}

void CProgress::SetBorderFlag(BOOL flag)
{
	bBorderFlag = flag; 
}

void CProgress::SetTextFlag(BOOL flag)
{
	
// bInternalTextFlag is set permanently at creation 
	
	bInternalTextFlag = flag;
	
// bUseText may change according to the size of the progress meter rectangle
// if text can't be displayed without being clipped, bUseText is set to FALSE
// if user later resizes the window and the progress meter becomes large enough
// to show text, bUseText is changed back to TRUE
	
	bUseText = flag;
}

void CProgress::SetMeterSize(int width,int height)
{
	
// sets size of progress meter including border if any
	
	Meterrectangle.SetRect(0,0,width,height);
	rightrectangle = Meterrectangle;
	leftrectangle.SetRect(0,0,0,0);
}

void CProgress::SetMeterLocation(int x,int y)
{
	
// sets upper left corner of progress meter
	
	point.x = x;
	point.y = y; 
}

void CProgress::Meter(long varnum,long minnum, long maxnum)
{
	
	COLORREF textcolor;
	COLORREF backcolor;
	
// this member shows the meter progress and text. The meter consists of a rectangle
// composed of a left rectangle of one color and a right rectangle of another color.
// The left rectangle gradually increases in width, while the right rectangle 
// gradually decreases until it's width is zero. A 3D border or text is optional
	
// if either brush failed to create or the window for the meter was not created or
// the counter varnum is greater than the maximum rectangle width, maxnum,
// bail out and return, having done nothing.
	
	if(bLeftBrushCreated == FALSE
		|| bRightBrushCreated == FALSE
		|| bWindowCreated == FALSE
		|| varnum > maxnum
		|| !maxnum
		|| oldpercentage >= maxnum) {
		
		return;
	}
	
	iIsBarType = 1;
	
	percentage = 0;
	oldalign = 0; 
	iVarnum = varnum;
	
	long left = 0;
	
// sets the size of the sum of the left and right rectangle to the size of the window client area
// which may have changed due to window resizing by user...This also updates private data Meterrectangle
	
	GetClientRect(&Meterrectangle);
	
// set the size of the increasing rectangle to Meterrectangle, but set width to zero
	
	leftrectangle = Meterrectangle;   
	leftrectangle.right = (int)left;
	
// set width of leftrectangle to some percentage of Meterrectangle
	
	left = ((long)Meterrectangle.Width() *  (long)(varnum - minnum)) /  (long)(maxnum - minnum);
	
// the decreasing rectangle starts where the increasing rectangle stops...
	
	leftrectangle.right = (int)left; 
	
	rightrectangle = Meterrectangle;
	rightrectangle.left = leftrectangle.right;
	
// calculate percentage complete for text, if used.
	
	percentage = (int)(varnum);
	
// the below two lines prevent unnecessary printing and reduces flickering
// print number only if integer percentage has changed from last time
	
	if(percentage > oldpercentage) oldpercentage = percentage;
	else return;
	
// prepare to paint...
	
	CDC * pCDC = GetDC(); 
	
	if(bUseText == TRUE) {
		
		sprintf(szBuffer,"%d%s",percentage,format);
		
		oldalign = pCDC->SetTextAlign(TA_CENTER | TA_TOP);
		textcolor = pCDC->SetTextColor(RightColor);
		backcolor = pCDC->SetBkColor(LeftColor);
	} 
	
// paint the left rectangle part of the meter and print the text
	
	pCDC->FillRect(&leftrectangle,&lBrush); 
	
	if(bUseText == TRUE) {
		pCDC->ExtTextOut(hposition,vposition,ETO_CLIPPED,&leftrectangle,szBuffer,strlen(szBuffer),NULL);  
		pCDC->SetTextColor(LeftColor);
		pCDC->SetBkColor(RightColor);
	}
	
// paint the right rectangle part of the meter and print the text
	
	pCDC->FillRect(&rightrectangle,&rBrush);
	
	if(bUseText == TRUE) {
		pCDC->ExtTextOut(hposition,vposition,ETO_CLIPPED,&rightrectangle,szBuffer,strlen(szBuffer),NULL); 
		pCDC->SetTextColor(textcolor);
		pCDC->SetBkColor(backcolor);
		pCDC->SetTextAlign(oldalign);
	}
	
	ReleaseDC(pCDC); 
}

void CProgress::ClearMeter()
{
	if(bLeftBrushCreated == FALSE
		|| bRightBrushCreated == FALSE
		|| bWindowCreated == FALSE) return;
	
	CDC * pCDC = GetDC();
	GetClientRect(&Meterrectangle);
	pCDC->FillRect(&Meterrectangle,&rBrush);
	blockcounter = 0;
	oldvarnum = 0;
	oldpercentage = 0;
	percentage = 0;
	ReleaseDC(pCDC);  
	
}

void CProgress::Meter(long varnum,long maxnum,int blocks)
{
	
// if either brush failed to create or the window for the meter was not created or
// the counter varnum is greater than the maximum rectangle width, maxnum,
// bail out and return, having done nothing.
	
	if(bLeftBrushCreated == FALSE
		|| bRightBrushCreated == FALSE
		|| bWindowCreated == FALSE
		|| varnum > maxnum
		|| !maxnum
		|| blockcounter >= blocks) {  
		return;
	}
	
	iIsBarType = 0;
	iBlocks = blocks;
	
	if(blockcounter == 0) {
		
// sets the size of the sum of the left and right rectangle to the size of the window client area
// which may have changed due to window resizing by user...This also updates private data Meterrectangle
		
		GetClientRect(&Meterrectangle);
		
// set the size of the right rectangle to Meterrectangle and color with
// backcolor if blockcounter is zero
		
		rightrectangle = Meterrectangle;  
		
// width of each block including offset portion
		
		iBlockRectWidth = Meterrectangle.Width() / blocks;  
		
// offset is always a percentage of iBlockRectWidth
		
		iOffset = iBlockRectWidth / 5;
		if(iOffset == 0) {
			iOffset = 1;   
		}
		
// actual color block is...
		
		iBlockWidth = iBlockRectWidth - iOffset;  
		
// leftrectangle has same width each time, but different offset
		
		leftrectangle.top = 2;
		leftrectangle.bottom = Meterrectangle.Height() - 2;
		iInitialOffset = (Meterrectangle.Width() - (blocks * (iOffset + iBlockWidth))) / 2;
		
	}
	
	leftrectangle.left = (blockcounter * iBlockRectWidth) + iOffset + iInitialOffset;
	leftrectangle.right = leftrectangle.left + iBlockWidth;
	
// the below two lines prevent unnecessary printing and reduces flickering
// print number only if integer percentage has changed from last time
	
//int fix = 100/blocks;
	
	long fix = maxnum / blocks;
	
	if(varnum >= oldvarnum + fix) {
		
		oldvarnum = varnum;
	} 
	
	else return;
	
// prepare to paint...
	
	CDC * pCDC = GetDC();
	
	if(!blockcounter) pCDC->FillRect(&rightrectangle,&rBrush);
	
// paint the left rectangle part of the meter and print the text
	
	pCDC->FillRect(&leftrectangle,&lBrush); 
	
// paint the right rectangle part of the meter and print the text
	
	blockcounter++;
	
	ReleaseDC(pCDC); 
}

int CProgress::Create(CWnd * ptr,int x,int y,int cx,int cy,BOOL border,BOOL text,COLORREF fore,COLORREF back)
{ 
	
// if window has already been created, return having done nothing
	
	if(bWindowCreated == TRUE) return 0;
	
// set up meter parameters before creation
	
	SetMeterLocation(x,y); 
	SetMeterSize(cx,cy);
	SetBorderFlag(border);
	SetTextFlag(text);
	
// if creation of brushes fails, decline to create meter window
	
	if(!SetForeColor(fore)) return 0;
	if(!SetBackColor(back)) return 0; 

// create the Windows window with a 3D border
	
	if(bBorderFlag == TRUE) {
		if(!CreateEx(WS_EX_CLIENTEDGE,
			csClassName,
			NULL,
			WS_CHILD | WS_BORDER,
			point.x,
			point.y,
			Meterrectangle.Width(),
			Meterrectangle.Height(),
			ptr->GetSafeHwnd(),0)) return 0;
	}
	else {
		
// create the Windows window with no border - useful for placing progress meter
// in a pane in the status bar...
		
		if(!CreateEx(NULL,
			csClassName,
			NULL,
			WS_CHILD,
			point.x,
			point.y,
			Meterrectangle.Width(),
			Meterrectangle.Height(),
			ptr->GetSafeHwnd(),0)) return 0;
	}
	
// get the device context and metrics of the CProgress object
	
	CClientDC dc(this);
	dc.GetTextMetrics(&tm);
	
// calculate the placement of the counter text - may or may not be used
	
	if(bUseText == TRUE) {
		hposition = point.x + (Meterrectangle.Width() / 2);
		vposition = point.y + (Meterrectangle.Height() - tm.tmHeight) / 2;
	}
	
// the window is shown, but not activated so that the focus is not taken from the 
// parent window
	
	ShowWindow(SW_SHOWNA);
	
// Meterrectangle gets client rectangle - several pixels different from
// the original Meterrectangle because of the window border, if any
	
	GetClientRect(&Meterrectangle);
	
	if(bUseText == TRUE) {
		
// adjust the placement of where the text will print in the meter (centered)
		
		CDC * pDC = GetDC();
		pDC->GetTextMetrics(&tm);
		hposition = Meterrectangle.Width() / 2;
		vposition = (Meterrectangle.Height() - tm.tmHeight) / 2; 
		ReleaseDC(pDC);
	}
	
// make sure that the window isn't created twice in the same instance
	
	bWindowCreated = TRUE;	
	
// indicate creation success
	
	return 1;
}

void CProgress::OnSize(UINT nType,int cx,int cy) 
{
	CWnd::OnSize(nType,cx,cy);
	
// if window has not been created, return having done nothing
	
	if(bWindowCreated == TRUE && iIsBarType == 1) {
		
// get the device context and metrics of the CProgress object
		
		CClientDC dc(this);
		dc.GetTextMetrics(&tm);
		
// sets the size of the sum of the left and right rectangle to the size of the window client area
// which may have changed due to window resizing by user...This also updates private date Meterrectangle
		
		GetClientRect(&Meterrectangle);  
		
		point.x = Meterrectangle.left;
		point.y = Meterrectangle.top;
		
		long left = 0; 
		
// set the size of the increasing rectangle to Meterrectangle, but set width to zero
		
		leftrectangle = Meterrectangle;   
		leftrectangle.right = (int)left;
		
// set width of leftrectangle to some percentage of Meterrectangle
		
		left = ((long)Meterrectangle.Width() *  (long)percentage) /  (long)100;
		
// the decreasing rectangle starts where the increasing rectangle stops...
		
		leftrectangle.right = (int)left; 
		
		rightrectangle = Meterrectangle;
		rightrectangle.left = leftrectangle.right;
		
// set bUseText to TRUE or FALSE depending upon meter rectangle height
// this over-rides bInternalTextFlag choice if meter rectangle is too
// small to use text without clipping.
		
		if(bInternalTextFlag == TRUE) {
			if(tm.tmHeight > Meterrectangle.Height()) bUseText = FALSE;
			else bUseText = TRUE;
		}
		
		if(bUseText == TRUE) {
			
// calculate the placement of the counter text - may or may not be used 
			
			hposition = point.x + (Meterrectangle.Width() / 2);
			vposition = point.y + (Meterrectangle.Height() - tm.tmHeight) / 2;
		}
	}
	
}
