[ Home | Syllabus |Course Notes]
Let user draw lines of various widths and colors
CPaintApp myApp;
BEGIN_MESSAGE_MAP (CPaintApp, CWinApp)
ON_COMMAND (ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND (ID_FILE_OPEN, CWinApp::OnFileOpen)
ON_COMMAND (ID_APP_ABOUT, OnAppAbout)
END_MESSAGE_MAP ()
BOOL CPaintApp::InitInstance ()
{
SetRegistryKey ("Programming Windows 95 with MFC");
LoadStdProfileSettings ();
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate (
IDR_MAINFRAME,
RUNTIME_CLASS (CPaintDoc),
RUNTIME_CLASS (CMainFrame),
RUNTIME_CLASS (CPaintView)
);
AddDocTemplate (pDocTemplate);
RegisterShellFileTypes (TRUE);
CCommandLineInfo cmdInfo;
ParseCommandLine (cmdInfo);
if (!ProcessShellCommand (cmdInfo))
return FALSE;
m_pMainWnd->DragAcceptFiles ();
return TRUE;
}
void CPaintApp::OnAppAbout ()
{
CAboutDialog dlg;
dlg.DoModal ();
}
class CPaintDoc : public CDocument
{
DECLARE_DYNCREATE (CPaintDoc)
private:
UINT m_nWidth;
UINT m_nColor;
CObArray m_lineArray;
void InitWidthAndColor (); // initializes on Open/New
public:
static const COLORREF m_crColors[8]; // can choose between 8 colors
CPaintDoc ();
virtual BOOL OnNewDocument ();
virtual BOOL OnOpenDocument (LPCTSTR);
virtual void DeleteContents ();
virtual void Serialize (CArchive&);
CLine* AddLine (CPoint, CPoint);
CLine* GetLine (int);
int GetLineCount ();
protected:
afx_msg void OnWidth (UINT);
afx_msg void OnColor (UINT);
afx_msg void OnUpdateWidthUI (CCmdUI*);
afx_msg void OnUpdateColorUI (CCmdUI*);
DECLARE_MESSAGE_MAP ()
};
BEGIN_MESSAGE_MAP (CPaintDoc, CDocument)
ON_COMMAND_RANGE (ID_WIDTH_VTHIN, ID_WIDTH_VTHICK, OnWidth)
ON_COMMAND_RANGE (ID_COLOR_BLACK, ID_COLOR_WHITE, OnColor)
ON_UPDATE_COMMAND_UI_RANGE (ID_WIDTH_VTHIN, ID_WIDTH_VTHICK,
OnUpdateWidthUI)
ON_UPDATE_COMMAND_UI_RANGE (ID_COLOR_BLACK, ID_COLOR_WHITE,
OnUpdateColorUI)
END_MESSAGE_MAP ()
const COLORREF CPaintDoc::m_crColors[8] = {
RGB ( 0, 0, 0), // Black
RGB ( 0, 0, 255), // Blue
RGB ( 0, 255, 0), // Green
RGB ( 0, 255, 255), // Cyan
RGB (255, 0, 0), // Red
RGB (255, 0, 255), // Magenta
RGB (255, 255, 0), // Yellow
RGB (255, 255, 255) // White
};
CPaintDoc::CPaintDoc ()
{
m_lineArray.SetSize (0, 64);
}
BOOL CPaintDoc::OnNewDocument ()
{
if (!CDocument::OnNewDocument ())
return FALSE;
InitWidthAndColor (); // needed because SDI (constructor won't work)
return TRUE;
}
BOOL CPaintDoc::OnOpenDocument (LPCTSTR lpszPathName)
{
if (!CDocument::OnOpenDocument (lpszPathName)) // work done here (dialog box)
return FALSE;
InitWidthAndColor ();
return TRUE;
}
void CPaintDoc::InitWidthAndColor ()
{
m_nColor = ID_COLOR_RED - ID_COLOR_BLACK;
m_nWidth = ID_WIDTH_MEDIUM - ID_WIDTH_VTHIN;
}
void CPaintDoc::DeleteContents ()
{
int nCount = m_lineArray.GetSize ();
if (nCount) {
for (int i=0; i<nCount; i++)
delete m_lineArray[i]; // Array of Pointers - delete heap memory
m_lineArray.RemoveAll ();
}
}
void CPaintDoc::Serialize (CArchive& ar)
{
m_lineArray.Serialize (ar);
}
CLine* CPaintDoc::AddLine (CPoint ptFrom, CPoint ptTo)
{
static UINT nWidths[5] = { 1, 8, 16, 24, 32 };
CLine* pLine;
try {
pLine = new CLine (ptFrom, ptTo, nWidths[m_nWidth],
m_crColors[m_nColor]);
m_lineArray.Add (pLine);
SetModifiedFlag ();
}
catch (CMemoryException* e) {
if (pLine != NULL) {
delete pLine;
pLine = NULL;
}
AfxMessageBox ("Out of memory", MB_ICONSTOP | MB_OK);
e->Delete ();
}
return pLine;
}
CLine* CPaintDoc::GetLine (int nIndex)
{
return (CLine*) m_lineArray[nIndex];
}
int CPaintDoc::GetLineCount ()
{
return m_lineArray.GetSize ();
}
void CPaintDoc::OnWidth (UINT nID)
{
m_nWidth = nID - ID_WIDTH_VTHIN;
}
void CPaintDoc::OnColor (UINT nID)
{
m_nColor = nID - ID_COLOR_BLACK;
}
void CPaintDoc::OnUpdateWidthUI (CCmdUI* pCmdUI)
{
pCmdUI->SetCheck ((pCmdUI->m_nID - ID_WIDTH_VTHIN) == m_nWidth);
}
void CPaintDoc::OnUpdateColorUI (CCmdUI* pCmdUI)
{
pCmdUI->SetCheck ((pCmdUI->m_nID - ID_COLOR_BLACK) == m_nColor);
}
class CPaintView : public CScrollView
{
DECLARE_DYNCREATE (CPaintView)
private:
CPoint m_ptFrom;
CPoint m_ptTo;
CPaintDoc* GetDocument () { return (CPaintDoc*) m_pDocument; }
void InvertLine (CDC*, CPoint, CPoint);
public:
virtual void OnInitialUpdate (); // OVERRIDE
protected:
virtual void OnDraw (CDC*);
afx_msg void OnLButtonDown (UINT, CPoint);
afx_msg void OnMouseMove (UINT, CPoint);
afx_msg void OnLButtonUp (UINT, CPoint);
afx_msg void OnContextMenu (CWnd*, CPoint);
DECLARE_MESSAGE_MAP ()
};
void CPaintView::OnInitialUpdate ()
{
SetScrollSizes (MM_TEXT, CSize (2048, 2048));
CScrollView::OnInitialUpdate ();
}
void CPaintView::OnDraw (CDC* pDC)
{
CPaintDoc* pDoc = GetDocument ();
int nCount = pDoc->GetLineCount ();
if (nCount) {
for (int i=0; i<nCount; i++)
pDoc->GetLine (i)->Draw (pDC);
}
}
void CPaintView::OnLButtonDown (UINT nFlags, CPoint point)
{
CClientDC dc (this);
OnPrepareDC (&dc);
dc.DPtoLP (&point);
m_ptFrom = point;
m_ptTo = point;
SetCapture ();
}
void CPaintView::OnMouseMove (UINT nFlags, CPoint point)
{
if (GetCapture () == this) {
CClientDC dc (this);
OnPrepareDC (&dc);
dc.DPtoLP (&point);
InvertLine (&dc, m_ptFrom, m_ptTo); // "Erases" temporary rubber band line
InvertLine (&dc, m_ptFrom, point); // And Draws a new one
m_ptTo = point;
}
}
void CPaintView::OnLButtonUp (UINT nFlags, CPoint point)
{
if (GetCapture () == this) {
ReleaseCapture ();
CClientDC dc (this);
OnPrepareDC (&dc);
dc.DPtoLP (&point);
InvertLine (&dc, m_ptFrom, m_ptTo); // erase temporary line
CLine* pLine = GetDocument ()->AddLine (m_ptFrom, point); // Add a permanent line to the document
if (pLine != NULL)
pLine->Draw (&dc); // And Draw it (more efficient than calling OnDraw)
}
}
void CPaintView::InvertLine (CDC* pDC, CPoint ptFrom, CPoint ptTo)
{
int nOldMode = pDC->SetROP2 (R2_NOT);
pDC->MoveTo (ptFrom);
pDC->LineTo (ptTo);
pDC->SetROP2 (nOldMode);
}
void CPaintView::OnContextMenu (CWnd* pWnd, CPoint point)
{
CMenu menu;
menu.LoadMenu (IDR_CONTEXTMENU);
CMenu* pContextMenu = menu.GetSubMenu (0);
for (int i=0; i<8; i++)
pContextMenu->ModifyMenu (ID_COLOR_BLACK + i,
MF_BYCOMMAND | MF_OWNERDRAW, ID_COLOR_BLACK + i);
pContextMenu->TrackPopupMenu (TPM_LEFTALIGN | TPM_LEFTBUTTON |
TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd ());
}
class CLine : public CObject
{
DECLARE_SERIAL (CLine)
private:
CPoint m_ptFrom;
CPoint m_ptTo;
UINT m_nWidth;
COLORREF m_crColor;
public:
CLine () {}
CLine (CPoint, CPoint, UINT, COLORREF);
virtual void Serialize (CArchive&);
virtual void Draw (CDC*);
};
IMPLEMENT_SERIAL (CLine, CObject, 1) // 1 is version number
CLine::CLine (CPoint ptFrom, CPoint ptTo, UINT nWidth, COLORREF crColor)
{
m_ptFrom = ptFrom;
m_ptTo = ptTo;
m_nWidth = nWidth;
m_crColor = crColor;
}
void CLine::Serialize (CArchive& ar)
{
CObject::Serialize (ar);
if (ar.IsStoring ())
ar << m_ptFrom << m_ptTo << m_nWidth << (DWORD) m_crColor;
else
ar >> m_ptFrom >> m_ptTo >> m_nWidth >> (DWORD) m_crColor;
}
void CLine::Draw (CDC* pDC)
{
CPen pen (PS_SOLID, m_nWidth, m_crColor);
CPen* pOldPen = pDC->SelectObject (&pen);
pDC->MoveTo (m_ptFrom);
pDC->LineTo (m_ptTo);
pDC->SelectObject (pOldPen);
}