Figure 4   Simplified DOCOBJ.H

 //----------------------------------------------------------------------------
// OLE Document Object interfaces
// Copyright 1995 - 1996 Microsoft Corporation. All Rights Reserved.
//----------------------------------------------------------------------------

interface IOleDocument;
interface IOleDocumentSite;
interface IOleDocumentView;
interface IEnumOleDocumentViews;
interface IContinueCallback;
interface IPrint;
interface IOleCommandTarget;

interface IOleDocument : IUnknown
{
    typedef [unique] IOleDocument *LPOLEDOCUMENT;
    
    typedef enum {
        DOCMISC_CANCREATEMULTIPLEVIEWS    = 1,
        DOCMISC_SUPPORTCOMPLEXRECTANGLES  = 2,
        DOCMISC_CANTOPENEDIT              = 4, // fails the 
                                               // IOleDocumentView::Open method
        DOCMISC_NOFILESUPPORT             = 8, // does not support read/
                                               // writing to a file    
    } DOCMISC;
     

    HRESULT CreateView(
        [in, unique] IOleInPlaceSite *pIPSite,
        [in, unique] IStream *pstm,
        [in] DWORD dwReserved,
        [out] IOleDocumentView **ppView);

    HRESULT    GetDocMiscStatus(
        [out] DWORD *pdwStatus);

    HRESULT EnumViews(
        [out] IEnumOleDocumentViews **ppEnum,
        [out] IOleDocumentView **ppView);
}

//---------------------------------------------------------------------------

interface IOleDocumentSite : IUnknown
{
    typedef [unique] IOleDocumentSite *LPOLEDOCUMENTSITE;

    HRESULT ActivateMe(
            [in] IOleDocumentView *pViewToActivate);
}

//----------------------------------------------------------------------------

interface IOleDocumentView : IUnknown
{
    typedef [unique] IOleDocumentView *LPOLEDOCUMENTVIEW;

    HRESULT SetInPlaceSite(
           [in, unique] IOleInPlaceSite *pIPSite);

    HRESULT GetInPlaceSite(
           [out] IOleInPlaceSite **ppIPSite);

    HRESULT GetDocument(
        [out] IUnknown **ppunk);

    [input_sync] 
    HRESULT SetRect(
        [in] LPRECT prcView);

    HRESULT GetRect(
        [out] LPRECT prcView);

    [input_sync] 
    HRESULT SetRectComplex(
        [in, unique] LPRECT prcView,
        [in, unique] LPRECT prcHScroll,
        [in, unique] LPRECT prcVScroll,
        [in, unique] LPRECT prcSizeBox);

    HRESULT Show(
        [in] BOOL fShow);

    HRESULT UIActivate(
        [in] BOOL fUIActivate);

    HRESULT Open(void);

    HRESULT CloseView(DWORD dwReserved);

    HRESULT SaveViewState(
        [in] LPSTREAM pstm);

    HRESULT ApplyViewState(
        [in] LPSTREAM pstm);

    HRESULT Clone(
           [in] IOleInPlaceSite *pIPSiteNew,
        [out] IOleDocumentView **ppViewNew);
}

//----------------------------------------------------------------------------

interface IEnumOleDocumentViews : IUnknown
{ 

    typedef [unique] IEnumOleDocumentViews *LPENUMOLEDOCUMENTVIEWS;

    [local]
    HRESULT __stdcall Next(
        [in] ULONG cViews,
        [out] IOleDocumentView **rgpView,
        [out] ULONG *pcFetched);

    [call_as(Next)]
    HRESULT __stdcall RemoteNext(
        [in] ULONG cViews,
        [out, size_is(cViews), length_is(*pcFetched)]
        IOleDocumentView **rgpView,
        [out] ULONG *pcFetched);
                  
    HRESULT Skip(
           [in] ULONG cViews);
        
    HRESULT Reset();

    HRESULT Clone(
           [out] IEnumOleDocumentViews **ppEnum);
}

//----------------------------------------------------------------------------

interface IContinueCallback : IUnknown
{
    typedef [unique] IContinueCallback *LPCONTINUECALLBACK;

    HRESULT FContinue();

    HRESULT FContinuePrinting(
        [in] LONG nCntPrinted,
        [in] LONG nCurPage,
        [in, unique] wchar_t * pwszPrintStatus);
}

//----------------------------------------------------------------------------

interface IPrint : IUnknown
{
    typedef [unique] IPrint *LPPRINT;

    typedef enum
    {
        PRINTFLAG_MAYBOTHERUSER           = 1,
        PRINTFLAG_PROMPTUSER              = 2,
        PRINTFLAG_USERMAYCHANGEPRINTER    = 4,
        PRINTFLAG_RECOMPOSETODEVICE       = 8,
        PRINTFLAG_DONTACTUALLYPRINT       = 16,
        PRINTFLAG_FORCEPROPERTIES         = 32,
        PRINTFLAG_PRINTTOFILE             = 64

    } PRINTFLAG;

    typedef struct  tagPAGERANGE
    {
        LONG nFromPage;
        LONG nToPage;
    } PAGERANGE;

    typedef struct  tagPAGESET
    {
        ULONG    cbStruct;
        BOOL    fOddPages;
        BOOL    fEvenPages;
        ULONG    cPageRange;
        [size_is(cPageRange)]
        PAGERANGE rgPages[];
    } PAGESET;

    cpp_quote("#define PAGESET_TOLASTPAGE    ((WORD)(-1L))")

    HRESULT SetInitialPageNum(
        [in] LONG nFirstPage);

    HRESULT    GetPageInfo(
        [out] LONG *pnFirstPage,
        [out] LONG *pcPages);

    [local]
    HRESULT __stdcall Print(
        [in] DWORD grfFlags,
        [in, out] DVTARGETDEVICE **pptd,
        [in, out] PAGESET ** ppPageSet,
        [in, out, unique] STGMEDIUM * pstgmOptions, 
        [in] IContinueCallback *pcallback,
        [in]  LONG nFirstPage,
        [out] LONG *pcPagesPrinted,
        [out] LONG *pnLastPage);

    [call_as(Print)]
    HRESULT __stdcall RemotePrint(
        [in] DWORD grfFlags,
        [in, out] DVTARGETDEVICE **pptd,
        [in, out] PAGESET ** pppageset,
        [in, out, unique] RemSTGMEDIUM * pstgmOptions, 
        [in] IContinueCallback * pcallback,
        [in]  LONG nFirstPage,
        [out] LONG * pcPagesPrinted,
        [out] LONG * pnLastPage);
}

//----------------------------------------------------------------------------

interface IOleCommandTarget : IUnknown
{
    typedef [unique] IOleCommandTarget *LPOLECOMMANDTARGET;

    typedef enum 
    {
        OLECMDF_SUPPORTED      = 0x00000001,
        OLECMDF_ENABLED        = 0x00000002,
        OLECMDF_LATCHED        = 0x00000004,
        OLECMDF_NINCHED        = 0x00000008,
    } OLECMDF;

    
    typedef struct _tagOLECMD {
        ULONG    cmdID;
        DWORD    cmdf;
    } OLECMD;

    typedef struct _tagOLECMDTEXT{
        DWORD cmdtextf;    
        ULONG cwActual;
        ULONG cwBuf;    /* size in wide chars of the buffer for text */
        [size_is(cwBuf)]
        wchar_t rgwz[]; /* Array into which callee writes the text */
    } OLECMDTEXT;

    typedef enum 
    {
        OLECMDTEXTF_NONE                = 0,
        OLECMDTEXTF_NAME                = 1,
        OLECMDTEXTF_STATUS              = 2,
    } OLECMDTEXTF;

    typedef enum 
    {
        OLECMDEXECOPT_DODEFAULT         = 0,
        OLECMDEXECOPT_PROMPTUSER        = 1,
        OLECMDEXECOPT_DONTPROMPTUSER    = 2,
        OLECMDEXECOPT_SHOWHELP          = 3
    } OLECMDEXECOPT;

    typedef enum {
        OLECMDID_OPEN                   = 1,
        OLECMDID_NEW                    = 2,
        OLECMDID_SAVE                   = 3,
        OLECMDID_SAVEAS                 = 4,
        OLECMDID_SAVECOPYAS             = 5,
        OLECMDID_PRINT                  = 6,
        OLECMDID_PRINTPREVIEW           = 7,
        OLECMDID_PAGESETUP              = 8,
        OLECMDID_SPELL                  = 9,
        OLECMDID_PROPERTIES             = 10,
        OLECMDID_CUT                    = 11,
        OLECMDID_COPY                   = 12,
        OLECMDID_PASTE                  = 13,
        OLECMDID_PASTESPECIAL           = 14,
        OLECMDID_UNDO                   = 15,
        OLECMDID_REDO                   = 16,
        OLECMDID_SELECTALL              = 17,
        OLECMDID_CLEARSELECTION         = 18,
        OLECMDID_ZOOM                   = 19,
        OLECMDID_GETZOOMRANGE           = 20
    } OLECMDID;

  //----------------------------------------------------------------------------
  // error codes

  #define OLECMDERR_E_FIRST           (OLE_E_LAST+1)
  #define OLECMDERR_E_NOTSUPPORTED    (OLECMDERR_E_FIRST)
  #define OLECMDERR_E_DISABLED        (OLECMDERR_E_FIRST+1)
  #define OLECMDERR_E_NOHELP          (OLECMDERR_E_FIRST+2)
  #define OLECMDERR_E_CANCELED        (OLECMDERR_E_FIRST+3)
  #define OLECMDERR_E_UNKNOWNGROUP    (OLECMDERR_E_FIRST+4)

    [input_sync] 
    HRESULT QueryStatus(
        [in, unique] const GUID *pguidCmdGroup,
        [in] ULONG cCmds,
        [size_is(cCmds)]
        [in, out] OLECMD prgCmds[],
        [in, out, unique] OLECMDTEXT *pCmdText);


    HRESULT Exec(
        [in, unique] const GUID *pguidCmdGroup,
        [in] DWORD nCmdID,
        [in] DWORD nCmdexecopt,
        [in, unique] VARIANTARG *pvaIn,
        [in, out, unique] VARIANTARG *pvaOut);
}

Figure 5   IOleDocumentView Functions

SetInPlaceSite

Associates a view site object (provided by the container) with this view.

GetInPlaceSite

Returns a pointer to this view's associated site.

GetDocument

Returns a pointer to this view's associated document.

SetRect

Sets the viewpoint coordinates for this view.

GetRect

Retrieves the viewpoint coordinates for this view.

SetRectComplex

Lets the container specify not only the simple view rectangle but also the area occupied by the view's scrollbars and sizing box. (Complex rectangles don't have to be supported by a view; the container can find this out through the IOleDocument::GetMiscStatus function, which will include the DOCMISC_SUPPORTCOMPLEXRECTANGLES flag on return if this support exists.)

Show

Indicates whether to show or hide this view.

UIActivate

Indicates whether to UI activate or deactivate this view.

Open

Asks to display the view in a separate window. (This ability can be turned off with the DOCMISC_CANTOPENEDIT MiscStatus bit.)

CloseView

Asks this view to shut itself down.

SaveViewState

Writes the view's state information to an IStream.

ApplyViewState

Asks a view to restore its state to a previously saved setting in an IStream.

Clone

Asks this view to duplicate itself with the same view context, but in a different viewport.

Figure 6   OLECMD Flags

Flag

Description

OLECMDF_SUPPORTED

The command is supported by this object.

OLECMDF_ENABLED

The command is available and enabled.

OLECMDF_LATCHED

The command is an on-off toggle and is currently on.

OLECMDF_NINCHED

The command is an on-off toggle but the state cannot be determined because the attribute of this command is found in both on and off states in the relevant selection. This state corresponds to an "indeterminate" state of a 3-state checkbox, for example.

Figure 7   OLECMDEXECOPT Options

Flag

Description

OLECMDEXECOPT_PROMPTUSER

Execute the command after taking user input.

OLECMDEXECOPT_DONTPROMPTUSER

Execute the command without prompting the user (for example, clicking on the Print toolbar button causes the document to be immediately printed without requir-ing user input).

OLECMDEXECOPT_DODEFAULT

Caller is not sure whether the user should be prompted or not.

OLECMDEXECOPT_SHOWHELP

Object should show help for the corre-sponding command and not execute.

Figure 8   IOleCommandTarget Commands for Exec

Identifier

Description

OLECMDID_OPEN

File Open

OLECMDID_NEW

File New

OLECMDID_SAVE

File Save

OLECMDID_SAVEAS

File Save As

OLECMDID_SAVECOPYAS

File Save Copy As

OLECMDID_PRINT

File Print

OLECMDID_PRINTPREVIEW

File Print Preview

OLECMDID_PAGESETUP

File Page Setup

OLECMDID_SPELL

Tools Spelling

OLECMDID_PROPERTIES

File Properties

OLECMDID_CUT

Edit Cut

OLECMDID_COPY

Edit Copy

OLECMDID_PASTE

Edit Paste

OLECMDID_PASTESPECIAL

Edit Paste Special

OLECMDID_UNDO

Edit Undo

OLECMDID_REDO

Edit Redo

OLECMDID_SELECTALL

Edit Select All

OLECMDID_CLEARSELECTION

Edit Clear

OLECMDID_ZOOM

Query zoom value / display zoom dialog / set zoom value

OLECMDID_GETZOOMRANGE

Retrieves zoom range applicable to View Zoom

Figure 9   IPrint::Print Flags

Value

Description

PRINTFLAG_MAYBOTHERUSER*

Specifies that user interaction is permitted. If not set, the printing process must not require any user input.

PRINTFLAG_PROMPTUSER

Prompt the user for job-specific printing options using the normal print dialog for the object. Support for this option is required. (PRINTFLAG_MAYBOTHERUSER must, of course, be specified as well.)

PRINTFLAG_USERMAYCHANGEPRINTER

The user may change the target printer. (Again, this is only valid if PRINTFLAG_

PROMPTUSER is also specified.)

PRINTFLAG_RECOMPOSETODEVICE

The print job should recompose itself for the indicated target device. Otherwise, it should attempt to use its existing compositional-device association.

PRINTFLAG_DONTACTUALLYPRINT

Do a printing dry run, with any prompting as indicated but without actually sending a print job.

PRINTFLAG_PRINTTOFILE

Print to a file. (The portname field of DVTARGETDEVICE, an argument to the in on the Print function, should indicate the filename.)

* Indicates that this identifier name has replaced the DoughnutHoleSize property in Microsoft Excel as the author's favorite.

Figure 10   Changes in Scribble

Ipframe.h

 // ipframe.h : interface of the CInPlaceFrame class
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1995 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

class CInPlaceFrame : public CDocObjectIPFrameWnd
{
    DECLARE_DYNCREATE(CInPlaceFrame)
public:
    CInPlaceFrame();

// Attributes
public:

// Operations
public:

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CInPlaceFrame)
    public:
    virtual BOOL OnCreateControlBars(CFrameWnd* pWndFrame, CFrameWnd* pWndDoc);
    //}}AFX_VIRTUAL

// Implementation
public:
    virtual ~CInPlaceFrame();
#ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
#endif

protected:
    CToolBar    m_wndToolBar;
    COleResizeBar   m_wndResizeBar;
    COleDropTarget m_dropTarget;

// Generated message map functions
protected:
    //{{AFX_MSG(CInPlaceFrame)
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
        // NOTE - the ClassWizard will add and remove member functions here.
        //    DO NOT EDIT what you see in these blocks of generated code!
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

Ipframe.cpp

 // ipframe.cpp : implementation of the CInPlaceFrame class
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1995 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include "scribble.h"

#include "ipframe.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

//BINDER:
//    An MFC application's in-place frame window is normally a subclass
//  of COleIPFrameWnd.  To be Binder-compatible, we steal code from
//  CDocObjectIPFrameWnd instead.
//BINDER_END

/////////////////////////////////////////////////////////////////////////////
// CInPlaceFrame

IMPLEMENT_DYNCREATE(CInPlaceFrame, CDocObjectIPFrameWnd)

BEGIN_MESSAGE_MAP(CInPlaceFrame, CDocObjectIPFrameWnd)
    //{{AFX_MSG_MAP(CInPlaceFrame)
    ON_WM_CREATE()
    //}}AFX_MSG_MAP
    // Global help commands
    ON_COMMAND(ID_HELP_INDEX, CDocObjectIPFrameWnd::OnHelpIndex)
    ON_COMMAND(ID_HELP_USING, CDocObjectIPFrameWnd::OnHelpUsing)
    ON_COMMAND(ID_HELP, CDocObjectIPFrameWnd::OnHelp)
    ON_COMMAND(ID_DEFAULT_HELP, CDocObjectIPFrameWnd::OnHelpIndex)
    ON_COMMAND(ID_CONTEXT_HELP, CDocObjectIPFrameWnd::OnContextHelp)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// arrays of IDs used to initialize control bars

// toolbar buttons - IDs are command buttons
static UINT BASED_CODE buttons[] =
{
    // same order as in the bitmap 'itoolbar.bmp'
    ID_EDIT_CUT,
    ID_EDIT_COPY,
    ID_EDIT_PASTE,
        ID_SEPARATOR,
    ID_PEN_THICK_OR_THIN,
        ID_SEPARATOR,
    ID_APP_ABOUT,
    ID_CONTEXT_HELP,
};

/////////////////////////////////////////////////////////////////////////////
// CInPlaceFrame construction/destruction

CInPlaceFrame::CInPlaceFrame()
{
}

CInPlaceFrame::~CInPlaceFrame()
{
}

int CInPlaceFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CDocObjectIPFrameWnd::OnCreate(lpCreateStruct) == -1)
        return -1;

    // CResizeBar implements in-place resizing.
    if (!m_wndResizeBar.Create(this))
    {
        TRACE0("Failed to create resize bar\n");
        return -1;      // fail to create
    }

    // By default, it is a good idea to register a drop-target that does
    //  nothing with your frame window.  This prevents drops from
    //  "falling through" to a container that supports drag-drop.
    m_dropTarget.Register(this);

    return 0;
}

// OnCreateControlBars is called by the framework to create control bars on the
//  container application's windows.  pWndFrame is the top level frame window of
//  the container and is always non-NULL.  pWndDoc is the doc level frame window
//  and will be NULL when the container is an SDI application.  A server
//  application can place MFC control bars on either window.
BOOL CInPlaceFrame::OnCreateControlBars(CFrameWnd* pWndFrame, 
                                        CFrameWnd* pWndDoc)
{
    // Create toolbar on client's frame window
    if (!m_wndToolBar.Create(pWndFrame) ||
        !m_wndToolBar.LoadBitmap(IDR_SCRIBTYPE_SRVR_IP) ||
        !m_wndToolBar.SetButtons(buttons, sizeof(buttons)/sizeof(UINT)))
    {
        TRACE0("Failed to create toolbar\n");
        return FALSE;
    }
    // Set owner to this window, so messages are delivered to correct app
    m_wndToolBar.SetOwner(this);

    // TODO: Delete these three lines if you don't want the toolbar to
    //  be dockable
    m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
    pWndFrame->EnableDocking(CBRS_ALIGN_ANY);
    pWndFrame->DockControlBar(&m_wndToolBar);

    // TODO: Remove this if you don't want tool tips
    m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
        CBRS_TOOLTIPS | CBRS_FLYBY);

    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CInPlaceFrame diagnostics

#ifdef _DEBUG
void CInPlaceFrame::AssertValid() const
{
    CDocObjectIPFrameWnd::AssertValid();
}

void CInPlaceFrame::Dump(CDumpContext& dc) const
{
    CDocObjectIPFrameWnd::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CInPlaceFrame commands

Scribdoc.h

 // scribdoc.h : interface of the CScribDoc class
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1995 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.
//

// Forward declaration of data structure class
class CStroke;

class CScribItem;

class CScribDoc : public CDocObjectServerDoc
{
protected: // create from serialization only
    CScribDoc();
    DECLARE_DYNCREATE(CScribDoc)

// Attributes
protected:
    // The document keeps track of the current pen width on
    // behalf of all views. We'd like the user interface of
    // Scribble to be such that if the user chooses the Draw
    // Thick Line command, it will apply to all views, not just
    // the view that currently has the focus.

    UINT            m_nPenWidth;        // current user-selected pen width
    BOOL            m_bThickPen;        // TRUE if current pen is thick
    UINT            m_nThinWidth;
    UINT            m_nThickWidth;
    CPen            m_penCur;           // pen created according to
                                        // user-selected pen style (width)
public:
    CTypedPtrList<CObList,CStroke*>     m_strokeList;   
    CPen*           GetCurrentPen() { return &m_penCur; }

protected:
    CSize           m_sizeDoc;
public:
    CSize GetDocSize() { return m_sizeDoc; }
    COleServerItem* OnGetEmbeddedItem();
    CScribItem* GetEmbeddedItem()
        { return (CScribItem*)COleServerDoc::GetEmbeddedItem(); }

// Operations
public:
    CStroke* NewStroke();

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CScribDoc)
    public:
    virtual BOOL OnNewDocument();
    virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);
    virtual void DeleteContents();
    //}}AFX_VIRTUAL

// Implementation
protected:
    void ReplacePen();
    void OnSetItemRects(LPCRECT lpPosRect, LPCRECT lpClipRect);

public:
    virtual ~CScribDoc();
    virtual void Serialize(CArchive& ar);   // overridden for document i/o
#ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
#endif
protected:
    void            InitDocument();

// Generated message map functions
protected:
    //{{AFX_MSG(CScribDoc)
    afx_msg void OnEditClearAll();
    afx_msg void OnPenThickOrThin();
    afx_msg void OnUpdateEditClearAll(CCmdUI* pCmdUI);
    afx_msg void OnUpdatePenThickOrThin(CCmdUI* pCmdUI);
    afx_msg void OnPenWidths();
    afx_msg void OnEditCopy();
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////
// class CStroke
//
// A stroke is a series of connected points in the scribble drawing.
// A scribble document may have multiple strokes.

class CStroke : public CObject
{
public:
    CStroke(UINT nPenWidth);

protected:
    CStroke();
    DECLARE_SERIAL(CStroke)

// Attributes
    UINT                   m_nPenWidth;  // one pen width applies to entire 
                                         // stroke
    CArray<CPoint,CPoint>  m_pointArray; // series of connected points
    CRect               m_rectBounding;  // smallest rect that surrounds all
                                         // of the points in the stroke
                                         // measured in MM_LOENGLISH units
                                         // (0.01 inches, with Y-axis inverted)
public:
    CRect& GetBoundingRect() { return m_rectBounding; }

// Operations
public:
    BOOL DrawStroke(CDC* pDC);
    void FinishStroke();

public:
    virtual void Serialize(CArchive& ar);
};

Scribdoc.cpp

 // scribdoc.cpp : implementation of the CScribDoc class
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1995 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.
//

#include "stdafx.h"
#include "scribble.h"

#include "scribdoc.h"
#include "pendlg.h"
#include "scribvw.h"
#include "scribitm.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CScribDoc

IMPLEMENT_DYNCREATE(CScribDoc, CDocObjectServerDoc)

BEGIN_MESSAGE_MAP(CScribDoc, CDocObjectServerDoc)
    //{{AFX_MSG_MAP(CScribDoc)
    ON_COMMAND(ID_EDIT_CLEAR_ALL, OnEditClearAll)
    ON_COMMAND(ID_PEN_THICK_OR_THIN, OnPenThickOrThin)
    ON_UPDATE_COMMAND_UI(ID_EDIT_CLEAR_ALL, OnUpdateEditClearAll)
    ON_UPDATE_COMMAND_UI(ID_PEN_THICK_OR_THIN, OnUpdatePenThickOrThin)
    ON_COMMAND(ID_PEN_WIDTHS, OnPenWidths)
    ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CScribDoc construction/destruction

CScribDoc::CScribDoc()
{
    m_sizeDoc = CSize(200, 200);
}

CScribDoc::~CScribDoc()
{
}

BOOL CScribDoc::OnNewDocument()
{
    if (!CDocObjectServerDoc::OnNewDocument())
        return FALSE;
    InitDocument();
    return TRUE;
}


/////////////////////////////////////////////////////////////////////////////
// CScribDoc serialization

void CScribDoc::Serialize(CArchive& ar)
{
    if (ar.IsStoring())
    {
        ar << m_sizeDoc;
    }
    else
    {
        ar >> m_sizeDoc;
    }
    m_strokeList.Serialize(ar);
}

/////////////////////////////////////////////////////////////////////////////
// CScribDoc diagnostics

#ifdef _DEBUG
void CScribDoc::AssertValid() const
{
    CDocObjectServerDoc::AssertValid();
}

void CScribDoc::Dump(CDumpContext& dc) const
{
    CDocObjectServerDoc::Dump(dc);
}

#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CScribDoc commands

BOOL CScribDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
    if (!CDocObjectServerDoc::OnOpenDocument(lpszPathName))
        return FALSE;
    InitDocument();
    return TRUE;
}

void CScribDoc::DeleteContents()
{
    while (!m_strokeList.IsEmpty())
    {
        delete m_strokeList.RemoveHead();
    }
    CDocObjectServerDoc::DeleteContents();
}

void CScribDoc::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();       // initialze pen according to current width

    // default document size is 2 x 2 inches
    m_sizeDoc = CSize(200,200);

}

CStroke* CScribDoc::NewStroke()
{
    CStroke* pStrokeItem = new CStroke(m_nPenWidth);
    m_strokeList.AddTail(pStrokeItem);
    SetModifiedFlag();  // Mark the document as having been modified, for
                        // purposes of confirming File Close.
    return pStrokeItem;
}




/////////////////////////////////////////////////////////////////////////////
// CStroke

IMPLEMENT_SERIAL(CStroke, CObject, 2)

CStroke::CStroke()
{
    // This empty constructor should be used by serialization only
}

CStroke::CStroke(UINT nPenWidth)
{
    m_nPenWidth = nPenWidth;
    m_rectBounding.SetRectEmpty();
}

void CStroke::Serialize(CArchive& ar)
{
    if (ar.IsStoring())
    {
        ar << m_rectBounding;
        ar << (WORD)m_nPenWidth;
        m_pointArray.Serialize(ar);
    }
    else
    {
        ar >> m_rectBounding;
        WORD w;
        ar >> w;
        m_nPenWidth = w;
        m_pointArray.Serialize(ar);
    }
}

BOOL CStroke::DrawStroke(CDC* pDC)
{
    CPen penStroke;
    if (!penStroke.CreatePen(PS_SOLID, m_nPenWidth, RGB(0,0,0)))
        return FALSE;
    CPen* pOldPen = pDC->SelectObject(&penStroke);
    pDC->MoveTo(m_pointArray[0]);
    for (int i=1; i < m_pointArray.GetSize(); i++)
    {
        pDC->LineTo(m_pointArray[i]);
    }

    pDC->SelectObject(pOldPen);
    return TRUE;
}

void CScribDoc::OnEditClearAll()
{
    DeleteContents();
    SetModifiedFlag();  // Mark the document as having been modified, for
                        // purposes of confirming File Close.
    UpdateAllViews(NULL);
}

void CScribDoc::OnPenThickOrThin()
{
    // Toggle the state of the pen between thin or thick.
    m_bThickPen = !m_bThickPen;

    // Change the current pen to reflect the new user-specified width.
    ReplacePen();
}


void CScribDoc::ReplacePen()
{
    m_nPenWidth = m_bThickPen? m_nThickWidth : m_nThinWidth;

    // Change the current pen to reflect the new user-specified width.
    m_penCur.DeleteObject();
    m_penCur.CreatePen(PS_SOLID, m_nPenWidth, RGB(0,0,0)); // solid black
}

void CScribDoc::OnUpdateEditClearAll(CCmdUI* pCmdUI)
{
    // Enable the command user interface object (menu item or tool bar
    // button) if the document is non-empty, i.e., has at least one stroke.
    pCmdUI->Enable(!m_strokeList.IsEmpty());
}


void CScribDoc::OnUpdatePenThickOrThin(CCmdUI* pCmdUI)
{
    // Add check mark to Draw Thick Line menu item, if the current
    // pen width is "thick".
    pCmdUI->SetCheck(m_bThickPen);
}


void CScribDoc::OnPenWidths()
{
    CPenWidthsDlg dlg;
    // Initialize dialog data
    dlg.m_nThinWidth = m_nThinWidth;
    dlg.m_nThickWidth = m_nThickWidth;

    // Invoke the dialog box
    if (dlg.DoModal() == IDOK)
    {
        // retrieve the dialog data
        m_nThinWidth = dlg.m_nThinWidth;
        m_nThickWidth = dlg.m_nThickWidth;

        // Update the pen that is used by views when drawing new strokes,
        // to reflect the new pen width definitions for "thick" and "thin".
        ReplacePen();
    }
}

void CStroke::FinishStroke()
{
    // Calculate the bounding rectangle.  It's needed for smart
    // repainting.

    if (m_pointArray.GetSize()==0)
    {
        m_rectBounding.SetRectEmpty();
        return;
    }
    CPoint pt = m_pointArray[0];
    m_rectBounding = CRect(pt.x, pt.y, pt.x, pt.y);

    for (int i=1; i < m_pointArray.GetSize(); i++)
    {
        // If the point lies outside of the accumulated bounding
        // rectangle, then inflate the bounding rect to include it.
        pt = m_pointArray[i];
        m_rectBounding.left     = min(m_rectBounding.left, pt.x);
        m_rectBounding.right    = max(m_rectBounding.right, pt.x);
        m_rectBounding.top      = max(m_rectBounding.top, pt.y);
        m_rectBounding.bottom   = min(m_rectBounding.bottom, pt.y);
    }

    // Add the pen width to the bounding rectangle.  This is necessary
    // to account for the width of the stroke when invalidating
    // the screen.
    m_rectBounding.InflateRect(CSize(m_nPenWidth, -(int)m_nPenWidth));
    return;
}

COleServerItem* CScribDoc::OnGetEmbeddedItem()
{
    // OnGetEmbeddedItem is called by the framework to get the COleServerItem
    //  that is associated with the document.  It is only called when necessary.

    CScribItem* pItem = new CScribItem(this);
    ASSERT_VALID(pItem);
    return pItem;
}

void CScribDoc::OnSetItemRects(LPCRECT lpPosRect, LPCRECT lpClipRect)
{
    // call base class to change the size of the window
    CDocObjectServerDoc::OnSetItemRects(lpPosRect, lpClipRect);

    // notify first view that scroll info should change
    POSITION pos = GetFirstViewPosition();
    CScribView* pView = (CScribView*)GetNextView(pos);
    pView->SetScrollInfo();
}

void CScribDoc::OnEditCopy()
{
    CScribItem* pItem = GetEmbeddedItem();
    pItem->CopyToClipboard(TRUE);
}

Scribitm.h

 // scribitm.h : interface of the CScribItem class
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1995 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

class CScribItem : public CDocObjectServerItem
{
    DECLARE_DYNAMIC(CScribItem)

// Constructors
public:
    CScribItem(CScribDoc* pContainerDoc);

// Attributes
    CScribDoc* GetDocument() const
        { return (CScribDoc*)COleServerItem::GetDocument(); }

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CScribItem)
    public:
    virtual BOOL OnDraw(CDC* pDC, CSize& rSize);
    virtual BOOL OnGetExtent(DVASPECT dwDrawAspect, CSize& rSize);
    //}}AFX_VIRTUAL

// Implementation
public:
    ~CScribItem();
#ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
#endif

protected:
    virtual void Serialize(CArchive& ar);   // overridden for document i/o
};

/////////////////////////////////////////////////////////////////////////////

Scribitm.cpp

 // scribitm.cpp : implementation of the CScribItem class
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1995 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include "scribble.h"

#include "scribdoc.h"
#include "scribitm.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CScribItem implementation

IMPLEMENT_DYNAMIC(CScribItem, CDocObjectServerItem)

CScribItem::CScribItem(CScribDoc* pContainerDoc)
    : CDocObjectServerItem(pContainerDoc, TRUE)
{
    // TODO: add one-time construction code here
    //  (eg, adding additional clipboard formats to the item's data source)
}

CScribItem::~CScribItem()
{
    // TODO: add cleanup code here
}

void CScribItem::Serialize(CArchive& ar)
{
    // CScribItem::Serialize will be called by the framework if
    //  the item is copied to the clipboard.  This can happen automatically
    //  through the OLE callback OnGetClipboardData.  A good default for
    //  the embedded item is simply to delegate to the document's Serialize
    //  function.  If you support links, then you will want to serialize
    //  just a portion of the document.

    if (!IsLinkedItem())
    {
        CScribDoc* pDoc = GetDocument();
        ASSERT_VALID(pDoc);
        pDoc->Serialize(ar);
    }
}

BOOL CScribItem::OnGetExtent(DVASPECT dwDrawAspect, CSize& rSize)
{
    // This implementation of CScribItem::OnGetExtent only handles
    //  the "content" aspect indicated by DVASPECT_CONTENT.

    if (dwDrawAspect != DVASPECT_CONTENT)
        return CDocObjectServerItem::OnGetExtent(dwDrawAspect, rSize);

    // CScribItem::OnGetExtent is called to get the extent in
    //  HIMETRIC units of the entire item.  The default implementation
    //  here simply returns a hard-coded number of units.

    CScribDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);

    rSize = pDoc->GetDocSize();
    CClientDC dc(NULL);

    // set a MM_LOENGLISH based on logical inches
    //  (we can't use MM_LOENGLISH because MM_LOENGLISH uses physical inches)
    dc.SetMapMode(MM_ANISOTROPIC);
    dc.SetViewportExt(dc.GetDeviceCaps(LOGPIXELSX), 
                      dc.GetDeviceCaps(LOGPIXELSY));
    dc.SetWindowExt(100, -100);
    dc.LPtoHIMETRIC(&rSize);

    return TRUE;
}

BOOL CScribItem::OnDraw(CDC* pDC, CSize& /* rSize */)
{
    CScribDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);

    pDC->SetMapMode(MM_ANISOTROPIC);
    CSize sizeDoc = pDoc->GetDocSize();
    sizeDoc.cy = -sizeDoc.cy;
    pDC->SetWindowOrg(0,0);
    pDC->SetWindowExt(sizeDoc);

    CTypedPtrList<CObList,CStroke*>& strokeList = pDoc->m_strokeList;
    POSITION pos = strokeList.GetHeadPosition();
    while (pos != NULL)
    {
        strokeList.GetNext(pos)->DrawStroke(pDC);
    }

    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CScribItem diagnostics

#ifdef _DEBUG
void CScribItem::AssertValid() const
{
    CDocObjectServerItem::AssertValid();
}

void CScribItem::Dump(CDumpContext& dc) const
{
    CDocObjectServerItem::Dump(dc);
}
#endif

/////////////////////////////////////////////////////////////////////////////

Scribvw.h

 // scribvw.h : interface of the CScribView class
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1995 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

class CScribView : public CScrollView
{
protected: // create from serialization only
       CScribView();
       DECLARE_DYNCREATE(CScribView)

// Attributes
public:
       CScribDoc* GetDocument();

protected:
       CStroke*    m_pStrokeCur;   // the stroke in progress
       CPoint      m_ptPrev;       // the last mouse pt in the stroke in progress

// Operations
public:
       void SetScrollInfo();       // resync scroll sizes

// Overrides
       // ClassWizard generated virtual function overrides
       //{{AFX_VIRTUAL(CScribView)
       public:
       virtual void OnDraw(CDC* pDC);  // overridden to draw this view
       virtual void OnInitialUpdate();
       virtual void OnPrepareDC(CDC* pDC, CPrintInfo* pInfo = NULL);
       protected:
       virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
       virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
       virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
       virtual void OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint);
       virtual void OnPrint(CDC* pDC, CPrintInfo* pInfo);
       //}}AFX_VIRTUAL

// Implementation
public:
       void PrintTitlePage(CDC* pDC, CPrintInfo* pInfo);
       void PrintPageHeader(CDC* pDC, CPrintInfo* pInfo, CString& strHeader);
       virtual ~CScribView();
#ifdef _DEBUG
       virtual void AssertValid() const;
       virtual void Dump(CDumpContext& dc) const;
#endif

protected:

// Generated message map functions
protected:
       //{{AFX_MSG(CScribView)
       afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
       afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
       afx_msg void OnMouseMove(UINT nFlags, CPoint point);
       afx_msg void OnSize(UINT nType, int cx, int cy);
       //}}AFX_MSG
       DECLARE_MESSAGE_MAP()
};

#ifndef _DEBUG  // debug version in scribvw.cpp
inline CScribDoc* CScribView::GetDocument()
   { return (CScribDoc*) m_pDocument; }
#endif

/////////////////////////////////////////////////////////////////////////////

Scribvw.cpp

 // scribvw.cpp : implementation of the CScribView class
//
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1995 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.

#include "stdafx.h"
#include "scribble.h"

#include "scribdoc.h"
#include "scribvw.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CScribView

IMPLEMENT_DYNCREATE(CScribView, CScrollView)


BEGIN_MESSAGE_MAP(CScribView, CScrollView)
       //{{AFX_MSG_MAP(CScribView)
       ON_WM_LBUTTONDOWN()
       ON_WM_LBUTTONUP()
       ON_WM_MOUSEMOVE()
       ON_WM_SIZE()
       //}}AFX_MSG_MAP

       // Standard printing commands
       ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
       ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CScribView construction/destruction

CScribView::CScribView()
{
       SetScrollSizes(MM_TEXT, CSize(0, 0));
}

CScribView::~CScribView()
{
}



/////////////////////////////////////////////////////////////////////////////
// CScribView drawing

void CScribView::OnDraw(CDC* pDC)
{
       CScribDoc* pDoc = GetDocument();
       ASSERT_VALID(pDoc);


       // Get the invalidated rectangle of the view, or in the case
       // of printing, the clipping region of the printer dc.
       CRect rectClip;
       CRect rectStroke;
       pDC->GetClipBox(&rectClip);
       pDC->LPtoDP(&rectClip);
       rectClip.InflateRect(1, 1); // avoid rounding to nothing

       // Note: CScrollView::OnPaint() will have already adjusted the
       // viewport origin before calling OnDraw(), to reflect the
       // currently scrolled position.

       // 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);
              rectStroke = pStroke->GetBoundingRect();
              pDC->LPtoDP(&rectStroke);
              rectStroke.InflateRect(1, 1); // avoid rounding to nothing
              if (!rectStroke.IntersectRect(&rectStroke, &rectClip))
                     continue;
              pStroke->DrawStroke(pDC);
       }
}

/////////////////////////////////////////////////////////////////////////////
// CScribView printing

BOOL CScribView::OnPreparePrinting(CPrintInfo* pInfo)
{
       pInfo->SetMaxPage(2);   // the document is two pages long:
                               // the first page is the title page
                               // the second is the drawing






       BOOL bRet = DoPreparePrinting(pInfo);       // default preparation
       pInfo->m_nNumPreviewPages = 2;  // Preview 2 pages at a time
       // Set this value after calling DoPreparePrinting to override
       // value read from .INI file
       return bRet;
}

void CScribView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
       // TODO: add extra initialization before printing
}

void CScribView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
       // TODO: add cleanup after printing
}


/////////////////////////////////////////////////////////////////////////////
// CScribView diagnostics

#ifdef _DEBUG
void CScribView::AssertValid() const
{
       CScrollView::AssertValid();
}

void CScribView::Dump(CDumpContext& dc) const
{
       CScrollView::Dump(dc);
}

CScribDoc* CScribView::GetDocument() // non-debug version is inline
{
       ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CScribDoc)));
       return (CScribDoc*) m_pDocument;
}

#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CScribView message handlers

void CScribView::OnLButtonDown(UINT, CPoint point)
{  
    // When the user presses the mouse button, she may be
       // starting a new stroke, or selecting or de-selecting a stroke.

       // CScrollView changes the viewport origin and mapping mode.
       // It's necessary to convert the point from device coordinates
       // to logical coordinates, such as are stored in the document.
       CClientDC dc(this);
       OnPrepareDC(&dc);
       dc.DPtoLP(&point);

       m_pStrokeCur = GetDocument()->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;

}


void CScribView::OnLButtonUp(UINT, CPoint point)
{
       // Mouse button up is interesting in the Scribble application
       // only if the user is currently drawing a new stroke by dragging
       // the captured mouse.

       if (GetCapture() != this)
              return; // If this window (view) didn't capture the mouse,
                      // then the user isn't drawing in this window.

       CScribDoc* pDoc = GetDocument();

-----------------------------------------------------

       // We can't interpret the hint, so assume that anything might
       // have been updated.
       Invalidate(TRUE);
       return;
}

void CScribView::OnInitialUpdate()
{
       SetScrollInfo();
       CScrollView::OnInitialUpdate();
}

void CScribView::SetScrollInfo()
{
       CClientDC dc(NULL);
       OnPrepareDC(&dc);
       CSize sizeDoc = GetDocument()->GetDocSize();
       dc.LPtoDP(&sizeDoc);
       SetScrollSizes(MM_TEXT, sizeDoc);
}

void CScribView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
       if (pInfo->m_nCurPage == 1)  // page no. 1 is the title page
       {
              PrintTitlePage(pDC, pInfo);
              return; // nothing else to print on page 1 but the page title
       }
       CString strHeader = GetDocument()->GetTitle();

       PrintPageHeader(pDC, pInfo, strHeader);
       // PrintPageHeader() subtracts out from the pInfo->m_rectDraw the
       // amount of the page used for the header.

       pDC->SetWindowOrg(pInfo->m_rectDraw.left,-pInfo->m_rectDraw.top);

       // Now print the rest of the page
       OnDraw(pDC);
}

void CScribView::PrintTitlePage(CDC* pDC, CPrintInfo* pInfo)
{
       // Prepare a font size for displaying the file name
       LOGFONT logFont;
       memset(&logFont, 0, sizeof(LOGFONT));
       logFont.lfHeight = 75;  //  3/4th inch high in MM_LOENGLISH
                                                 // (1/100th inch)
       CFont font;
       CFont* pOldFont = NULL;
       if (font.CreateFontIndirect(&logFont))
              pOldFont = pDC->SelectObject(&font);

       // Get the file name, to be displayed on title page
       CString strPageTitle = GetDocument()->GetTitle();

       // Display the file name 1 inch below top of the page,
       // centered horizontally
       pDC->SetTextAlign(TA_CENTER);
       pDC->TextOut(pInfo->m_rectDraw.right/2, -100, strPageTitle);

       if (pOldFont != NULL)
              pDC->SelectObject(pOldFont);
}

void CScribView::PrintPageHeader(CDC* pDC, CPrintInfo* pInfo,
       CString& strHeader)
{
       // Print a page header consisting of the name of
       // the document and a horizontal line
       pDC->TextOut(0,-25, strHeader);  // 1/4 inch down

       // Draw a line across the page, below the header
       TEXTMETRIC textMetric;
       pDC->GetTextMetrics(&textMetric);
       int y = -35 - textMetric.tmHeight;         // line 1/10th inch below text
       pDC->MoveTo(0, y);                         // from left margin
       pDC->LineTo(pInfo->m_rectDraw.right, y);   // to right margin

       // Subtract out from the drawing rectange the space used by the header.
       y -= 25;    // space 1/4 inch below (top of) line
       pInfo->m_rectDraw.top += y;
}

void CScribView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{
       CScribDoc* pDoc = GetDocument();
       CScrollView::OnPrepareDC(pDC, pInfo);

       pDC->SetMapMode(MM_ANISOTROPIC);
       CSize sizeDoc = pDoc->GetDocSize();
       sizeDoc.cy = -sizeDoc.cy;
       pDC->SetWindowExt(sizeDoc);

       // Binder objects don't scale

       int xLogPixPerInch = pDC->GetDeviceCaps(LOGPIXELSX);
       int yLogPixPerInch = pDC->GetDeviceCaps(LOGPIXELSY);

       long xExt = (long)(sizeDoc.cx * xLogPixPerInch) / 100;
       long yExt = (long)(sizeDoc.cy * yLogPixPerInch) / 100;

       pDC->SetViewportExt((int)xExt, (int)-yExt);
}

void CScribView::OnSize(UINT nType, int cx, int cy)
{
       SetScrollInfo();       // ensure that scroll info is up-to-date
       CScrollView::OnSize(nType, cx, cy);
}