Figure 2   SMOOTH's CSmooth View Class

SmoothView.h


class CSmoothView : public CScrollView
{
protected: // create from serialization only
    CFont m_font;
    CPalette m_palette;
    int m_nImageHeight;
    int m_nImageWidth;
    CBitmap m_bitmap;
    void InitScrollSizes ();
    int m_nLineSlices;
    int m_nPageSlices;
    DWORD m_dwMinTime;
    CSmoothView();
    DECLARE_DYNCREATE(CSmoothView)

// Attributes
public:
    void DoPaletteChanged ();
    BOOL DoQueryNewPalette ();
    CSmoothDoc* GetDocument();

// Operations
public:

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CSmoothView)
    public:
    virtual void OnDraw(CDC* pDC);  // overridden to draw this view
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
    virtual BOOL OnScroll(UINT nScrollCode, UINT nPos,
                          BOOL bDoScroll = TRUE);
    protected:
    virtual void OnInitialUpdate(); // called first time after construct
    //}}AFX_VIRTUAL

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

protected:

// Generated message map functions
    protected:
    //{{AFX_MSG(CSmoothView)
    afx_msg void OnSize(UINT nType, int cx, int cy);
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

#ifndef _DEBUG  // debug version in SmoothView.cpp
inline CSmoothDoc* CSmoothView::GetDocument()
    { return (CSmoothDoc*)m_pDocument; }
#endif

SmoothView.cpp

#include "stdafx.h"
#include "Smooth.h"

#include "SmoothDoc.h"
#include "SmoothView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

///////////////////////////////////////////////////////////////////////
// CSmoothView

IMPLEMENT_DYNCREATE(CSmoothView, CScrollView)

BEGIN_MESSAGE_MAP(CSmoothView, CScrollView)
    //{{AFX_MSG_MAP(CSmoothView)
    ON_WM_SIZE()
    ON_WM_CREATE()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

///////////////////////////////////////////////////////////////////////
// CSmoothView construction/destruction

CSmoothView::CSmoothView()
{
    m_dwMinTime = 10;       // Minimum delay time in milliseconds
    m_nPageSlices = 12;     // Number of scroll subdivisions per page
    m_nLineSlices = 4;      // Number of scroll subdivisions per line
    m_nImageWidth = 0;
    m_nImageHeight = 0;
}

CSmoothView::~CSmoothView()
{
}

BOOL CSmoothView::PreCreateWindow(CREATESTRUCT& cs)
{
    // TODO: Modify the Window class or styles here by modifying
    //  the CREATESTRUCT cs

    return CScrollView::PreCreateWindow(cs);
}

///////////////////////////////////////////////////////////////////////
// CSmoothView drawing

void CSmoothView::OnDraw(CDC* pDC)
{
    CSmoothDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);

    //
    // Realize a palette if this is a palettized output device.
    //
    CPalette* pOldPalette= NULL;
    if (m_palette.m_hObject != NULL) {
        pOldPalette = pDC->SelectPalette (&m_palette, FALSE);
        pDC->RealizePalette ();
    }

    //
    // Paint the window background with the bitmap.
    //
    CDC memoryDC;
    memoryDC.CreateCompatibleDC (pDC);
    CBitmap* pOldBitmap = memoryDC.SelectObject (&m_bitmap);

    CRect rect;
    pDC->GetClipBox (&rect);

    int nStartCol = rect.left / m_nImageWidth;
    int nEndCol = (rect.right + m_nImageWidth + 1) / m_nImageWidth;
    int nStartRow = rect.top / m_nImageHeight;
    int nEndRow = (rect.bottom + m_nImageHeight + 1) / m_nImageHeight;

    for (int i=nStartCol; i<nEndCol; i++) {
        for (int j=nStartRow; j<nEndRow; j++) {
            int x = i * m_nImageWidth;
            int y = j * m_nImageHeight;
            pDC->BitBlt (x, y, m_nImageWidth, m_nImageHeight,
                         &memoryDC, 0, 0, SRCCOPY);
        }
    }
    memoryDC.SelectObject (pOldBitmap);

    //
    // Draw a string of text that floats above the background.
    //
    pDC->SetBkMode (TRANSPARENT);
    CFont* pOldFont = pDC->SelectObject (&m_font);

    CString string = "Smooth scrolling is cool!";

    rect.SetRect (0, 0, 1024, 1024);
    rect.OffsetRect (6, 6);
    pDC->SetTextColor (RGB (192, 192, 192));
    pDC->DrawText (string, &rect,
                   DT_SINGLELINE | DT_CENTER | DT_VCENTER);

    rect.SetRect (0, 0, 1024, 1024);
    pDC->SetTextColor (RGB (0, 0, 0));
    pDC->DrawText (string, &rect,
                   DT_SINGLELINE | DT_CENTER | DT_VCENTER);

    pDC->SelectObject (pOldFont);

    //
    // Deselect the palette that was selected in earlier.
    //
    if (pOldPalette != NULL)
        pDC->SelectPalette (pOldPalette, FALSE);
}

void CSmoothView::OnInitialUpdate()
{
    CScrollView::OnInitialUpdate ();
    InitScrollSizes ();
}

///////////////////////////////////////////////////////////////////////
// CSmoothView diagnostics

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

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

CSmoothDoc* CSmoothView::GetDocument() // non-debug version is inline
{
    ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSmoothDoc)));
    return (CSmoothDoc*)m_pDocument;
}
#endif //_DEBUG

///////////////////////////////////////////////////////////////////////
// CSmoothView message handlers

BOOL CSmoothView::OnScroll(UINT nScrollCode, UINT nPos, BOOL bDoScroll) 
{
    //
    // First handle left/right scroll messages. If scrolling by page,
    // scroll m_nPageSlices times rather than 1. If scrolling by line,
    // scroll m_nLineSlices times.
    //
    BYTE nCode = LOBYTE (nScrollCode);

    if ((nCode == SB_PAGELEFT) || (nCode == SB_PAGERIGHT) ||
        (nCode == SB_LINELEFT) || (nCode == SB_LINERIGHT)) {

        int nCount, nInc, nFinalInc, nLineCode;

        switch (nCode) {

        case SB_PAGELEFT:
            nLineCode = SB_LINELEFT;
            nInc = m_pageDev.cx / m_nPageSlices;
            nFinalInc = m_pageDev.cx % m_nPageSlices;
            nCount = m_nPageSlices;
            break;

        case SB_PAGERIGHT:
            nLineCode = SB_LINERIGHT;
            nInc = m_pageDev.cx / m_nPageSlices;
            nFinalInc = m_pageDev.cx % m_nPageSlices;
            nCount = m_nPageSlices;
            break;

        case SB_LINELEFT:
            nLineCode = SB_LINELEFT;
            nInc = m_lineDev.cx / m_nLineSlices;
            nFinalInc = m_lineDev.cx % m_nLineSlices;
            nCount = m_nLineSlices;
            break;

        case SB_LINERIGHT:
            nLineCode = SB_LINERIGHT;
            nInc = m_lineDev.cx / m_nLineSlices;
            nFinalInc = m_lineDev.cx % m_nLineSlices;
            nCount = m_nLineSlices;
            break;
        }

        int nOldLineSize = m_lineDev.cx;
        BOOL bResult = FALSE;
        DWORD dwTime = 0;

        while (nCount--) {
            DWORD dwCurrentTime = ::GetCurrentTime ();
            DWORD dwElapsedTime = dwCurrentTime - dwTime;
            if (dwElapsedTime < m_dwMinTime)
                ::Sleep (m_dwMinTime - dwElapsedTime);
            dwTime = dwCurrentTime;

            m_lineDev.cx = nInc;
            BOOL bScrolled =
                CScrollView::OnScroll (MAKEWORD (nLineCode, -1), nPos);
            m_lineDev.cx = nOldLineSize;

            if (!bScrolled)
                return bResult;

            bResult = TRUE;
        }

        if (nFinalInc) {
            m_lineDev.cx = nFinalInc;
            if (!CScrollView::OnScroll (MAKEWORD (nLineCode, -1), nPos))
                bResult = TRUE;
            m_lineDev.cx = nOldLineSize;
        }
        return bResult;
    }

    //
    // Next handle up/down scroll messages. If scrolling by page,
    // scroll m_nPageSlices times rather than 1. If scrolling by line,
    // scroll m_nLineSlices times.
    //
    nCode = HIBYTE (nScrollCode);

    if ((nCode == SB_PAGEUP) || (nCode == SB_PAGEDOWN) ||
        (nCode == SB_LINEUP) || (nCode == SB_LINEDOWN)) {

        int nCount, nInc, nFinalInc, nLineCode;

        switch (nCode) {

        case SB_PAGEUP:
            nLineCode = SB_LINEUP;
            nInc = m_pageDev.cy / m_nPageSlices;
            nFinalInc = m_pageDev.cy % m_nPageSlices;
            nCount = m_nPageSlices;
            break;

        case SB_PAGEDOWN:
            nLineCode = SB_LINEDOWN;
            nInc = m_pageDev.cy / m_nPageSlices;
            nFinalInc = m_pageDev.cy % m_nPageSlices;
            nCount = m_nPageSlices;
            break;

        case SB_LINEUP:
            nLineCode = SB_LINEUP;
            nInc = m_lineDev.cy / m_nLineSlices;
            nFinalInc = m_lineDev.cy % m_nLineSlices;
            nCount = m_nLineSlices;
            break;

        case SB_LINEDOWN:
            nLineCode = SB_LINEDOWN;
            nInc = m_lineDev.cy / m_nLineSlices;
            nFinalInc = m_lineDev.cy % m_nLineSlices;
            nCount = m_nLineSlices;
            break;
        }

        int nOldLineSize = m_lineDev.cy;
        BOOL bResult = FALSE;
        DWORD dwTime = 0;

        while (nCount--) {
            DWORD dwCurrentTime = ::GetCurrentTime ();
            DWORD dwElapsedTime = dwCurrentTime - dwTime;
            if (dwElapsedTime < m_dwMinTime)
                ::Sleep (m_dwMinTime - dwElapsedTime);
            dwTime = dwCurrentTime;

            m_lineDev.cy = nInc;
            BOOL bScrolled =
                CScrollView::OnScroll (MAKEWORD (-1, nLineCode), nPos);
            m_lineDev.cy = nOldLineSize;

            if (!bScrolled)
                return bResult;

            bResult = TRUE;
        }

        if (nFinalInc) {
            m_lineDev.cy = nFinalInc;
            if (!CScrollView::OnScroll (MAKEWORD (-1, nLineCode), nPos))
                bResult = TRUE;
            m_lineDev.cy = nOldLineSize;
        }
        return bResult;
    }
    //
    // If we make it to here, the scroll message wasn't handled above.
    // Call the base class's OnScroll function and let it do the work.
    //
    return CScrollView::OnScroll (nScrollCode, nPos, bDoScroll);
}

void CSmoothView::OnSize(UINT nType, int cx, int cy) 
{
    CScrollView::OnSize (nType, cx, cy);
    InitScrollSizes ();
}

void CSmoothView::InitScrollSizes ()
{
    CRect rect;
    GetClientRect (&rect);

    int nHPageSize = (rect.Width () * 9) / 10;
    int nVPageSize = (rect.Height () * 9) / 10;
    int nHLineSize = nHPageSize / 10;
    int nVLineSize = nVPageSize / 10;

    SetScrollSizes (MM_TEXT,
                    CSize (1024, 1024),
                    CSize (nHPageSize, nVPageSize),
                    CSize (nHLineSize, nVLineSize));
}

int CSmoothView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    if (CScrollView::OnCreate(lpCreateStruct) == -1)
        return -1;
    
    //
    // Load the background bitmap.
    //
    HANDLE hBitmap = ::LoadImage (AfxGetInstanceHandle (),
                                  MAKEINTRESOURCE (IDB_BITMAP), IMAGE_BITMAP, 
                                  0, 0, LR_CREATEDIBSECTION);

    ASSERT (hBitmap != NULL);
    m_bitmap.Attach (hBitmap);

    BITMAP bm;
    m_bitmap.GetObject (sizeof (bm), &bm);
    m_nImageWidth = bm.bmWidth;
    m_nImageHeight = bm.bmHeight;

    //
    // Create a gray scale palette for the bitmap (if needed).
    //
    CClientDC dc (this);

    if (dc.GetDeviceCaps (RASTERCAPS) & RC_PALETTE) {
        UINT nSize = sizeof (LOGPALETTE) + (sizeof (PALETTEENTRY) * 31);
        LOGPALETTE* plp = (LOGPALETTE*) new BYTE[nSize];

        plp->palVersion = 0x300;
        plp->palNumEntries = 32;

        for (int i=0; i<32; i++) {
            plp->palPalEntry[i].peRed = i * 8;
            plp->palPalEntry[i].peGreen = i * 8;
            plp->palPalEntry[i].peBlue = i * 8;
            plp->palPalEntry[i].peFlags = 0;
        }

        m_palette.CreatePalette (plp);
        ASSERT_VALID (&m_palette);

        delete[] plp;
    }

    //
    // Create a 36-point bold Times New Roman font.
    //
    LOGFONT lf;
    ::ZeroMemory (&lf, sizeof (lf));

    lf.lfHeight = 360;
    lf.lfWeight = FW_BOLD;
    lf.lfItalic = TRUE;
    ::lstrcpy (lf.lfFaceName, "Times New Roman");

    m_font.CreatePointFontIndirect (&lf);
    return 0;
}

BOOL CSmoothView::DoQueryNewPalette ()
{
    //
    // This function is called when the application's main frame window
    // receives a WM_QUERYNEWPALETTE message.
    //
    if (m_palette.m_hObject == NULL)
        return 0;

    CClientDC dc (this);
    CPalette* pOldPalette = dc.SelectPalette (&m_palette, FALSE);

    UINT nCount;
    if (nCount = dc.RealizePalette ())
        Invalidate ();

    dc.SelectPalette (pOldPalette, FALSE);
    return nCount;
}

void CSmoothView::DoPaletteChanged ()
{
    //
    // This function is called when the application's main frame window
    // receives a WM_PALETTECHANGED message.
    //
    if (m_palette.m_hObject != NULL) {
        CClientDC dc (this);
        CPalette* pOldPalette = dc.SelectPalette (&m_palette, FALSE);

        if (dc.RealizePalette ())
            Invalidate ();

        dc.SelectPalette (pOldPalette, FALSE);
    }
}