Figure 1   Modified Scribble

scribble.h

 ////////////////////////////////////////////////////////////////
// Modified SCRIBBLE Copyright 1996 Microsoft Systems Journal. 
// Portions Copyright (C) 1992-1995 Microsoft Corporation.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// See SCRIBBLE.CPP for Description of program.
//
#include "resource.h"       // main symbols

class CScribbleApp : public CWinApp {
public:
   CScribbleApp();
   ~CScribbleApp();
   virtual BOOL InitInstance();

    // ***PD Override exception handler
   virtual LRESULT ProcessWndProcException(CException* e, const MSG* pMsg);

   //{{AFX_MSG(CScribbleApp)
   afx_msg void OnAppAbout();
    afx_msg void OnMyFileNew();    // ***PD: This replaces OnFileNew to
                                            // handle ID_FILE_NEW
   afx_msg void OnExUser();
   afx_msg void OnExMemory();
   afx_msg void OnExResource();
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};

scribble.cpp

 ////////////////////////////////////////////////////////////////
// Modified SCRIBBLE Copyright 1996 Microsoft Systems Journal. 
// Portions Copyright (C) 1992-1995 Microsoft Corporation
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// This program is based on the SCRIBBLE program that comes with
// Microsoft Visual C++, as part of the MFC tutorial.
//
// This modified SCRIBBLE shows
//
// -how to write an SDI app that starts with an empty frame instead
//  of a new document;
//
// -how to write an exception handler to handle any uncaught exception
//  that occurs while the app is running.
//
// Mods identified with ***PD
//
#include "stdafx.h"
#include "Scribble.h"
#include "MainFrm.h"
#include "ScribDoc.h"
#include "ScribVw.h"
#include <eh.h>         // set_terminate
#include "TraceWin.h"

BEGIN_MESSAGE_MAP(CScribbleApp, CWinApp)
   ON_COMMAND(ID_APP_ABOUT,    OnAppAbout)
   ON_COMMAND(ID_EX_USER,      OnExUser)
   ON_COMMAND(ID_EX_MEMORY,    OnExMemory)
   ON_COMMAND(ID_EX_RESOURCE,  OnExResource)
   ON_COMMAND(ID_FILE_NEW,     OnMyFileNew) // ***PD: my own handler
   ON_COMMAND(ID_FILE_OPEN,    CWinApp::OnFileOpen)
   ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()

CScribbleApp theApp;

typedef void (*TERMINATE_FN)();
TERMINATE_FN old_terminate = NULL;

////////////////
// Custom terminate handler just displays message
//
void my_terminate()
{
   MessageBox(NULL, "Help me, I'm dying.", "Terminate", MB_OK);

    // Call old terminate fn, if any. On Windows, old_terminate is NULL;
   if (old_terminate)
      old_terminate();
}



CScribbleApp::CScribbleApp()
{
   MfxTraceInit();    // initialize TRACEWIN tracing
   old_terminate = set_terminate(my_terminate);
   TRACE("old terminate fn = %p\n", old_terminate);
}

CScribbleApp::~CScribbleApp()
{
   set_terminate(old_terminate);
}

BOOL CScribbleApp::InitInstance()
{
   SetRegistryKey("MSJ");     // Use HKEY_CURRENT_USER\Software\MSJ for INI
   LoadStdProfileSettings();  // Load INI file options (including MRU file)

   // create doc template
   CDocTemplate* pDocTemplate;
   pDocTemplate = new CSingleDocTemplate(
      IDR_MAINFRAME,
      RUNTIME_CLASS(CScribbleDoc),
      RUNTIME_CLASS(CMainFrame),
      RUNTIME_CLASS(CScribbleView));
   AddDocTemplate(pDocTemplate);

// EnableShellOpen();            // Only need for File Manager double-click/DDE
   RegisterShellFileTypes(TRUE); // TRUE for Win 95
   
   // Parse command line for standard shell commands, DDE, file open
   CCommandLineInfo cmdInfo;
   ParseCommandLine(cmdInfo);
   if (!ProcessShellCommand(cmdInfo)) // dispatch command
      return FALSE;
   return TRUE;
}

//////////////////
// ***PD REALLY create new document: initialize the empty doc object.
//
void CScribbleApp::OnMyFileNew()
{
   OnFileNew(); // do normal thing to create new doc
                // (in case this is an open doc, reuse it)

   CFrameWnd* pFrame = (CFrameWnd*)m_pMainWnd;
   ASSERT_KINDOF(CFrameWnd, pFrame);
   CScribbleView* pView = (CScribbleView*)pFrame->GetActiveView();
   ASSERT_KINDOF(CScribbleView, pView);
   pView->GetDocument()->Initialize(); // initialize new doc
}

void CScribbleApp::OnAppAbout()
{
    CDialog(IDD_ABOUTBOX).DoModal();
}

//////////////////
// Commands to throw various kinds of exceptions.
//
void CScribbleApp::OnExUser()     { AfxThrowUserException();     }
void CScribbleApp::OnExMemory()   { AfxThrowMemoryException();   }
void CScribbleApp::OnExResource() { AfxThrowResourceException(); }

//////////////////
// Exception handler of last resort. Display message box describing
// exception, then try to save all files.
//
LRESULT CScribbleApp::ProcessWndProcException(CException* e, const MSG* pMsg)
{
   CString s = "An unhandled error occurred in your program.\n\n";
   CString m;
   m.Format("type\t%s\n", e->GetRuntimeClass()->m_lpszClassName);
   s += m;
   m.Format("HWND\t0x%04x\nmsg\t0x%04x\nwParam\t0x%08x\nlParam\t0x%08x",
      pMsg->hwnd, pMsg->message, pMsg->wParam, pMsg->lParam);
   s += m;
   s += "\n\nSelect:\nAbort \tto terminate the program;\n";
   s += "Retry \tto let MFC handle the error; or\n";
   s += "Ignore \tto do nothing.\n\n";
   s += "After this dialog, you'll have a chance to save modified documents.";
   int nRes = MessageBox(m_pMainWnd->GetSafeHwnd(), s, "Error!", 
      MB_ABORTRETRYIGNORE);

   // Save any modified docs
   SaveAllModified();

   if (nRes==IDABORT)
      THROW_LAST();        // will call terminate()
   else if (nRes==IDRETRY)
      return CWinApp::ProcessWndProcException(e, pMsg); // do MFC Thing

   return 0; // IDIGNORE
}

scribdoc.h

 ////////////////////////////////////////////////////////////////
// Modified SCRIBBLE Copyright 1996 Microsoft Systems Journal. 
// Portions Copyright (C) 1992-1995 Microsoft Corporation.
// NOTE: Only changes from original SCRIBBLE code are shown.
// Full source available on any MSJ bulletin board.

class CScribbleDoc : public CDocument {
protected:
   BOOL            m_bInitialized; // ***PD: whether doc is initialized
public:

.

.

.

    void     InitDocument();

   // ***PD new functions
   BOOL     Initialize();
   BOOL     IsInitialized() { return m_bInitialized; }

.

.

.

 };

scribdoc.cpp

 ////////////////////////////////////////////////////////////////
// Modified SCRIBBLE Copyright 1996 Microsoft Systems Journal. 
// Portions Copyright (C) 1992-1995 Microsoft Corporation.
// NOTE: Only changes from original SCRIBBLE code are shown.
// Full source available on any MSJ bulletin board.
//
#include "stdafx.h"
#include "Scribble.h"
#include "ScribDoc.h"
.
.
.

BOOL CScribbleDoc::OnNewDocument()
{
   if (!CDocument::OnNewDocument())
      return FALSE;
   InitDocument();
   m_bInitialized = FALSE; // ***PD: Not initialized yet
   return TRUE;
}

BOOL CScribbleDoc::OnOpenDocument(LPCTSTR lpszPathName) 
{
   if (!CDocument::OnOpenDocument(lpszPathName))
      return FALSE;
   InitDocument();
   return m_bInitialized = TRUE; // ***PD: an open doc is initialized
}

void CScribbleDoc::DeleteContents() 
{
   while (!m_strokeList.IsEmpty()) {
      delete m_strokeList.RemoveHead();
   }
   CDocument::DeleteContents();
}

//////////////////
// ***PD: NOTE: This is the old function from the tutorial code. It's called
// InitDocument but it's only the raw initialization, not the
// "real" initialization, which requires setting m_bInitialized to TRUE.
//
void CScribbleDoc::InitDocument()
{
   m_bThickPen = FALSE;
   m_nThinWidth = 2;   // default thin pen is 2 pixels wide
   m_nThickWidth = 5;  // default thick pen is 5 pixels wide
   ReplacePen();       // initialize pen according to current width
}

//////////////////
// ***PD: This function REALLY initializes a new document, as opposed to
// OnNewDocument. That is, it performs the initialization required
// to use the document. It's called when the user invokes the File New
// command from the menu. This implementation doesn't actually do anything,
// it just sleeps to simulate some long operation a real app might do.
//
BOOL CScribbleDoc::Initialize()
{
   // This implementation doesn't actually do anything
   // except sleep for three seconds to simulate a lengthy operation
   //
   CWaitCursor wait;   // display wait cursor
   Sleep(3000);
   return m_bInitialized = TRUE;
}

scribvw.h

 ////////////////////////////////////////////////////////////////
// Modified SCRIBBLE Copyright 1996 Microsoft Systems Journal. 
// Portions Copyright (C) 1992-1995 Microsoft Corporation.
// NOTE: Only changes from original SCRIBBLE code are shown.
// Full source available on any MSJ bulletin board.

class CScribbleView : public CView {
protected:
   // ***PD new override
   BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,
      AFX_CMDHANDLERINFO* pHandlerInfo);

   // ***PD overrides
   virtual void OnDraw(CDC* pDC);
   afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
};

scribvw.cpp

 ////////////////////////////////////////////////////////////////
// Modified SCRIBBLE Copyright 1996 Microsoft Systems Journal. 
// Portions Copyright (C) 1992-1995 Microsoft Corporation.
// NOTE: Only changes from original SCRIBBLE code are shown.
// Full source available on any MSJ bulletin board.

#include "stdafx.h"
#include "Scribble.h"
#include "ScribDoc.h"
#include "ScribVw.h"

.
.
.

void CScribbleView::OnDraw(CDC* pDC)
{
   CScribbleDoc* pDoc = GetDocument();
   ASSERT_VALID(pDoc);
   if (pDoc->IsInitialized()) { 
      // The view delegates the drawing of individual strokes to
      // CStroke::DrawStroke().
      CTypedPtrList<CObList,CStroke*>& strokeList = pDoc->m_strokeList;
      POSITION pos = strokeList.GetHeadPosition();
      while (pos != NULL) {
         CStroke* pStroke = strokeList.GetNext(pos);
         pStroke->DrawStroke(pDC);
      }
   } else {
      // ***PD Draw "empty" background if doc not initialized.
      // Note: be sure to use COLOR_3DSHADOW to get the
      // right logical color, in case user has customized it.
      //
      CRect rc;
      GetClientRect(&rc);
      HGDIOBJ hOldBrush = pDC->SelectObject(GetSysColorBrush(COLOR_3DSHADOW));
      pDC->PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), PATCOPY);
      pDC->SelectObject(hOldBrush);
   }
}

//////////////////
// ***PD: Change so drawing is allowed only if doc is initialized.
//
void CScribbleView::OnLButtonDown(UINT, CPoint point) 
{
   CScribbleDoc* pDoc = GetDocument();
   if (pDoc->IsInitialized()) {
      m_pStrokeCur = pDoc->NewStroke();
      // Add first point to the new stroke
      m_pStrokeCur->m_pointArray.Add(point);

      SetCapture();       // Capture the mouse until button up.
      m_ptPrev = point;   // Serves as the MoveTo() anchor point for the
                          // LineTo() the next point, as the user drags the
                          // mouse.
   }
   return;
}

//////////////////
// ***PD: If doc not initialized, disable all doc/view commands by
// returning FALSE here--i.e., do not even route through doc/view msg maps.
// Command routing behaves as if there were no doc nor view. NOTE: this 
// only works if CFrameWnd::m_bAutoMenuEnable = TRUE, the default.
//
BOOL CScribbleView::OnCmdMsg(UINT nID, int nCode, void* pExtra,
   AFX_CMDHANDLERINFO* pHandlerInfo)
{
   CScribbleDoc *pDoc = GetDocument();
   return pDoc->IsInitialized() ?
      CView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo) : FALSE;
}