The EX26A Example—A Data Object Clipboard

This example uses the CDib class from EX11C. Here you'll be able to move and resize the DIB image with a tracker rectangle, and you'll be able to copy and paste the DIB to and from the clipboard using a COM data object. The example also includes functions for reading DIBs from and writing DIBs to BMP files.

If you create such an example from scratch, use AppWizard without any ActiveX or Automation options and then add the following line in your StdAfx.h file:

#include <afxole.h>

Add the following call at the start of the application's InitInstance function:

AfxOleInit();

To prepare EX26A, open the \vcpp32\ex26a\ex26a.dsw workspace and then build the project. Run the application, and paste a bitmap into the rectangle by choosing Paste From on the Edit menu. You'll see an MDI application similar to the one shown in Figure 26-2.

Click to view at full size.

Figure 26-2. The EX26A program in operation.

The CMainFrame Class

This class contains the handlers OnQueryNewPalette and OnPaletteChanged for the WM_QUERYNEWPALETTE and WM_PALETTECHANGED messages, respectively. These handlers send a user-defined WM_VIEWPALETTECHANGED message to all the views, and then the handler calls CDib::UsePalette to realize the palette. The value of wParam tells the view whether it should realize the palette in background or foreground mode.

The CEx26aDoc Class

This class is pretty straightforward. It contains an embedded CDib object, m_dib, plus a Clear All command handler. The overridden DeleteContents member function calls the CDib::Empty function.

The CEx26aView Class

This class contains the clipboard function command handlers, the tracking code, the DIB drawing code, and the palette message handler. Figure 26-3 shows the header and implementation files with manually entered code in boldface.

EX26AVIEW.H

#if !defined(AFX_EX26AVIEW_H__4F329B0F_5DF1_11D0_848F_00400526305B
_ _INCLUDED_)
#define AFX_EX26AVIEW_H__4F329B0F_5DF1_11D0_848F_00400526305B
_ _INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif// _MSC_VER > 1000

#define WM_VIEWPALETTECHANGED  WM_USER + 5

class CEx26aView : public CScrollView
{
    // for tracking
    CRectTracker m_tracker;
    CRect m_rectTracker; // logical coordinates
    CSize m_sizeTotal;   // document size
protected: // create from serialization only
    CEx26aView();
    DECLARE_DYNCREATE(CEx26aView)

// Attributes
public:
    CEx26aDoc* GetDocument();
// Operations
public:

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CEx26aView)
    public:
    virtual void OnDraw(CDC* pDC);  // overridden to draw this view
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
    virtual void OnPrepareDC(CDC* pDC, CPrintInfo* pInfo = NULL);
    virtual void OnInitialUpdate();
    protected:
    virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
    virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
    virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
    //}}AFX_VIRTUAL

// Implementation
public:
    virtual ~CEx26aView();

#ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
#endif

protected:

// generated message map functions
protected:
    //{{AFX_MSG(CEx26aView)
    afx_msg void OnEditCopy();
    afx_msg void OnUpdateEditCopy(CCmdUI* pCmdUI);]
    afx_msg void OnEditCopyto();
    afx_msg void OnEditCut();
    afx_msg void OnEditPaste();
    afx_msg void OnUpdateEditPaste(CCmdUI* pCmdUI);
    afx_msg void OnEditPastefrom();
    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
    afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);
    afx_msg LONG OnViewPaletteChanged(UINT wParam, LONG lParam);
    afx_msg void OnSetFocus(CWnd* pOldWnd);
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
private:
    BOOL DoPasteDib(COleDataObject* pDataObject);
    COleDataSource* SaveDib();
};

#ifndef _DEBUG  // debug version in Ex26aView.cpp
inline CEx26aDoc* CEx26aView::GetDocument()
    { return (CEx26aDoc*)
m_pDocument; }
#endif

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

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations 
//  immediately before the previous line

#endif 
// !defined(AFX_EX26AVIEW_H__4F329B0F_5DF1_11D0_848F_00400526305B
__INCLUDED_)

EX26AVIEW.CPP
#include "stdafx.h"
#include "ex26a.h"

#include "cdib.h"
#include "ex26aDoc.h"
#include "ex26aView.h"

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

///////////////////////////////////////////////////////////////////////
// CEx26aView

IMPLEMENT_DYNCREATE(CEx26aView, CScrollView)

BEGIN_MESSAGE_MAP(CEx26aView, CScrollView)
    //{{AFX_MSG_MAP(CEx26aView)
    ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
    ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
    ON_COMMAND(ID_EDIT_COPYTO, OnEditCopyto)
    ON_COMMAND(ID_EDIT_CUT, OnEditCut)
    ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
    ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
    ON_COMMAND(ID_EDIT_PASTEFROM, OnEditPastefrom)
    ON_WM_LBUTTONDOWN()
    ON_WM_SETCURSOR()
    ON_MESSAGE(WM_VIEWPALETTECHANGED, OnViewPaletteChanged)
    ON_UPDATE_COMMAND_UI(ID_EDIT_COPYTO, OnUpdateEditCopy)
    ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCopy)
    ON_WM_SETFOCUS()
    //}}AFX_MSG_MAP
    // standard printing commands
    ON_COMMAND(ID_FILE_PRINT, CScrollView::OnFilePrint)
    ON_COMMAND(ID_FILE_PRINT_DIRECT, CScrollView::OnFilePrint)
    ON_COMMAND(ID_FILE_PRINT_PREVIEW, CScrollView::OnFilePrintPreview)
END_MESSAGE_MAP()

//////////////////////////////////////////////////////////////////////
// CEx26aView construction/destruction

CEx26aView::CEx26aView() : m_sizeTotal(800, 1050), // 8-by-10.5 inches
                                                   //  when printed
    m_rectTracker(50, 50, 250, 250)
{
}

CEx26aView::~CEx26aView()
{
}

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

    return CScrollView::PreCreateWindow(cs);
}

//////////////////////////////////////////////////////////////////////
// CEx26aView drawing

void CEx26aView::OnDraw(CDC* pDC)
{
    CDib& dib = GetDocument()->m_dib;
    m_tracker.m_rect = m_rectTracker;
    pDC->LPtoDP(m_tracker.m_rect); // tracker wants device coordinates
    m_tracker.Draw(pDC);
    dib.Draw(pDC, m_rectTracker.TopLeft(), 
m_rectTracker.Size());
}

//////////////////////////////////////////////////////////////////////
// CEx26aView printing

BOOL CEx26aView::OnPreparePrinting(CPrintInfo* pInfo)
{
    pInfo->SetMaxPage(1);
    return DoPreparePrinting(pInfo);
}

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

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

//////////////////////////////////////////////////////////////////////
// CEx26aView diagnostics

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

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

CEx26aDoc* CEx26aView::GetDocument() // nondebug version is inline
{
    ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CEx26aDoc)));
    return (CEx26aDoc*)m_pDocument;
}
#endif //_DEBUG

//////////////////////////////////////////////////////////////
// helper functions used for clipboard and drag-drop 

BOOL CEx26aView::DoPasteDib(COleDataObject* pDataObject)
{
    // update command user interface should keep us out of 
    //  here if not CF_DIB
    if (!pDataObject->IsDataAvailable(CF_DIB)) {
        TRACE("CF_DIB format is unavailable\n");
        return FALSE;
    }
    CEx26aDoc* pDoc = GetDocument();
    // Seems to be MOVEABLE memory, so we must use GlobalLock!
    //  (hDib != lpDib) GetGlobalData copies the memory, so we can
    //  hang onto it until we delete the CDib.
    HGLOBAL hDib = pDataObject->GetGlobalData(CF_DIB);
    ASSERT(hDib != NULL);
    LPVOID lpDib = ::GlobalLock(hDib);
    ASSERT(lpDib != NULL);
    pDoc->m_dib.AttachMemory(lpDib, TRUE, hDib);
    pDoc->SetModifiedFlag();
    pDoc->UpdateAllViews(NULL);
    return TRUE;
}

COleDataSource* CEx26aView::SaveDib()
{
    CDib& dib = GetDocument()->m_dib;
    if (dib.GetSizeImage() > 0) {
        COleDataSource* pSource = new COleDataSource();
        int nHeaderSize = dib.GetSizeHeader();
        int nImageSize = dib.GetSizeImage();
        HGLOBAL hHeader = ::GlobalAlloc(GMEM_SHARE,
            nHeaderSize + nImageSize);
        LPVOID pHeader = ::GlobalLock(hHeader);
        ASSERT(pHeader != NULL);
        LPVOID pImage = (LPBYTE) pHeader + nHeaderSize;
        memcpy(pHeader, dib.m_lpBMIH, nHeaderSize); 
        memcpy(pImage, dib.m_lpImage, nImageSize);
        // Receiver is supposed to free the global memory 
        ::GlobalUnlock(hHeader);
        pSource->CacheGlobalData(CF_DIB, hHeader);
        return pSource;
    }
    return NULL;
}


//////////////////////////////////////////////////////////////////////
// CEx26aView message handlers

void CEx26aView::OnEditCopy() 
{
    COleDataSource* pSource = SaveDib();
    if (pSource) {
        pSource->SetClipboard(); // OLE deletes data source
    }
}

void CEx26aView::OnUpdateEditCopy(CCmdUI* pCmdUI) 
{
    // serves Copy, Cut, and Copy To
    CDib& dib = GetDocument()->m_dib;
    pCmdUI->Enable(dib.GetSizeImage() > 0L);
}

void CEx26aView::OnEditCopyto()
{
    CDib& dib = GetDocument()->m_dib;
    CFileDialog dlg(FALSE, "bmp", "*.bmp");
    if (dlg.DoModal() != IDOK) return;

    BeginWaitCursor();
    dib.CopyToMapFile(dlg.GetPathName());
    EndWaitCursor();
}

void CEx26aView::OnEditCut() 
{
    OnEditCopy();
    GetDocument()->OnEditClearAll();
}
void CEx26aView::OnEditPaste() 
{
    CEx26aDoc* pDoc = GetDocument();
    COleDataObject dataObject;
    VERIFY(dataObject.AttachClipboard());
    DoPasteDib(&dataObject);
    CClientDC dc(this);
    pDoc->m_dib.UsePalette(&dc);
    pDoc->SetModifiedFlag();
    pDoc->UpdateAllViews(NULL);
}

void CEx26aView::OnUpdateEditPaste(CCmdUI* pCmdUI) 
{
    COleDataObject dataObject;
    BOOL bAvail = dataObject.AttachClipboard() &&
        dataObject.IsDataAvailable(CF_DIB);
    pCmdUI->Enable(bAvail);
}

void CEx26aView::OnEditPastefrom() 
{
    CEx26aDoc* pDoc = GetDocument();
    CFileDialog dlg(TRUE, "bmp", "*.bmp");
    if (dlg.DoModal() != IDOK) return;
    if (pDoc->m_dib.AttachMapFile(dlg.GetPathName(), TRUE)) { // share
        CClientDC dc(this);
        pDoc->m_dib.SetSystemPalette(&dc);
        pDoc->m_dib.UsePalette(&dc);
        pDoc->SetModifiedFlag();
        pDoc->UpdateAllViews(NULL);
    }
}

void CEx26aView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo) 
{
    // custom MM_LOENGLISH; positive y is down
    if (pDC->IsPrinting()) {
        int nHsize = pDC->GetDeviceCaps(HORZSIZE) * 1000 / 254;
        int nVsize = pDC->GetDeviceCaps(VERTSIZE) * 1000 / 254;
        pDC->SetMapMode(MM_ANISOTROPIC);
        pDC->SetWindowExt(nHsize, nVsize);
        pDC->SetViewportExt(pDC->GetDeviceCaps(HORZRES),
                            pDC->GetDeviceCaps(VERTRES));
    }
    else {
        CScrollView::OnPrepareDC(pDC, pInfo);
    }
}
void CEx26aView::OnInitialUpdate() 
{
    SetScrollSizes(MM_TEXT, m_sizeTotal);
    m_tracker.m_nStyle = CRectTracker::solidLine |
        CRectTracker::resizeOutside;
    CScrollView::OnInitialUpdate();
}

void CEx26aView::OnLButtonDown(UINT nFlags, CPoint point) 
{
    if (m_tracker.Track(this, point, FALSE, NULL)) {
        CClientDC dc(this);
        OnPrepareDC(&dc);
        m_rectTracker = m_tracker.m_rect;
        dc.DPtoLP(m_rectTracker); // Update logical coordinates
        Invalidate();
    }
}

BOOL CEx26aView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
    if (m_tracker.SetCursor(pWnd, nHitTest)) {
        return TRUE;
    }
    else {
        return CScrollView::OnSetCursor(pWnd, nHitTest, message);
    }
}

LONG CEx26aView::OnViewPaletteChanged(UINT wParam, LONG lParam) 
{
    TRACE("CEx26aView::OnViewPaletteChanged, HWND = %x,  \ 
        code = %d\n", GetSafeHwnd(), wParam);
    CClientDC dc(this);
    GetDocument()->m_dib.UsePalette(&dc, wParam);
    Invalidate();
    return 0;
}

void CEx26aView::OnSetFocus(CWnd* pOldWnd) 
{
    CScrollView::OnSetFocus(pOldWnd);
    AfxGetApp()->m_pMainWnd->SendMessage(WM_PALETTECHANGED, 
        (UINT) GetSafeHwnd());
}
Figure 26-3. The CEx26aView class listing.

Several interesting things happen in the view class. In the DoPasteDib helper, we can call GetGlobalData because we can attach the returned HGLOBAL variable to the document's CDib object. If we called GetData, we would have to copy the memory block ourselves. The Paste From and Copy To command handlers rely on the memory-mapped file support in the CDib class. The OnPrepareDC function creates a special printer-mapping mode that is just like MM_LOENGLISH except that positive y is down. One pixel on the display corresponds to 0.01 inch on the printer.