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.
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()); } |
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.