// MainFrm.cpp : implementation of the CMainFrame class
//
#include "stdafx.h"
#include "SDIMultiApp1.h"
#include "MainFrm.h"
#include "DlgOne.h"
#include "DlgTwo.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CMainFrame message map
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_CREATE()
ON_WM_CLOSE()
ON_COMMAND (ID_VIEW_PRIMARYVIEW, &CMainFrame::OnViewPrimaryview )
ON_UPDATE_COMMAND_UI (ID_VIEW_PRIMARYVIEW, &CMainFrame::OnUpdateViewPrimaryview )
ON_COMMAND (ID_VIEW_SECONDARYVIEW, &CMainFrame::OnViewSecondaryview )
ON_UPDATE_COMMAND_UI (ID_VIEW_SECONDARYVIEW, &CMainFrame::OnUpdateViewSecondaryview )
ON_MESSAGE(UDM_TIMER_LONG, OnTimerLong )
ON_MESSAGE(UDM_TIMER_SHORT, OnTimerShort )
ON_MESSAGE(UDM_TIMER_ELAPSED, OnTimerElapsed )
ON_MESSAGE(UDM_TIMER_DATETIME, OnTimerDateTime )
ON_MESSAGE(UDM_LONG_TASK_DONE, OnLongTaskDone )
ON_MESSAGE(UDM_SHORT_TASK_DONE, OnShortTaskDone )
ON_COMMAND(ID_SAMPLESTUFF_DIALOGONE, &CMainFrame::OnSamplestuffDialogone)
ON_COMMAND(ID_SAMPLESTUFF_DIALOGTWO, &CMainFrame::OnSamplestuffDialogtwo)
END_MESSAGE_MAP()
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CMainFrame status bar panes
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
static UINT indicators[] =
{
ID_SEPARATOR, // status line indicator
/*
ID_INDICATOR_CAPS,
ID_INDICATOR_NUM,
ID_INDICATOR_SCRL,
*/
};
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CMainFrame construction/destruction
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
CMainFrame::CMainFrame()
{
m_pLongStatus = NULL;
m_pShortStatus = NULL;
m_pSysTimeStatus = NULL;
m_pTimersThread = NULL;
m_pThreadActionLong = NULL;
m_pThreadActionShort = NULL;
m_pLongRunning = NULL;
m_pShortRunning = NULL;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
CMainFrame::~CMainFrame()
{
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CMainFrame overrides
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
if (!CFrameWnd::PreCreateWindow(cs))
{
return FALSE;
}
return TRUE;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP
| CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
if (!m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(indicators,
sizeof(indicators)/sizeof(UINT)))
{
TRACE0("Failed to create status bar\n");
return -1; // fail to create
}
// The default height of the statusbar chops off descenders in lowercase
// letters like "y", "g", and "p" in our sunken statusbar panes. Because
// of this, we need to set our statusbar height to be just a little taller
// than default. To set the statusbar height, we need to send the
// SB_SETMINHEIGHT message with the desired height being passed as a
// WPARAM. Finally, we need to call UpdateWindow to make sure the
// statusbar is resized.
m_wndStatusBar.SendMessage(SB_SETMINHEIGHT, 20, 0);
m_wndStatusBar.UpdateWindow();
// now, let's add our new statusbar panes
AddStatusPanes();
// TODO: Delete these three lines if you don't want the toolbar to be dockable
m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
EnableDocking(CBRS_ALIGN_ANY);
DockControlBar(&m_wndToolBar);
return 0;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
CRect cr;
GetWindowRect(&cr);
// we need to subtract a reasonable value from the returned screen height
// so that when we actually create the view, it won't cover up part of the
// screen with it's soon-to-be-resized rectangle.
int nHeight = ::GetSystemMetrics(SM_CYSCREEN) - 100;
// our splitter window will be static
if (!m_mainSplitter.CreateStatic(this, 2, 1))
{
AfxMessageBox("Error setting up splitter window", MB_ICONERROR);
return FALSE;
}
// add our replacable views - the view IDs are defined in constants.h
m_mainSplitter.AddSwitchableView(RUNTIME_CLASS(CPrimaryView ), pContext, CRect(0, 1, cr.Width(), nHeight), IDC_PANE0_PRIMARY_VIEW );
m_mainSplitter.AddSwitchableView(RUNTIME_CLASS(CSecondaryView), pContext, CRect(0, 1, cr.Width(), nHeight), IDC_PANE0_SECONDARY_VIEW);
// create views in the splitter
// we need a blank view to start off in the upper pane as the "old view
// because part of the initialization of the app is changing to the proper
// view. We'll use the view created by the app wizard.
m_mainSplitter.CreateView(0, 0, RUNTIME_CLASS(CSDIMultiApp1View), CSize(cr.Width(), nHeight), pContext);
// and for the lower pane, we'll use our CInfoView class.
m_mainSplitter.CreateView(1, 0, RUNTIME_CLASS(CInfoView), CSize(cr.Width(), 0 ), pContext);
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CMainFrame diagnostics
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#ifdef _DEBUG
void CMainFrame::AssertValid() const
{
CFrameWnd::AssertValid();
}
void CMainFrame::Dump(CDumpContext& dc) const
{
CFrameWnd::Dump(dc);
}
#endif //_DEBUG
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CMainFrame programmer-added functions
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
// Initialize the splitter window with the desired initial views.
//------------------------------------------------------------------------------
bool CMainFrame::PrepareViews()
{
bool bResult = m_mainSplitter.SwitchView(IDC_PANE0_PRIMARY_VIEW, 0, 0);
//m_mainSplitter.RecalcLayout();
if (bResult)
{
CPrimaryView* pView1 = GetPrimaryView();
if (pView1)
{
// do view intizialtion here
}
else
{
bResult = false;
}
if (bResult)
{
CInfoView* pView2 = GetInfoView();
if (pView2)
{
// initizliae view here
}
else
{
bResult = false;
}
}
}
return bResult;
}
////////////////////////////////////////////////////////////////////////////////
// Code for changing views //
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
// Gets the pointer to the primary view. Since we're using dynamic_cast, this
// function will return NULL if the view in the upper pain isn't the one we're
// expecting.
//------------------------------------------------------------------------------
CPrimaryView* CMainFrame::GetPrimaryView()
{
return (dynamic_cast<CPrimaryView*>(m_mainSplitter.GetPane(0,0)));
}
//------------------------------------------------------------------------------
// Gets the pointer to the scondary view. Since we're using dynamic_cast,
// this function will return NULL if the view in the upper pain isn't the one
// we're expecting.
//------------------------------------------------------------------------------
CSecondaryView* CMainFrame::GetSecondaryView()
{
return (dynamic_cast<CSecondaryView*>(m_mainSplitter.GetPane(0,0)));
}
//------------------------------------------------------------------------------
// Gets the pointer to the info view. Since we're using dynamic_cast, this
// function will return NULL if the view in the upper pain isn't the one we're
// expecting. In the case of the info view, we should never get a null
// pointer since that's the only view currently residing in the bottom pane.
//------------------------------------------------------------------------------
CInfoView* CMainFrame::GetInfoView()
{
return (dynamic_cast<CInfoView*>(m_mainSplitter.GetPane(1,0)));
}
////////////////////////////////////////////////////////////////////////////////
// Code for status bar panes //
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
// Add the specified pane.
//------------------------------------------------------------------------------
BOOL CMainFrame::AddOnePane(CStatusStatic* pPane, UINT nPaneID, CString sDefaultText, int nPaneWidth)
{
ASSERT(pPane);
ASSERT(nPaneID > 0);
DWORD dwFlags = WS_CHILD|WS_VISIBLE|SS_CENTER|SS_CENTERIMAGE|SS_NOPREFIX;
CRect ctrlRect(0,0,0,0);
BOOL bReturn = FALSE;
int nIndex = -1;
//int nPaneWidth = 150;
if (!pPane->Create(sDefaultText, dwFlags, ctrlRect, &m_wndStatusBar, 0))
{
return FALSE;
}
// We MUST set the font for each pane. If we don't, the pane will be set to
// 12-point MS Shell Dlg. This should actually be set within the
// ExtStatusBarControl class, but I wanted to illustrate the need to do
// this.
pPane->SetFont(m_wndStatusBar.GetFont());
bReturn = m_wndStatusBar.AddPane(nPaneID, 1);
if (!bReturn)
{
AfxMessageBox(_T("Pane index out of range\nor pane with same ID already exists in the status bar"), MB_ICONERROR);
return FALSE;
}
nIndex = m_wndStatusBar.CommandToIndex(nPaneID);
if (nIndex == -1)
{
return FALSE;
}
m_wndStatusBar.SetPaneWidth(nIndex, nPaneWidth);
m_wndStatusBar.AddPaneControl(pPane, nPaneID, true);
return TRUE;
}
//------------------------------------------------------------------------------
// Adds the status panes to our custom status bar.
//------------------------------------------------------------------------------
BOOL CMainFrame::AddStatusPanes()
{
m_pSysTimeStatus = new CStatusStatic;
if (m_pSysTimeStatus)
{
if (!AddOnePane(m_pSysTimeStatus, ID_STATUSPANE_TIME, " 00/00/0000 00:00 ", 110))
{
if (m_pSysTimeStatus)
{
delete m_pSysTimeStatus;
m_pSysTimeStatus = NULL;
}
return FALSE;
}
}
else
{
return FALSE;
}
m_pLongStatus = new CStatusStatic;
if (m_pLongStatus)
{
if (!AddOnePane(m_pLongStatus, ID_STATUSPANE_LONGUPDATE, " Nextg Long Update - 00:00 ", 150))
{
delete m_pLongStatus;
m_pLongStatus = NULL;
return FALSE;
}
}
else
{
return FALSE;
}
m_pShortStatus = new CStatusStatic;
if (m_pShortStatus)
{
if (!AddOnePane(m_pShortStatus, ID_STATUSPANE_SHORTUPDATE, " Next Short Update - 00:00 ", 150))
{
delete m_pShortStatus;
m_pShortStatus = NULL;
return FALSE;
}
}
else
{
return FALSE;
}
m_pLongRunning = new CStatusStatic;
if (m_pLongRunning)
{
if (!AddOnePane(m_pLongRunning, ID_STATUSPANE_LONGRUNNING, "", 90))
{
delete m_pLongRunning;
m_pLongRunning = NULL;
return FALSE;
}
}
else
{
return FALSE;
}
m_pShortRunning = new CStatusStatic;
if (m_pShortRunning)
{
if (!AddOnePane(m_pShortRunning, ID_STATUSPANE_SHORTRUNNING, "", 90))
{
delete m_pShortRunning;
m_pShortRunning = NULL;
return FALSE;
}
}
else
{
return FALSE;
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////
// Code for threads //
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
// Create the timer thread. The timer thread can support multiple timers. At
// it's most simple, it can count down an interval of milliseconds and fire a
// message back to the specified window. In our case, the timers are attached
// to our custom statusbar panes. The reason is because the timer updates a
// couple of the status bar panes every second (countdown timers). One of the
// timers doesn't fir a message at all (but it could), and the other sends a
// message to update an elapsed time in the grid.
//------------------------------------------------------------------------------
BOOL CMainFrame::CreateTimerThread()
{
// In my app, the settings are held in a XML file. For purposes of example,
// we'll just hard code the intervals to expedite the article.
int nLongInterval = 300000; // 5 minutes
int nShortInterval = 15000;
bool bAllowLongUpdate = true;
bool bAllowShortUpdate = true;
if (!bAllowLongUpdate)
{
nLongInterval = 0;
}
if (!bAllowShortUpdate)
{
nShortInterval = 0;
}
// create the new thread - all timer messages will be sent to this object,
// and if necessary, reflected to the current view(s).
m_pTimersThread = new CTimersThread((void*)this, 0);
if (m_pTimersThread)
{
m_pTimersThread->SetInterval(1000);
// having these two timers seems redundant since they both fire at
// the same time - see about combining them
m_pTimersThread->SetTimer(TIMER_DATETIME, 0, 1000, "Current Date/Time", " %m/%d/%Y %H:%M ", m_pSysTimeStatus );
m_pTimersThread->SetTimer(TIMER_ELAPSED, UDM_TIMER_ELAPSED, 1000, "Patient's Elapsed Time", "", NULL );
m_pTimersThread->SetTimer(TIMER_LONG, UDM_TIMER_LONG, nLongInterval, "Long Update", " Next Long Update - %02d:%02d ", m_pLongStatus );
m_pTimersThread->SetTimer(TIMER_SHORT, UDM_TIMER_SHORT, nShortInterval, "Short Update", " Next Short Update - %02d:%02d ", m_pShortStatus );
// sanity checks - if we don't want automatic updates, or if something went
// wrong when ewe retrieved the intervals, the timers need to be turned off
// to avoid unexpected/undesireable events.
if (!bAllowLongUpdate || nLongInterval == 0)
{
m_pTimersThread->EnableTimer(TIMER_LONG, false);
}
if (!bAllowShortUpdate || nShortInterval == 0)
{
m_pTimersThread->EnableTimer(TIMER_SHORT, false);
}
}
return (m_pTimersThread != NULL);
}
//------------------------------------------------------------------------------
// Starts the timer thread. All timers in the thread start at the same time.
//------------------------------------------------------------------------------
void CMainFrame::StartTimerThread()
{
if (m_pTimersThread)
{
m_pTimersThread->Start();
}
}
//------------------------------------------------------------------------------
// Stop the timer thread.
//------------------------------------------------------------------------------
void CMainFrame::StopTimerThread()
{
if (m_pTimersThread)
{
m_pTimersThread->Kill();
}
}
//------------------------------------------------------------------------------
// Creates our action threads that do something important when signaled to do
// so. It would be a simple matter to add more threads if necessary. I guess
// you could think of this as a rudimentary thread pool. The threads are started
// in response to messages received by this window from the timer thread.
//------------------------------------------------------------------------------
BOOL CMainFrame::CreateActionThreads()
{
// Create them, but don't start them. This lets us start them over and over
// again without having to deal with the overhead of creating/deleting
// threads that are used repeatedly.
m_pThreadActionLong = new CThreadActionLong ((void*)this, UDM_LONG_TASK_DONE);
m_pThreadActionShort = new CThreadActionShort((void*)this, UDM_SHORT_TASK_DONE);
return (m_pThreadActionLong && m_pThreadActionShort);
}
//------------------------------------------------------------------------------
// Pauses the timers thread
//------------------------------------------------------------------------------
void CMainFrame::PauseTimers()
{
// make sure we have a valid pointer
if (m_pTimersThread)
{
m_pTimersThread->Pause();
}
}
//------------------------------------------------------------------------------
// Un-pauses the timers thread
//------------------------------------------------------------------------------
void CMainFrame::ContinueTimers()
{
// make sure we have a valid pointer
if (m_pTimersThread)
{
m_pTimersThread->Continue();
}
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CMainFrame programmer-defined message handlers
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
// This function handles the message sent from the timer thread for the long
// timer.
//------------------------------------------------------------------------------
LRESULT CMainFrame::OnTimerLong(WPARAM wParam, LPARAM lParam)
{
wParam;
lParam;
if (m_pTimersThread)
{
EDTIMER* pTimer = m_pTimersThread->GetTimerByID(TIMER_LONG);
if (pTimer)
{
CStatusStatic* pPane = pTimer->pStatusPane;
if (pPane)
{
pPane->SetWindowText(" Next Long Update - updating ");
}
}
}
// Make sure we have a thread to run, and make sure its not already running.
// If it is running, we just skip over so we don't fall behind in
// handling further activation messages from the timer thread.
if (m_pThreadActionLong && m_pThreadActionLong->GetActivityStatus() != CThread::THREAD_RUNNING)
{
m_pLongRunning->SetWindowText(" Long Running ");
m_pThreadActionLong->Start();
}
return 1L;
}
//------------------------------------------------------------------------------
// This function handles the message sent from the timer thread for the short
// timer.
//------------------------------------------------------------------------------
LRESULT CMainFrame::OnTimerShort(WPARAM wParam, LPARAM lParam)
{
wParam;
lParam;
if (m_pTimersThread)
{
EDTIMER* pTimer = m_pTimersThread->GetTimerByID(TIMER_SHORT);
if (pTimer)
{
CStatusStatic* pPane = pTimer->pStatusPane;
if (pPane)
{
pPane->SetWindowText(" Next Short Update - updating ");
}
}
}
// Make sure we have a thread to run, and make sure its not already running.
// If it is running, we just skip over so we don't fall behind in
// handling further activation messages from the timer thread.
if (m_pThreadActionShort && m_pThreadActionShort->GetActivityStatus() != CThread::THREAD_RUNNING)
{
m_pShortRunning->SetWindowText(" Short Running ");
m_pThreadActionShort->Start();
}
return 1L;
}
//------------------------------------------------------------------------------
// This function handles the message sent from the timer thread for the elapsed
// timer.
//------------------------------------------------------------------------------
LRESULT CMainFrame::OnTimerElapsed(WPARAM wParam, LPARAM lParam)
{
wParam;
lParam;
return 1L;
}
//------------------------------------------------------------------------------
// This function handles the message sent from the timer thread for the
// date/time timer. For now, no message is sent because the thread itself
// updates the date/time statusbar pane, and nothing else needs to be done.
//------------------------------------------------------------------------------
LRESULT CMainFrame::OnTimerDateTime(WPARAM wParam, LPARAM lParam)
{
wParam;
lParam;
return 1L;
}
//------------------------------------------------------------------------------
// This function handles the message sent by the long action thread when that
// thread has completed it's task.
//------------------------------------------------------------------------------
LRESULT CMainFrame::OnLongTaskDone(WPARAM wParam, LPARAM lParam)
{
wParam;
lParam;
m_pLongRunning->SetWindowText("");
return 1L;
}
//------------------------------------------------------------------------------
// This function handles the message sent by the short action thread when that
// thread has completed it's task.
//------------------------------------------------------------------------------
LRESULT CMainFrame::OnShortTaskDone(WPARAM wParam, LPARAM lParam)
{
wParam;
lParam;
m_pShortRunning->SetWindowText("");
return 1L;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// CMainFrame message handlers
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
// Switch to the grid view in the upper splitter pane
//------------------------------------------------------------------------------
void CMainFrame::OnViewPrimaryview()
{
m_mainSplitter.SwitchView(IDC_PANE0_PRIMARY_VIEW, 0, 0);
}
//------------------------------------------------------------------------------
// If we're not already looking at the grid view, enable the menu item that
// allows us to select it.
//------------------------------------------------------------------------------
void CMainFrame::OnUpdateViewPrimaryview(CCmdUI *pCmdUI)
{
pCmdUI->Enable(!m_mainSplitter.GetIsActiveView(IDC_PANE0_PRIMARY_VIEW, 0, 0));
}
//------------------------------------------------------------------------------
// Switch to the gdi view in the upper splitter pane
//------------------------------------------------------------------------------
void CMainFrame::OnViewSecondaryview()
{
m_mainSplitter.SwitchView(IDC_PANE0_SECONDARY_VIEW, 0, 0);
}
//------------------------------------------------------------------------------
// If we're not already looking at the GDI view, enable the menu item that
// allows us to select it.
//------------------------------------------------------------------------------
void CMainFrame::OnUpdateViewSecondaryview(CCmdUI *pCmdUI)
{
pCmdUI->Enable(!m_mainSplitter.GetIsActiveView(IDC_PANE0_SECONDARY_VIEW, 0, 0));
}
//------------------------------------------------------------------------------
// We need to kill and delete the timer thread BEFORE the destructor is called
// in CMainFrame because the thread has pointers to the custom statusbar panes.
// Since we're killing the timer thread here, we may as well wait for the action
// threads if they're running.
//------------------------------------------------------------------------------
void CMainFrame::OnClose()
{
if (m_pThreadActionLong)
{
m_pThreadActionLong->Kill();
// we may have to wait for the thread to stop, so just sit and spin here
while (m_pThreadActionLong->IsAlive()) {}
delete m_pThreadActionLong;
}
if (m_pThreadActionShort)
{
m_pThreadActionShort->Kill();
// we may have to wait for the thread to stop, so just sit and spin here
while (m_pThreadActionShort->IsAlive()) {}
delete m_pThreadActionShort;
}
if (m_pTimersThread)
{
m_pTimersThread->Kill();
// we may have to wait for the thread to stop, so just sit and spin here
while (m_pTimersThread->IsAlive()) {}
m_pTimersThread->Cleanup();
delete m_pTimersThread;
}
m_pTimersThread = NULL;
CFrameWnd::OnClose();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void CMainFrame::OnSamplestuffDialogone()
{
PauseTimers();
CDlgOne dlg;
dlg.DoModal();
ContinueTimers();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void CMainFrame::OnSamplestuffDialogtwo()
{
CDlgTwo dlg;
dlg.DoModal();
}