📄 multtimer.cpp
字号:
//
// FILE: MultTimer.cpp
//
// Copyright (c) 1997 by Aaron Michael Cohen and Mike Woodring
//
/////////////////////////////////////////////////////////////////////////
#include "MultTimer.h"
// define the single application instance...
CMultTimerApp theApp;
// static class variables for popping up the dialogs just to
// the right and below the last one...
int CMultTimerDlg::s_LastDialogX = 0;
int CMultTimerDlg::s_LastDialogY = 0;
// CMultTimerApp application class member functions...
BOOL CMultTimerApp::InitInstance(void) {
// no child threads at first...
m_lChildThreadCount = 0;
CMultTimerDlg *pDlg = new CMultTimerDlg();
// this is also the application main window...
m_pMainWnd = pDlg;
// inform the user...
CString csTitle;
pDlg->GetWindowText(csTitle);
csTitle += " (primary thread)";
pDlg->SetWindowText(csTitle);
return TRUE;
}
void CMultTimerApp::IncrementChildThreadCount(void) {
::InterlockedIncrement(&m_lChildThreadCount);
}
void CMultTimerApp::DecrementChildThreadCount(void) {
::InterlockedDecrement(&m_lChildThreadCount);
}
BOOL CMultTimerApp::CanExitNow(void) {
return (m_lChildThreadCount == 0);
}
CMultTimerThread::InitInstance(void) {
// increase the application child thread count...
theApp.IncrementChildThreadCount();
// create a dialog that will run on a secondary thread...
CMultTimerDlg *pDlg = new CMultTimerDlg();
// set the main window for this thread
// the thread will exit when this window closes...
SetMainWnd(pDlg);
// inform the user...
CString csTitle;
pDlg->GetWindowText(csTitle);
csTitle += " (child thread)";
pDlg->SetWindowText(csTitle);
return TRUE;
}
int CMultTimerThread::ExitInstance(void) {
// decrease the application child thread count...
theApp.DecrementChildThreadCount();
// call the parent class implementation...
int nStatus = CMcl4MfcGUIThread::ExitInstance();
// delete ourselves...
delete this;
// return the saved status...
return nStatus;
}
// CMultTimerDlg dialog class message map...
BEGIN_MESSAGE_MAP( CMultTimerDlg, CDialog)
ON_WM_PAINT()
ON_WM_SYSCOMMAND()
END_MESSAGE_MAP()
// CMultTimerDlg dialog class member functions...
CMultTimerDlg::CMultTimerDlg() {
m_ctStartTime = 0;
m_bTimerRunning = FALSE;
m_nTicks = 0;
// The parent of the dialog window will be sent
// WM_ACTIVATE and WM_ACTIVATEAPP messages when the new dialog
// is made active. these are sent from the thread of the new
// dilaog and this will cause the dialog to be frozen
// if the parent is blocked, even if the parent window is
// processed on another thread.
//
// In addition the system may attach the raw input queue of the
// new thread to the thread of the parent if the parent is a
// dialog box.
//
// It is best to use the desktop window as the
// parent (or the new dialog window itself,
// which amounts to the same thing) so that the new dialog
// has no dependancies on windows from other threads. Using
// null makes the main window m_pMainWnd
// the dialog's parent.
//
// If the default parent is used, or the parent window
// is NULL, the application main window will be used as
// the parent, and this causes the blocking problem.
Create(IDD_MULTTIMER_DLG, GetDesktopWindow());
}
void CMultTimerDlg::PostNcDestroy() {
delete this;
}
// CMultTimerDlg dialog class message map handlers...
BOOL CMultTimerDlg::OnInitDialog() {
CDialog::OnInitDialog();
// attach our member object to the edit control...
m_ceSeconds.SubclassDlgItem( IDC_SECONDS, this);
m_ceSeconds.SetWindowText("5");
// we must set the new dialog box as the foreground
// window because dialogs created on secondary threads
// will pop up BEHIND the active application window...
SetForegroundWindow();
// set the dialog icon...
HICON hIcon = AfxGetApp()->LoadIcon(IDI_MULTTIMER);
SetIcon( hIcon, TRUE);
// place the window offset from the last one we created...
if (s_LastDialogX || s_LastDialogY) {
// not the first dialog...
s_LastDialogX += 10;
s_LastDialogY += 10;
SetWindowPos( NULL, s_LastDialogX, s_LastDialogY, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
}
else {
// this is the first dialog, center it and remember
// where we put it...
CenterWindow();
CRect cr;
GetWindowRect(cr);
s_LastDialogX = cr.left;
s_LastDialogY = cr.top;
}
return TRUE;
}
void CMultTimerDlg::OnSysCommand( UINT nID, LPARAM lParam) {
if ((nID & 0xFFF0) == SC_CLOSE) {
// treat the main window specially...
if (this == theApp.m_pMainWnd) {
// this is the dialog on the primary thread,
// check if the other dialog threads have exited and
// only exit the program if this is TRUE...
if (theApp.CanExitNow()) {
// destroy the modeless dialog window,
// since this is the main window, the application
// will exit...
DestroyWindow();
}
else {
AfxMessageBox( "Cannot exit, child dialogs still open.", MB_ICONSTOP);
}
}
else {
// destroy the modeless dialog window...
DestroyWindow();
// Destroying the main window for a thread will cause the
// thread to exit. If we had not set the main window for the
// thread to this window, we could exit the thread with a
// call to ::PostQuitMessage(0);
}
}
else {
CDialog::OnSysCommand( nID, lParam);
}
}
void CMultTimerDlg::OnCancel() {
// create a new dialog box running on it's own thread...
new CMultTimerThread();
// dialog box thread C++ object will delete itself in
// it's ExitInstance handler...
}
void CMultTimerDlg::OnOK() {
// tell the OnPaint handler that we are running...
m_bTimerRunning = TRUE;
// read the user input...
CString csTime;
m_ceSeconds.GetWindowText(csTime);
// convert to clock ticks...
m_nTicks = atoi(csTime) * CLOCKS_PER_SEC;
// read the current time...
m_ctStartTime = clock();
// run in a tight loop...
// we need the RedrawWindow to use SendMessage calls to
// invoke the OnPaint handler when we are not processing
// the application message loop...
while ((clock() - m_ctStartTime) < m_nTicks) {
// invalidate the time portion of the window and
// update the display...
RedrawWindow( m_crLastPaint, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
// update the display ten times a second...
Sleep(100);
}
// all done, tell the OnPaint handler we've stopped...
m_bTimerRunning = FALSE;
// update the time display one last time, this time we
// don't need the RDW_UPDATENOW because we are going to
// process messages again...
RedrawWindow( m_crLastPaint, NULL, RDW_INVALIDATE);
}
afx_msg void CMultTimerDlg::OnPaint() {
CPaintDC dc(this);
// read the current time...
clock_t ctNow = clock();
clock_t ctRemaining;
// compute the time remaining,
// if the timer is not running show all zero's...
if (m_bTimerRunning) {
ctRemaining = m_nTicks - ctNow + m_ctStartTime;
}
else {
ctRemaining = 0;
}
// compute the parts of time...
int hours = ctRemaining / (CLOCKS_PER_SEC * 60 * 60);
ctRemaining -= hours * (CLOCKS_PER_SEC * 60 * 60);
int minutes = ctRemaining / (CLOCKS_PER_SEC * 60);
ctRemaining -= minutes * (CLOCKS_PER_SEC * 60);
int seconds = ctRemaining / (CLOCKS_PER_SEC);
ctRemaining -= seconds * CLOCKS_PER_SEC;
int hundreths = (ctRemaining * 100) / CLOCKS_PER_SEC;
// format the time string...
CString csTime;
csTime.Format( "%02d:%02d:%02d:%02d", hours, minutes, seconds, hundreths);
// compute the size of the time string...
CSize sz = dc.GetTextExtent(csTime);
// place time centered, one-fourth down in the window...
CRect cr;
GetClientRect(cr);
int x = (cr.Width() - sz.cx) >> 1;
int y = (cr.Height() - sz.cy) >> 2;
m_crLastPaint = CRect( x, y, x + sz.cx, y + sz.cy);
// set up the DC to draw the countdown time...
UINT oldAlign = dc.SetTextAlign( TA_TOP | TA_LEFT);
COLORREF oldBkColor = dc.SetBkColor( ::GetSysColor(COLOR_3DFACE));
// use GREEN for a running timer and RED for a stopped timer...
COLORREF oldTextColor = dc.SetTextColor( (m_bTimerRunning) ? RGB(0, 255, 0) : RGB(255, 0, 0));
dc.ExtTextOut( x, y, ETO_OPAQUE, NULL, csTime, NULL);
// restore the DC...
dc.SetTextAlign(oldAlign);
dc.SetBkColor(oldBkColor);
dc.SetTextColor(oldTextColor);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -