www.pudn.com > XAnalogClock_demo.zip > XAnalogClock.cpp


// XAnalogClock.cpp  Version 1.0 
// 
// Author:  Hans Dietrich 
//          hdietrich@gmail.com 
// 
// Description: 
//     XAnalogClock implements CXAnalogClock, a skinnable analog clock control. 
// 
// History 
//     Version 1.0 - 2006 March 23 
//     - Initial public release 
// 
// Public APIs: 
//          NAME                           DESCRIPTION 
//     ---------------------    ------------------------------------------- 
//     GetTime()                Get currently displayed time 
//     GetWindowSize()          Get control size 
//     IsClockRunning()         Returns run state of clock 
//     SetBackgroundColor()     Set background color 
//     SetBitmaps()             Set bitmap resource IDs 
//     SetGmtAdjust()           Not currently used 
//     SetHourHandColor()       Set color of hour hand 
//     SetMinuteHandColor()     Set color of minute hand 
//     SetSecondHandColor()     Set color of second hand 
//     SetTime()                Set starting time (if system time not used) 
//     SetTransparentColor()    Set transparent color 
//     ShowDate()               Show/hide date 
//     ShowSecondHand()         Show/hide second hand 
//     Run()                    Start the clock 
//     Stop()                   Stop the clock 
//     UseSystemTime()          Use system time, or user-supplied time (see 
//                              SetTime() 
// 
// License: 
//     This software is released into the public domain.  You are free to use 
//     it in any way you like, except that you may not sell this source code. 
// 
//     This software is provided "as is" with no expressed or implied warranty. 
//     I accept no liability for any damage or loss of business that this 
//     software may cause. 
// 
/////////////////////////////////////////////////////////////////////////////// 
 
#include "stdafx.h" 
#include "XAnalogClock.h" 
#include "math.h" 
#include "WuLine.h" 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
/////////////////////////////////////////////////////////////////////////////// 
// some definitions 
#define DATE_BOX_WIDTH		18 
#define DATE_BOX_HEIGHT		14 
#define DATE_BOX_XPOS		67 
#define DATE_BOX_YPOS		43 
#define SECOND_HAND_LENGTH	35.0 
#define MINUTE_HAND_LENGTH	39.0 
#define HOUR_HAND_LENGTH	28.0 
#define SECOND_HAND_COLOR	RGB(220,20,60) 
#define MINUTE_HAND_COLOR	RGB(0,0,0) 
#define HOUR_HAND_COLOR		RGB(0,0,0) 
#define PI					3.1415926535 
 
/////////////////////////////////////////////////////////////////////////////// 
// CXAnalogClock 
 
BEGIN_MESSAGE_MAP(CXAnalogClock, CStatic) 
	//{{AFX_MSG_MAP(CXAnalogClock) 
	ON_WM_PAINT() 
	ON_WM_ERASEBKGND() 
	ON_WM_TIMER() 
	ON_WM_CREATE() 
	ON_WM_DESTROY() 
	//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
/////////////////////////////////////////////////////////////////////////////// 
// ctor 
CXAnalogClock::CXAnalogClock() 
{ 
	m_nPrevMinute     = -1; 
	m_bShowDate       = TRUE; 
	m_bShowSecondHand = TRUE; 
	m_bUseSystemTime  = TRUE; 
	m_bStopped        = TRUE; 
	m_time            = CTime::GetCurrentTime(); 
	//m_time            = CTime(2006, 1, 15, 8, 40, 0); 
	m_dGmtAdjust      = 0.0; 
	m_rgbBackground   = RGB(255,0,255); 
	m_rgbTransparent  = RGB(255,0,255); 
	m_rgbSecondHand   = SECOND_HAND_COLOR; 
	m_rgbMinuteHand   = MINUTE_HAND_COLOR; 
	m_rgbHourHand     = HOUR_HAND_COLOR; 
	m_nFaceBitmapId   = 0; 
	m_nDateBitmapId   = 0; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// dtor 
CXAnalogClock::~CXAnalogClock() 
{ 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// PreSubclassWindow 
void CXAnalogClock::PreSubclassWindow() 
{ 
	CClientDC dc(NULL); 
	LoadBitmaps(&dc); 
 
	CStatic::PreSubclassWindow(); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// GetWindowSize 
CSize CXAnalogClock::GetWindowSize() 
{ 
	CClientDC dc(NULL); 
	LoadBitmaps(&dc); 
 
	return m_cdcClockFace.m_sizeBitmap; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// LoadBitmaps 
void CXAnalogClock::LoadBitmaps(CDC *pDC) 
{ 
	if ((m_nFaceBitmapId == 0) || (m_nDateBitmapId == 0)) 
	{ 
		TRACE(_T("bitmap resource IDs have not been set\n")); 
		return; 
	} 
 
	// load clockface.bmp 
	if (!m_cdcClockFace.IsValid()) 
		VERIFY(m_cdcClockFace.LoadBitmap(m_nFaceBitmapId, pDC)); 
 
	// load date.bmp 
	if (!m_cdcDate.IsValid()) 
		VERIFY(m_cdcDate.LoadBitmap(m_nDateBitmapId, pDC)); 
 
	if (!m_cdcPrevious.IsValid()) 
		VERIFY(m_cdcPrevious.LoadBitmap(m_nFaceBitmapId, pDC)); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// OnPaint 
void CXAnalogClock::OnPaint() 
{ 
	if ((m_nFaceBitmapId == 0) || (m_nDateBitmapId == 0)) 
	{ 
		TRACE(_T("bitmap resource IDs have not been set\n")); 
		return; 
	} 
 
	CPaintDC dc(this); // device context for painting 
 
	LoadBitmaps(&dc); 
 
	if (m_bUseSystemTime) 
		m_time = CTime::GetCurrentTime(); 
 
	CRect rect; 
	GetClientRect(&rect); 
 
	if ((rect.Width() < m_cdcClockFace.m_sizeBitmap.cx) || 
		(rect.Height() < m_cdcClockFace.m_sizeBitmap.cy)) 
	{ 
		TRACE(_T("ERROR - control dimensions are too small for clock\n")); 
	} 
 
	CDC memDC; 
	memDC.CreateCompatibleDC(&dc); 
	CBitmap bitmap; 
	bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height()); 
	CBitmap *pOldBitmap = memDC.SelectObject(&bitmap); 
 
	// fill in background color 
	CBrush brush(m_rgbBackground); 
	memDC.FillRect(&rect, &brush); 
 
	if (m_nPrevMinute != GetMinute()) 
	{ 
		// draw clock face 
		::TransparentBlt(memDC.m_hDC, 
			0, 0, 
			m_cdcClockFace.m_sizeBitmap.cx, m_cdcClockFace.m_sizeBitmap.cy, 
			m_cdcClockFace.m_dcMemory.m_hDC, 
			0, 0, 
			m_cdcClockFace.m_sizeBitmap.cx, m_cdcClockFace.m_sizeBitmap.cy, 
			m_rgbTransparent); 
 
		if (m_bShowDate) 
			PaintDate(&memDC); 
 
		PaintMinuteAndHourHands(&memDC); 
 
		// save clock face & hands for this minute 
		m_cdcPrevious.m_dcMemory.BitBlt(0, 0, 
			m_cdcClockFace.m_sizeBitmap.cx, m_cdcClockFace.m_sizeBitmap.cy, 
			&memDC, 0, 0, SRCCOPY); 
 
		m_nPrevMinute = GetMinute(); 
	} 
	else 
	{ 
		// minute hasn't changed, so use previous clock face & hands 
		memDC.BitBlt(0, 0, 
			rect.Width(), rect.Height(), 
			&m_cdcPrevious.m_dcMemory, 
			0, 0, SRCCOPY); 
	} 
 
	if (m_bShowSecondHand) 
		PaintSecondHand(&memDC); 
 
	// finally blt to screen 
	::TransparentBlt(dc.m_hDC, 
		0, 0, 
		rect.Width(), rect.Height(), 
		memDC.m_hDC, 
		0, 0, 
		rect.Width(), rect.Height(), 
		m_rgbTransparent); 
 
	memDC.SelectObject(pOldBitmap); 
 
	// Do not call CStatic::OnPaint() for painting messages 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// ConvertToDegrees 
float CXAnalogClock::ConvertToDegrees(float dTime) 
{ 
	float degrees = 0.; 
 
	degrees = (float)(90. - dTime * 6.0); 
	if (degrees < 0.0) 
		degrees = (float)360. + degrees; 
	return degrees; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// PaintDate 
void CXAnalogClock::PaintDate(CDC *pDC) 
{ 
	LOGFONT lf; 
	CFont *pFont = GetFont(); 
	if (pFont == NULL) 
		pFont = GetParent()->GetFont(); 
	if (pFont) 
	{ 
		pFont->GetLogFont(&lf); 
		//_tcscpy(lf.lfFaceName, _T("MS Sans Serif")); 
		_tcscpy(lf.lfFaceName, _T("Arial")); 
		//lf.lfWeight = FW_BOLD; 
		if (lf.lfHeight < 0) 
			lf.lfHeight += 1; 
		else 
			lf.lfHeight -= 1; 
 
		CFont font; 
		font.CreateFontIndirect(&lf); 
 
		CString strDate = _T(""); 
		strDate.Format("%2d", GetDay()); 
 
		CRect rect(0, 0, DATE_BOX_WIDTH, DATE_BOX_HEIGHT); 
 
		// create temporary dc to hold the date numerals 
		CDCBuffer cdcTemp; 
		VERIFY(cdcTemp.LoadBitmap(m_nDateBitmapId, pDC)); 
		CFont *pOldFont = cdcTemp.m_dcMemory.SelectObject(&font); 
		CBrush brush(RGB(255,0,255)); 
		cdcTemp.m_dcMemory.FillRect(&rect, &brush); 
		cdcTemp.m_dcMemory.SetBkColor(RGB(255,0,255)); 
		cdcTemp.m_dcMemory.ExtTextOut(3, 1, 0, &rect, strDate, NULL); 
 
		// draw date box template 
		pDC->BitBlt(DATE_BOX_XPOS, DATE_BOX_YPOS, DATE_BOX_WIDTH, DATE_BOX_HEIGHT, 
			&m_cdcDate.m_dcMemory, 0, 0, SRCCOPY); 
 
		// overlay with date numerals 
		::TransparentBlt(pDC->m_hDC, 
			DATE_BOX_XPOS+1, DATE_BOX_YPOS-1, 
			DATE_BOX_WIDTH, DATE_BOX_HEIGHT, 
			cdcTemp.m_dcMemory.m_hDC, 
			0, 0, 
			DATE_BOX_WIDTH, DATE_BOX_HEIGHT, 
			RGB(255,0,255)); 
 
		cdcTemp.m_dcMemory.SelectObject(pOldFont); 
	} 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// PaintMinuteAndHourHands 
void CXAnalogClock::PaintMinuteAndHourHands(CDC *pDC) 
{ 
	/////////////////////////////////////////////////////////////////////////// 
	// draw minute hand 
	int minute = GetMinute(); 
 
	float degrees, radians, cosine, sine; 
	short x1, y1, x2, y2; 
 
	degrees = ConvertToDegrees((float)minute); 
	radians = (float) ((PI * degrees)/180.); 
	cosine = (float)cos(radians); 
	sine = (float)sin(radians); 
 
	x1 = (short)(MINUTE_HAND_LENGTH * cosine + (float)m_cdcClockFace.m_sizeBitmap.cx/2); 
	y1 = (short)(-MINUTE_HAND_LENGTH * sine + (float)m_cdcClockFace.m_sizeBitmap.cy/2); 
	x2 = (short)(5.0 * cosine + (float)m_cdcClockFace.m_sizeBitmap.cx/2); 
	y2 = (short)(-5.0 * sine + (float)m_cdcClockFace.m_sizeBitmap.cy/2); 
 
	if (((minute >= 10) && (minute <= 20)) || ((minute >= 40) && (minute <= 50))) 
	{ 
		x1 = (short)(MINUTE_HAND_LENGTH * cosine + (float)m_cdcClockFace.m_sizeBitmap.cx/2); 
		y1 = (short)(-MINUTE_HAND_LENGTH * sine + (float)m_cdcClockFace.m_sizeBitmap.cy/2); 
		DrawWuLine(pDC, x1, y1, x2, y2, m_rgbMinuteHand); 
		x1 = (short)((MINUTE_HAND_LENGTH-4) * cosine + (float)m_cdcClockFace.m_sizeBitmap.cx/2); 
		y1 = (short)((-MINUTE_HAND_LENGTH+4) * sine + (float)m_cdcClockFace.m_sizeBitmap.cy/2); 
		DrawWuLine(pDC, x1, y1, x2, y2-2, m_rgbMinuteHand); 
		DrawWuLine(pDC, x1, y1, x2, y2-1, m_rgbMinuteHand); 
		DrawWuLine(pDC, x1, y1, x2, y2+1, m_rgbMinuteHand); 
		DrawWuLine(pDC, x1, y1, x2, y2+2, m_rgbMinuteHand); 
	} 
	else 
	{ 
		x1 = (short)(MINUTE_HAND_LENGTH * cosine + (float)m_cdcClockFace.m_sizeBitmap.cx/2); 
		y1 = (short)(-MINUTE_HAND_LENGTH * sine + (float)m_cdcClockFace.m_sizeBitmap.cy/2); 
		DrawWuLine(pDC, x1, y1, x2, y2, m_rgbMinuteHand); 
		x1 = (short)((MINUTE_HAND_LENGTH-4) * cosine + (float)m_cdcClockFace.m_sizeBitmap.cx/2); 
		y1 = (short)((-MINUTE_HAND_LENGTH+4) * sine + (float)m_cdcClockFace.m_sizeBitmap.cy/2); 
		DrawWuLine(pDC, x1, y1, x2-2, y2, m_rgbMinuteHand); 
		DrawWuLine(pDC, x1, y1, x2-1, y2, m_rgbMinuteHand); 
		DrawWuLine(pDC, x1, y1, x2+1, y2, m_rgbMinuteHand); 
		DrawWuLine(pDC, x1, y1, x2+2, y2, m_rgbMinuteHand); 
	} 
 
	/////////////////////////////////////////////////////////////////////////// 
	// draw hour hand 
	int hour = GetHour(); 
	if (hour > 12) 
		hour -= 12; 
	hour = hour*5 + GetMinute()/12; 
	degrees = ConvertToDegrees((float)hour); 
	radians = (float) ((PI * degrees)/180.); 
	cosine = (float)cos(radians); 
	sine = (float)sin(radians); 
 
	x1 = (short)(HOUR_HAND_LENGTH * cosine + (float)m_cdcClockFace.m_sizeBitmap.cx/2); 
	y1 = (short)(-HOUR_HAND_LENGTH * sine + (float)m_cdcClockFace.m_sizeBitmap.cy/2); 
	x2 = (short)(5.0 * cosine + (float)m_cdcClockFace.m_sizeBitmap.cx/2); 
	y2 = (short)(-5.0 * sine + (float)m_cdcClockFace.m_sizeBitmap.cy/2); 
 
	if (((hour >= 10) && (hour <= 20)) || ((hour >= 40) && (hour <= 50))) 
	{ 
		DrawWuLine(pDC, x1, y1, x2, y2, m_rgbHourHand); 
		x1 = (short)((HOUR_HAND_LENGTH-4) * cosine + (float)m_cdcClockFace.m_sizeBitmap.cx/2); 
		y1 = (short)((-HOUR_HAND_LENGTH+4) * sine + (float)m_cdcClockFace.m_sizeBitmap.cy/2); 
		DrawWuLine(pDC, x1, y1, x2, y2-2, m_rgbHourHand); 
		DrawWuLine(pDC, x1, y1, x2, y2-1, m_rgbHourHand); 
		DrawWuLine(pDC, x1, y1, x2, y2+1, m_rgbHourHand); 
		DrawWuLine(pDC, x1, y1, x2, y2+2, m_rgbHourHand); 
	} 
	else 
	{ 
		DrawWuLine(pDC, x1, y1, x2, y2, m_rgbHourHand); 
		x1 = (short)((HOUR_HAND_LENGTH-4) * cosine + (float)m_cdcClockFace.m_sizeBitmap.cx/2); 
		y1 = (short)((-HOUR_HAND_LENGTH+4) * sine + (float)m_cdcClockFace.m_sizeBitmap.cy/2); 
		DrawWuLine(pDC, x1, y1, x2-2, y2, m_rgbHourHand); 
		DrawWuLine(pDC, x1, y1, x2-1, y2, m_rgbHourHand); 
		DrawWuLine(pDC, x1, y1, x2+1, y2, m_rgbHourHand); 
		DrawWuLine(pDC, x1, y1, x2+2, y2, m_rgbHourHand); 
	} 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// PaintSecondHand 
void CXAnalogClock::PaintSecondHand(CDC *pDC) 
{ 
	if (m_bShowSecondHand) 
	{ 
		// draw second hand 
		float degrees, radians, cosine, sine; 
		short x1, y1, x2, y2; 
 
		degrees = ConvertToDegrees((float)GetSecond()); 
		radians = (float)((PI * degrees)/180.); 
		cosine = (float)cos(radians); 
		sine = (float)sin(radians); 
 
		x1 = (short)(SECOND_HAND_LENGTH * cosine + (float)m_cdcClockFace.m_sizeBitmap.cx/2); 
		y1 = (short)(-SECOND_HAND_LENGTH * sine + (float)m_cdcClockFace.m_sizeBitmap.cy/2); 
		x2 = (short)(5.0 * cosine + (float)m_cdcClockFace.m_sizeBitmap.cx/2); 
		y2 = (short)(-5.0 * sine + (float)m_cdcClockFace.m_sizeBitmap.cy/2); 
 
		DrawWuLine(pDC, x1, y1, x2, y2, m_rgbSecondHand); 
		DrawWuLine(pDC, x1+1, y1, x2+1, y2, m_rgbSecondHand); 
	} 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// OnEraseBkgnd 
BOOL CXAnalogClock::OnEraseBkgnd(CDC* pDC) 
{ 
	return CStatic::OnEraseBkgnd(pDC); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// OnTimer 
void CXAnalogClock::OnTimer(UINT nIDEvent) 
{ 
	if (!m_bUseSystemTime) 
	{ 
		CTimeSpan ts(0, 0, 0, 1); 
		m_time = m_time + ts; 
	} 
	Invalidate(FALSE); 
	CStatic::OnTimer(nIDEvent); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// Run 
void CXAnalogClock::Run() 
{ 
	m_nPrevMinute = -1; 
	SetTimer(1, 1000, NULL); 
	Invalidate(); 
	m_bStopped = FALSE; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// Stop 
void CXAnalogClock::Stop() 
{ 
	KillTimer(1); 
	m_bStopped = TRUE; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// GetSecond 
int CXAnalogClock::GetSecond() 
{ 
	return m_time.GetSecond(); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// GetMinute 
int CXAnalogClock::GetMinute() 
{ 
	return m_time.GetMinute(); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// GetHour 
int CXAnalogClock::GetHour() 
{ 
	int hour = m_time.GetHour(); 
	if (hour > 12) 
		hour -= 12; 
	return hour; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// GetDay 
int CXAnalogClock::GetDay() 
{ 
	int day = m_time.GetDay(); 
	return day; 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// PreTranslateMessage 
BOOL CXAnalogClock::PreTranslateMessage(MSG* pMsg) 
{ 
	return CStatic::PreTranslateMessage(pMsg); 
} 
 
/////////////////////////////////////////////////////////////////////////////// 
// OnDestroy 
void CXAnalogClock::OnDestroy()  
{ 
	Stop(); 
	CStatic::OnDestroy(); 
}