[ 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); }