PROWAREtech
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);
}
Comment