?? multtimer2.cpp
字號:
//
// FILE: MultTimer2.cpp
//
// Copyright (c) 1997 by Aaron Michael Cohen and Mike Woodring
//
/////////////////////////////////////////////////////////////////////////
#include "MultTimer2.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;
// static class variable for tracking child dialog threads...
CMclQueue<CMcl4MfcGUIThread *> CMultTimerDlg::m_cqChildThreads;
// CMultTimerApp application class member functions...
BOOL CMultTimerApp::InitInstance(void) {
CMultTimerDlg *pDlg = new CMultTimerDlg();
// this is also the application main window...
m_pMainWnd = pDlg;
// inform the user that this is the primary thread...
CString csTitle;
pDlg->GetWindowText(csTitle);
csTitle += " (primary thread)";
pDlg->SetWindowText(csTitle);
return TRUE;
}
// CMultTimerThread thread class member functions...
CMultTimerThread::InitInstance(void) {
// create a dialog that will run on a secondary thread...
CMultTimerDlg *pDlg = new CMultTimerDlg();
// set the dialog as the thread's main window,
// this will allow the framework to terminate the
// thread when the dialog box window is destroyed...
SetMainWnd(pDlg);
// inform the user that this is a child thread...
CString csTitle;
pDlg->GetWindowText(csTitle);
csTitle += " (child thread)";
pDlg->SetWindowText(csTitle);
return TRUE;
}
BOOL CMultTimerThread::PreTranslateMessage( MSG *pMsg) {
if (pMsg->message == WM_SHUTDOWN_THREAD) {
// handle this message...
OnShutdownThread( pMsg->wParam, pMsg->lParam);
return 1;
}
else {
// continue normal message processing...
return 0;
}
}
afx_msg void CMultTimerThread::OnShutdownThread( UINT wParam, LONG lParam) {
// DestroyWindow is supposed to be called by the thread that
// created the window...
// The Primary thread tells the child threads to destroy their
// windows and themselves by sending the WM_SHUTDOWN_THREAD message
// which the child thread uses to call DestroyWindow in the correct
// thread context...
GetMainWnd()->DestroyWindow();
}
// 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) {
// loop through the CMcl4MfcGUIThread pointers in our list
// and shutdown each of the child dialogs...
CMcl4MfcGUIThread *pThread;
while (m_cqChildThreads.Get( pThread, 0)) {
// Use the CWinThread object to instruct the thread to terminate...
// The thread may have previously terminated, in which case this
// will do nothing since the child thread's message pump
// has already terminated...
pThread->PostThreadMessage( WM_SHUTDOWN_THREAD, 0, 0);
///////////////////////////////////////////////////////////
// AN ASIDE:
// Sending a WM_CLOSE message to a MFC dialog box causes
// the OnCancel member function to be called, which is not
// what we want here.
// The proper way to close a modeless dialog such as this
// is to have the dialog thread call DestroyWindow, which
// we can do by sending a WM_SYSCOMMAND message with
// the SC_CLOSE parameter...
///////////////////////////////////////////////////////////
// wait for the thread to exit...
pThread->Wait(INFINITE);
// delete the CMcl4MfcGUIThread object...
delete pThread;
}
// destroy the modeless dialog window,
// since this is the main window, the application
// will exit...
DestroyWindow();
}
else {
// destroy the modeless dialog window...
// this also terminates the thread since
// the dialog was set to be the m_pMainWnd of the thread...
DestroyWindow();
}
}
else {
CDialog::OnSysCommand( nID, lParam);
}
}
void CMultTimerDlg::OnCancel() {
// create a new dialog box running on it's own thread...
CMcl4MfcGUIThread *pThread = new CMultTimerThread();
// add the CMcl4MfcGUIThread pointer to our list of threads...
m_cqChildThreads.Put(pThread);
}
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, show all zero's
// if the timer is not running...
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 + -