// aplProgressCtrlEx.cpp : implementation file
//

#include "stdafx.h"
#include "AplGuiEx.h"

#define APL_GUI_EX_DEFAULT_MARKUEE_SIZE 0.25	// в процентах (от 0 до 1)
#define APL_GUI_EX_SPACE				2		// промежуток между блоками в пикселах
#define APL_PROGRESS_EX_TIMER_ID		WM_USER+101		// идентификатор события таймера, по которому производится прокрутка
#define APL_GUI_EX_BLOCK_WIDTH_FACTOR	0.6		// отношение ширины квадратика бара к высоте элемента
#define APL_GUI_EX_BAR_FULL_TIME		2000	// время в мс за которое полоса должна преодолеть бар длинной 4"

// CaplProgressCtrlEx

IMPLEMENT_DYNAMIC(CaplProgressCtrlEx, CProgressCtrl)

CaplProgressCtrlEx::CaplProgressCtrlEx() : m_bMarqueeMode(false), m_nInterval(300), m_dMarqueSize(APL_GUI_EX_DEFAULT_MARKUEE_SIZE)
{

}

CaplProgressCtrlEx::~CaplProgressCtrlEx()
{
}


BEGIN_MESSAGE_MAP(CaplProgressCtrlEx, CProgressCtrl)
	ON_WM_TIMER()
	ON_WM_PAINT()
END_MESSAGE_MAP()

bool CaplProgressCtrlEx::SetMarqueeMode(bool bMarqueeMode, int nInterval)
{
	m_bMarqueeMode = bMarqueeMode;
	m_nInterval = nInterval;

	if (nInterval<=0)
		return false;

	if (bMarqueeMode)
	{
		int nLower, nUpper;
		GetRange(nLower, nUpper);
		SetPos(nLower);
		SetTimer(APL_PROGRESS_EX_TIMER_ID, nInterval, NULL);
	}
	else
		KillTimer(APL_PROGRESS_EX_TIMER_ID);
	return true;
}

double CaplProgressCtrlEx::SetMarqueeSize(double dSize)
{
	if (dSize<0 || dSize>1)
		return m_dMarqueSize;

	double dOldSize = m_dMarqueSize;
	m_dMarqueSize = dSize;

	return dOldSize;
}



// CaplProgressCtrlEx message handlers



void CaplProgressCtrlEx::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: Add your message handler code here and/or call default
	if (nIDEvent==APL_PROGRESS_EX_TIMER_ID)
	{
		StepIt();
		Invalidate();
	}

	CProgressCtrl::OnTimer(nIDEvent);
}

void CaplProgressCtrlEx::OnPaint()
{
	if (!m_bMarqueeMode)
	{
		CProgressCtrl::OnPaint();
		return;
	}

	CPaintDC w_dc(this); // device context for painting
	CDC dc; // virtual device context for painting
	CBitmap bm;

	// TODO: Add your message handler code here
	// Do not call CProgressCtrl::OnPaint() for painting messages
	COLORREF ctrlBarColor = GetSysColor(COLOR_HIGHLIGHT);
	COLORREF ctrlBarBkColor	= GetSysColor(COLOR_BTNFACE);

	CRect rectCtrl, rectCtrl_;
	GetWindowRect(&rectCtrl_);
	ScreenToClient(&rectCtrl_);

	rectCtrl = rectCtrl_;

	dc.CreateCompatibleDC(&w_dc);
	bm.CreateCompatibleBitmap(&w_dc, rectCtrl.Width(), rectCtrl.Height());
	dc.SelectObject(&bm);

	dc.FillSolidRect(rectCtrl, ctrlBarBkColor);

	DrawEdge(dc, rectCtrl, EDGE_SUNKEN, BF_ADJUST | BF_RECT | BF_FLAT);

	rectCtrl.DeflateRect(1,1);

	int cW = rectCtrl.Width();
	int cH = rectCtrl.Height();

	int blW = (int)(cH*APL_GUI_EX_BLOCK_WIDTH_FACTOR);	// ширина квадратика

	int sizeBar = (int)(cW*m_dMarqueSize);
	int factorBar = 3;
	int nBlock = blW+APL_GUI_EX_SPACE;

	if (!(GetStyle()&PBS_SMOOTH))
	{
		// рисование квадратиками
		if (sizeBar<(3*blW+2*APL_GUI_EX_SPACE))
		{
			sizeBar = 3*blW+2*APL_GUI_EX_SPACE;
			factorBar = 3;
		}
		else
		{
			sizeBar /= nBlock;
			factorBar = sizeBar;
			sizeBar *= nBlock;
			sizeBar -= APL_GUI_EX_SPACE;
		}
	}

	int nLower, nUpper, nPos;
	GetRange(nLower, nUpper);
	nPos = GetPos();

	int nScale = nUpper==nLower?0:(int)(cW/(nUpper-nLower));
	int nLeft = rectCtrl.left + (nPos-nLower)*nScale;

	CRect curRect;

	int nRight = nLeft+sizeBar;
	if (nRight<=rectCtrl.right)
	{
		CRect rect;
		rect.left = nLeft;
		rect.right = nRight;
		rect.top = rectCtrl.top;
		rect.bottom = rectCtrl.bottom;

		if (GetStyle()&PBS_SMOOTH)
			dc.FillSolidRect(rect, ctrlBarColor);
		else
		{
			for(int i=0; i<factorBar; ++i)
			{
				curRect.left = nLeft + i*nBlock;
				curRect.right = curRect.left + blW;
				curRect.top = rect.top;
				curRect.bottom = rect.bottom;
				dc.FillSolidRect(curRect, ctrlBarColor);
			}
		}
	}
	else
	{
		int nSize2 = nRight-rectCtrl.right;
		nRight = rectCtrl.right;
		CRect rect1, rect2;
		rect1.left = nLeft; rect2.left = rectCtrl.left;
		rect1.right = nRight; rect2.right = nSize2;
		rect1.top = rect2.top = rectCtrl.top;
		rect1.bottom = rect2.bottom = rectCtrl.bottom;
		
		if (GetStyle()&PBS_SMOOTH)
		{
			dc.FillSolidRect(rect1, ctrlBarColor);
			dc.FillSolidRect(rect2, ctrlBarColor);
		}
		else
		{
			curRect.left = curRect.right = rect1.left;
			curRect.top = rect1.top; curRect.bottom = rect1.bottom;
			while(curRect.right<rect1.right)
			{
				curRect.right = curRect.left + blW;
				if (curRect.right>rect1.right)
					curRect.right = rect1.right;
				dc.FillSolidRect(curRect, ctrlBarColor);
				curRect.left = curRect.right + APL_GUI_EX_SPACE;
			}

			curRect.left = curRect.right = rect2.right;
			curRect.top = rect2.top; curRect.bottom = rect2.bottom;
			while(curRect.left>rect2.left)
			{
				curRect.left = curRect.right - blW;
				if (curRect.left<rect2.left)
					curRect.left = rect2.left;
				dc.FillSolidRect(curRect, ctrlBarColor);
				curRect.right = curRect.left - APL_GUI_EX_SPACE;
			}
		}
	}

	w_dc.BitBlt(rectCtrl_.left, rectCtrl_.top, rectCtrl_.Width(), rectCtrl_.Height(), &dc, rectCtrl_.left, rectCtrl_.top, SRCCOPY);
}

void CaplProgressCtrlEx::Start()
{
	m_iOldPos = GetPos();
	GetRange(m_iOldLower, m_iOldUpper);
	SetRange32(0,300);
	SetMarqueeSize(600);
	SetStep(1);
	CRect rect;
	GetWindowRect(rect);
	int time((int)((rect.Width()*APL_GUI_EX_BAR_FULL_TIME)/600000));
	SetMarqueeMode(true, time);
}

void CaplProgressCtrlEx::End()
{
	SetMarqueeMode(false, m_nInterval);
	SetRange(m_iOldLower, m_iOldUpper);
	SetPos(m_iOldPos);
}