PROWAREtech

articles » current » windows » microsoft-foundation-classes » analog-clock

Windows MFC: Analog Clock Example

An example analog clock bult with Microsoft Foundation Classes.

See related: JavaScript Analog Clock and Blazor WASM Analog Clock

This is a very nice example of a MFC analog clock application courtesy of Programming Windows with MFC 2nd Ed. by Jeff Prosise. Compare it to the .NET Analog Clock. Download the complete source code: MFCCLOCK.zip

#include <afxwin.h>
#include <math.h>
#include "Clock.h"
#include "Resource.h"

#define SQUARESIZE	 20
#define ID_TIMER_CLOCK	1

CClockApp theApp;

BOOL CClockApp::InitInstance()
{
	m_pMainWnd = new CMainWnd;
	if(!((CMainWnd*)m_pMainWnd)->RestoreWindowState())
		m_pMainWnd->ShowWindow(m_nCmdShow);
	m_pMainWnd->UpdateWindow();
	return TRUE;
}

//SOME IMPORTANT MFC MACROS THAT WE ARE USING -- DO NOT REMOVE
BEGIN_MESSAGE_MAP(CMainWnd, CFrameWnd)
	ON_WM_CREATE()
	ON_WM_PAINT()
	ON_WM_TIMER()
	ON_WM_GETMINMAXINFO()
	ON_WM_NCHITTEST()
	ON_WM_SYSCOMMAND()
	ON_WM_CONTEXTMENU()
	ON_WM_ENDSESSION()
	ON_WM_CLOSE()
END_MESSAGE_MAP()

CMainWnd::CMainWnd()
{
	this->m_bAutoMenuEnable = FALSE;

	CTime time = CTime::GetCurrentTime();
	this->m_nPrevSecond = time.GetSecond();
	this->m_nPrevMinute = time.GetMinute();
	this->m_nPrevHour = time.GetHour() % 12; // 24 hour clock

	Create(AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW,
		theApp.LoadStandardCursor(IDC_ARROW),
		(HBRUSH)(COLOR_3DFACE+1),
		theApp.LoadIcon(IDI_APPICON)), _T("Clock"));
}

BOOL CMainWnd::PreCreateWindow(CREATESTRUCT &cs)
{
	if(!CFrameWnd::PreCreateWindow(cs))
		return FALSE;
	cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
	return TRUE;
}

int CMainWnd::OnCreate(LPCREATESTRUCT lpcs)
{
	if(CFrameWnd::OnCreate(lpcs) == -1)
		return -1;

	//set timer
	if(!SetTimer(ID_TIMER_CLOCK, 1000, NULL))
	{
		MessageBox(_T("SetTimer() failed."), _T("ERROR"), MB_ICONSTOP|MB_OK);
		return -1;
	}

	CMenu *pMenu = GetSystemMenu(FALSE);
	pMenu->AppendMenu(MF_SEPARATOR);
	pMenu->AppendMenu(MF_STRING, IDM_SYSMENU_FULL_WINDOW, _T("Remove &Title"));
	pMenu->AppendMenu(MF_STRING, IDM_SYSMENU_STAY_ON_TOP, _T("Stay on To&p"));
	return 0;
}

void CMainWnd::OnClose()
{
	SaveWindowState();
	KillTimer(ID_TIMER_CLOCK);
	CFrameWnd::OnClose();
}

void CMainWnd::OnEndSession(BOOL bEnding)
{
	if(bEnding)
		SaveWindowState();
	CFrameWnd::OnEndSession(bEnding);
}

void CMainWnd::OnGetMinMaxInfo(MINMAXINFO *pMMI)
{
	pMMI->ptMinTrackSize.x = 120;
	pMMI->ptMinTrackSize.y = 120;
}

LRESULT CMainWnd::OnNcHitTest(CPoint point)
{
	LRESULT nHitTest = CFrameWnd::OnNcHitTest(point);
	if((nHitTest == HTCLIENT) && (::GetAsyncKeyState(MK_LBUTTON) < 0))
		nHitTest = HTCAPTION;
	return nHitTest;
}

void CMainWnd::OnSysCommand(UINT nID, LPARAM lParam)
{
	UINT nMaskedID = nID & 0xFFF0;

	if(nMaskedID == IDM_SYSMENU_FULL_WINDOW)
	{
		this->m_bFullWindow = this->m_bFullWindow ? 0 : 1;
		this->SetTitleBarState();
		return;
	}
	else if(nMaskedID == IDM_SYSMENU_STAY_ON_TOP)
	{
		this->m_bStayOnTop = this->m_bStayOnTop ? 0 : 1;
		this->SetTopMostState();
	}
	CFrameWnd::OnSysCommand(nID, lParam);
}

void CMainWnd::OnContextMenu(CWnd *pWnd, CPoint point)
{
	CRect rect;
	this->GetClientRect(&rect);
	this->ClientToScreen(&rect);
	if(rect.PtInRect(point))
	{
		CMenu *pMenu = this->GetSystemMenu(FALSE);
		UpdateSystemMenu(pMenu);

		int nID = (int)pMenu->TrackPopupMenu(TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_RIGHTBUTTON|TPM_RETURNCMD, point.x, point.y, this);

		if(nID > 0)
			this->SendMessage(WM_SYSCOMMAND, nID, 0);

		return;
	}
	CFrameWnd::OnContextMenu(pWnd, point);
}

void CMainWnd::OnTimer(UINT nTimerID)
{
	if(this->IsIconic())
		return;

	CTime time = CTime::GetCurrentTime();
	int nSecond = time.GetSecond();
	int nMinute = time.GetMinute();
	int nHour = time.GetHour() % 12;

	if((nSecond == this->m_nPrevSecond) && 
		(nMinute == this->m_nPrevMinute) && 
		(nHour == this->m_nPrevHour))
		return;

	CRect rect;
	this->GetClientRect(&rect);

	CClientDC dc(this);
	dc.SetMapMode(MM_ISOTROPIC);
	dc.SetWindowExt(1000, 1000);
	dc.SetViewportExt(rect.Width(), -rect.Height());
	dc.SetViewportOrg(rect.Width()/2, rect.Height()/2);

	COLORREF clrColor = ::GetSysColor(COLOR_3DFACE);

	if(nMinute != this->m_nPrevMinute)
	{
		this->DrawHand(&dc, 200, 4, (this->m_nPrevHour * 30) + (this->m_nPrevMinute / 2), clrColor);
		this->DrawHand(&dc, 400, 8, this->m_nPrevMinute * 6, clrColor);
		this->m_nPrevMinute = nMinute;
		this->m_nPrevHour = nHour;
	}

	if(nSecond != this->m_nPrevSecond)
	{
		this->DrawSecondHand(&dc, 400, 8, this->m_nPrevSecond * 6, clrColor);
		this->DrawSecondHand(&dc, 400, 8, nSecond * 6, RGB(0,0,0));
		this->DrawHand(&dc, 200, 4, (nHour * 30) + (nMinute / 2), RGB(0,0,0));
		this->DrawHand(&dc, 400, 8, nMinute * 6, RGB(0,0,0));
		this->m_nPrevSecond = nSecond;
	}
}

void CMainWnd::OnPaint()
{
	CRect rect;
	this->GetClientRect(&rect);

	CPaintDC dc(this);
	dc.SetMapMode(MM_ISOTROPIC);
	dc.SetWindowExt(1000, 1000);
	dc.SetViewportExt(rect.Width(), -rect.Height());
	dc.SetViewportOrg(rect.Width()/2, rect.Height()/2);

	this->DrawClockFace(&dc);
	this->DrawHand(&dc, 200, 4, (this->m_nPrevHour * 30) + (this->m_nPrevMinute / 2), RGB(0,0,0));
	this->DrawHand(&dc, 400, 8, this->m_nPrevMinute * 6, RGB(0,0,0));
	this->DrawSecondHand(&dc, 400, 8, this->m_nPrevSecond * 6, RGB(0,0,0));
}

void CMainWnd::DrawClockFace(CDC *pDC)
{
	static CPoint point[12] = {
		CPoint(	 0, 450),
		CPoint( 225, 390),
		CPoint( 390, 225),
		CPoint( 450,	 0),
		CPoint( 390,-225),
		CPoint( 225,-390),
		CPoint(	 0,-450),
		CPoint(-225,-390),
		CPoint(-390,-225),
		CPoint(-450,	 0),
		CPoint(-390, 225),
		CPoint(-225, 390),
	};

	pDC->SelectStockObject(NULL_BRUSH);
	for(int i = 0; i < 12; i++)
		pDC->Rectangle(
		point[i].x - SQUARESIZE,
		point[i].y + SQUARESIZE,
		point[i].x + SQUARESIZE,
		point[i].y - SQUARESIZE);
}

void CMainWnd::DrawHand(CDC *pDC, int nLength, int nScale, int nDegrees, COLORREF clrColor)
{
	CPoint point[4];
	double nRadians = (double)nDegrees * 0.017453292;
	
	point[0].x = (int)(nLength * sin(nRadians));
	point[0].y = (int)(nLength * cos(nRadians));

	point[2].x = -point[0].x / nScale;
	point[2].y = -point[0].y / nScale;

	point[1].x = -point[2].y;
	point[1].y = point[2].x;

	point[3].x = -point[1].x;
	point[3].y = -point[1].y;

	CPen pen(PS_SOLID, 2, clrColor);
	CPen *pOldPen = pDC->SelectObject(&pen);
	pDC->MoveTo(point[0]);
	pDC->LineTo(point[1]);
	pDC->LineTo(point[2]);
	pDC->LineTo(point[3]);
	pDC->LineTo(point[0]);
	pDC->SelectObject(pOldPen);
}

void CMainWnd::DrawSecondHand(CDC *pDC, int nLength, int nScale, int nDegrees, COLORREF clrColor)
{
	CPoint point[2];
	double nRadians = (double)nDegrees * 0.017453292;

	point[0].x = (int)(nLength * sin(nRadians));
	point[0].y = (int)(nLength * cos(nRadians));

	point[1].x = -point[0].x / nScale;
	point[1].y = -point[0].y / nScale;

	CPen pen(PS_SOLID, 2, clrColor);
	CPen *pOldPen = pDC->SelectObject(&pen);
	pDC->MoveTo(point[0]);
	pDC->LineTo(point[1]);
	pDC->SelectObject(pOldPen);
}

void CMainWnd::SetTitleBarState()
{
	CMenu *pMenu = this->GetSystemMenu(FALSE);

	if(this->m_bFullWindow)
	{
		this->ModifyStyle(WS_CAPTION, 0);
		pMenu->ModifyMenu(IDM_SYSMENU_FULL_WINDOW, MF_STRING,
			IDM_SYSMENU_FULL_WINDOW, _T("Restore &Title"));
	}
	else
	{
		this->ModifyStyle(0, WS_CAPTION);
		pMenu->ModifyMenu(IDM_SYSMENU_FULL_WINDOW, MF_STRING,
			IDM_SYSMENU_FULL_WINDOW, _T("Remove &Title"));
	}
	SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_DRAWFRAME);
}

void CMainWnd::SetTopMostState()
{
	CMenu *pMenu = this->GetSystemMenu(FALSE);

	if(this->m_bStayOnTop)
	{
		this->SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
		pMenu->CheckMenuItem(IDM_SYSMENU_STAY_ON_TOP, MF_CHECKED);
	}
	else
	{
		this->SetWindowPos(&wndNoTopMost, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
		pMenu->CheckMenuItem(IDM_SYSMENU_STAY_ON_TOP, MF_UNCHECKED);
	}
}

BOOL CMainWnd::RestoreWindowState()
{
	CString version = _T("Version 1.0");
	this->m_bFullWindow = theApp.GetProfileInt(version, _T("FullWindow"), 0);
	this->SetTitleBarState();
	this->m_bStayOnTop = theApp.GetProfileInt(version, _T("StayOnTop"), 0);
	this->SetTopMostState();

	WINDOWPLACEMENT wp;
	wp.length = sizeof(wp);
	this->GetWindowPlacement(&wp);

	if(((wp.flags = theApp.GetProfileInt(version, _T("flags"), -1)) != -1) && 
		((wp.showCmd = theApp.GetProfileInt(version, _T("showCmd"), -1)) != -1) && 
		((wp.rcNormalPosition.left = theApp.GetProfileInt(version, _T("x1"), -1)) != -1) &&
		((wp.rcNormalPosition.top = theApp.GetProfileInt(version, _T("y1"), -1)) != -1) &&
		((wp.rcNormalPosition.right = theApp.GetProfileInt(version, _T("x2"), -1)) != -1) &&
		((wp.rcNormalPosition.bottom = theApp.GetProfileInt(version, _T("y2"), -1)) != -1))
	{
		wp.rcNormalPosition.left = min(wp.rcNormalPosition.left, ::GetSystemMetrics(SM_CXSCREEN) - ::GetSystemMetrics(SM_CXICON));
		wp.rcNormalPosition.top = min(wp.rcNormalPosition.top, ::GetSystemMetrics(SM_CYSCREEN) - ::GetSystemMetrics(SM_CYICON));
		SetWindowPlacement(&wp);
		return TRUE;
	}
	return FALSE;
}

void CMainWnd::SaveWindowState()
{
	CString version = _T("Version 1.0");
	theApp.WriteProfileInt(version, _T("FullWindow"), this->m_bFullWindow);
	theApp.WriteProfileInt(version, _T("StayOnTop"), this->m_bStayOnTop);

	WINDOWPLACEMENT wp;
	wp.length = sizeof(wp);
	this->GetWindowPlacement(&wp);

	theApp.WriteProfileInt(version, _T("flags"), wp.flags);
	theApp.WriteProfileInt(version, _T("showCmd"), wp.showCmd);
	theApp.WriteProfileInt(version, _T("x1"), wp.rcNormalPosition.left);
	theApp.WriteProfileInt(version, _T("y1"), wp.rcNormalPosition.top);
	theApp.WriteProfileInt(version, _T("x2"), wp.rcNormalPosition.right);
	theApp.WriteProfileInt(version, _T("y2"), wp.rcNormalPosition.bottom);
}

void CMainWnd::UpdateSystemMenu(CMenu *pMenu)
{
	static UINT nState[2][5] = {
		{ MFS_GRAYED, MFS_ENABLED, MFS_ENABLED, MFS_ENABLED, MFS_DEFAULT },
		{ MFS_DEFAULT, MFS_GRAYED, MFS_GRAYED, MFS_ENABLED, MFS_GRAYED }
	};
	
	if(this->IsIconic())
		return;

	int i = 0;
	if(this->IsZoomed())
		i = 1;
	
	CString strMenuText;
	pMenu->GetMenuString(SC_RESTORE, strMenuText, MF_BYCOMMAND);
	pMenu->ModifyMenu(SC_RESTORE, MF_STRING|nState[i][0], SC_RESTORE, strMenuText);

	pMenu->GetMenuString(SC_MOVE, strMenuText, MF_BYCOMMAND);
	pMenu->ModifyMenu(SC_MOVE, MF_STRING|nState[i][1], SC_MOVE, strMenuText);

	pMenu->GetMenuString(SC_SIZE, strMenuText, MF_BYCOMMAND);
	pMenu->ModifyMenu(SC_SIZE, MF_STRING|nState[i][2], SC_SIZE, strMenuText);

	pMenu->GetMenuString(SC_MINIMIZE, strMenuText, MF_BYCOMMAND);
	pMenu->ModifyMenu(SC_MINIMIZE, MF_STRING|nState[i][3], SC_MINIMIZE, strMenuText);

	pMenu->GetMenuString(SC_MAXIMIZE, strMenuText, MF_BYCOMMAND);
	pMenu->ModifyMenu(SC_MAXIMIZE, MF_STRING|nState[i][4], SC_MAXIMIZE, strMenuText);

	::SetMenuDefaultItem(pMenu->m_hMenu, i ? SC_RESTORE : SC_MAXIMIZE, FALSE);
}

PROWAREtech

Hello there! How can I help you today?
Ask any question

PROWAREtech

This site uses cookies. Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site. Read the privacy policy.
ACCEPT REJECT