// This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 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" #ifndef _AFX_NO_PRINTING_SUPPORT BOOL CALLBACK _AfxPreviewCloseProc(CFrameWnd* pFrameWnd); ///////////////////////////////////////////////////////////////////////////// // CPrintPreviewState helper structure CPrintPreviewState::CPrintPreviewState() { // set defaults nIDMainPane = AFX_IDW_PANE_FIRST; dwStates = AFX_CONTROLBAR_MASK(AFX_IDW_STATUS_BAR); // status bar visible if available lpfnCloseProc = _AfxPreviewCloseProc; // set frame hook so closing the frame window // when in preview state will just end the mode hMenu = NULL; pViewActiveOld = NULL; hAccelTable = NULL; } ///////////////////////////////////////////////////////////////////////////// // CView's OnPrintPreview. Here to force linkage void CView::OnFilePrintPreview() { // In derived classes, implement special window handling here // Be sure to Unhook Frame Window close if hooked. // must not create this on the frame. Must outlive this function CPrintPreviewState* pState = new CPrintPreviewState; // DoPrintPreview's return value does not necessarily indicate that // Print preview succeeded or failed, but rather what actions are necessary // at this point. If DoPrintPreview returns TRUE, it means that // OnEndPrintPreview will be (or has already been) called and the // pState structure will be/has been deleted. // If DoPrintPreview returns FALSE, it means that OnEndPrintPreview // WILL NOT be called and that cleanup, including deleting pState // must be done here. if (!DoPrintPreview(AFX_IDD_PREVIEW_TOOLBAR, this, RUNTIME_CLASS(CPreviewView), pState)) { // In derived classes, reverse special window handling here for // Preview failure case TRACE(traceAppMsg, 0, "Error: DoPrintPreview failed.\n"); AfxMessageBox(AFX_IDP_COMMAND_FAILURE); delete pState; // preview failed to initialize, delete State now } } BOOL CView::DoPrintPreview(UINT nIDResource, CView* pPrintView, CRuntimeClass* pPreviewViewClass, CPrintPreviewState* pState) { ASSERT_VALID_IDR(nIDResource); ASSERT_VALID(pPrintView); ASSERT(pPreviewViewClass != NULL); ASSERT(pPreviewViewClass->IsDerivedFrom(RUNTIME_CLASS(CPreviewView))); ASSERT(pState != NULL); CWnd* pMainWnd = GetParentFrame(); if (DYNAMIC_DOWNCAST(CFrameWnd, pMainWnd) == NULL) { // if it's not a frame, we'll try the main window pMainWnd = AfxGetMainWnd(); } CFrameWnd* pParent = STATIC_DOWNCAST(CFrameWnd, pMainWnd); ASSERT_VALID(pParent); CCreateContext context; context.m_pCurrentFrame = pParent; context.m_pCurrentDoc = GetDocument(); context.m_pLastView = this; // Create the preview view object CPreviewView* pView = (CPreviewView*)pPreviewViewClass->CreateObject(); if (pView == NULL) { TRACE(traceAppMsg, 0, "Error: Failed to create preview view.\n"); return FALSE; } ASSERT_KINDOF(CPreviewView, pView); pView->m_pPreviewState = pState; // save pointer pParent->OnSetPreviewMode(TRUE, pState); // Take over Frame Window // Create the toolbar from the dialog resource pView->m_pToolBar = new CDialogBar; CFrameWnd *pParentFrame = pParent->GetActiveFrame(); ASSERT(pParentFrame); COleIPFrameWnd *pInPlaceFrame = DYNAMIC_DOWNCAST(COleIPFrameWnd, pParentFrame); if (pInPlaceFrame) { CDocument *pViewDoc = GetDocument(); COleServerDoc *pDoc = DYNAMIC_DOWNCAST(COleServerDoc, pViewDoc); if (!pDoc) { pParent->OnSetPreviewMode(FALSE, pState); // restore Frame Window delete pView->m_pToolBar; // not autodestruct yet pView->m_pToolBar = NULL; pView->m_pPreviewState = NULL; // do not delete state structure delete pView; return FALSE; } CFrameWnd *pFrame = (CFrameWnd*)pInPlaceFrame->GetDocFrame(); if (!pFrame) pFrame = pInPlaceFrame->GetMainFrame(); ASSERT(pFrame != NULL); // hide existing toolbars. pDoc->OnDocWindowActivate(FALSE); if ( pFrame && pView->m_pToolBar->Create(pFrame, ATL_MAKEINTRESOURCE(nIDResource),CBRS_TOP, AFX_IDW_PREVIEW_BAR) ) { // automatic cleanup pView->m_pToolBar->m_bAutoDelete = TRUE; // Tell the toolbar where to route command messages pView->m_pToolBar->SetInPlaceOwner(pInPlaceFrame); // now, merge the print preview toolbar into the // appropriate frame which would be the document // frame for an mdi container or the app frame for // an sdi container. CRect rcBorder; BOOL bFrame = FALSE; CComPtr spIPUIWindow; if (FAILED(pInPlaceFrame->GetInPlaceDocFrame(&spIPUIWindow))) { pInPlaceFrame->GetInPlaceFrame(&spIPUIWindow); bFrame = TRUE; } ASSERT(spIPUIWindow); if (spIPUIWindow) { spIPUIWindow->GetBorder(rcBorder); pDoc->OnResizeBorder(rcBorder, spIPUIWindow, bFrame); } pInPlaceFrame->SetPreviewMode(TRUE); } else { TRACE(traceAppMsg, 0, "Error: Preview could not create toolbar dialog.\n"); pParent->OnSetPreviewMode(FALSE, pState); // restore Frame Window delete pView->m_pToolBar; // not autodestruct yet pView->m_pToolBar = NULL; pView->m_pPreviewState = NULL; // do not delete state structure delete pView; return FALSE; } } else { if (!pView->m_pToolBar->Create(pParent, ATL_MAKEINTRESOURCE(nIDResource), CBRS_TOP, AFX_IDW_PREVIEW_BAR)) { TRACE(traceAppMsg, 0, "Error: Preview could not create toolbar dialog.\n"); pParent->OnSetPreviewMode(FALSE, pState); // restore Frame Window delete pView->m_pToolBar; // not autodestruct yet pView->m_pToolBar = NULL; pView->m_pPreviewState = NULL; // do not delete state structure delete pView; return FALSE; } pView->m_pToolBar->m_bAutoDelete = TRUE; // automatic cleanup } // Create the preview view as a child of the App Main Window. This // is a sibling of this view if this is an SDI app. This is NOT a sibling // if this is an MDI app. if (!pView->Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0,0,0,0), pParent, AFX_IDW_PANE_FIRST, &context)) { TRACE(traceAppMsg, 0, "Error: couldn't create preview view for frame.\n"); pParent->OnSetPreviewMode(FALSE, pState); // restore Frame Window pView->m_pPreviewState = NULL; // do not delete state structure delete pView; return FALSE; } // Preview window shown now pState->pViewActiveOld = pParent->GetActiveView(); CView* pActiveView = pParent->GetActiveFrame()->GetActiveView(); if (pActiveView != NULL) pActiveView->OnActivateView(FALSE, pActiveView, pActiveView); if (!pView->SetPrintView(pPrintView)) { pView->OnPreviewClose(); return TRUE; // signal that OnEndPrintPreview was called } pParent->SetActiveView(pView); // set active view - even for MDI pView->m_pToolBar->SendMessage(WM_IDLEUPDATECMDUI, (WPARAM)TRUE); pParent->RecalcLayout(); // position and size everything pParent->UpdateWindow(); return TRUE; } BOOL CALLBACK _AfxPreviewCloseProc(CFrameWnd* pFrameWnd) { ASSERT_VALID(pFrameWnd); CPreviewView* pView = (CPreviewView*) pFrameWnd->GetDlgItem(AFX_IDW_PANE_FIRST); ASSERT_KINDOF(CPreviewView, pView); pView->OnPreviewClose(); return FALSE; } ///////////////////////////////////////////////////////////////////////////// // Preview View BEGIN_MESSAGE_MAP(CPreviewView, CScrollView) //{{AFX_MSG_MAP(CPreviewView) ON_WM_SIZE() // overriding CScrollView ON_WM_CREATE() ON_COMMAND(AFX_ID_PREVIEW_CLOSE, &CPreviewView::OnPreviewClose) ON_COMMAND(AFX_ID_PREVIEW_NUMPAGE, &CPreviewView::OnNumPageChange) ON_COMMAND(AFX_ID_PREVIEW_NEXT, &CPreviewView::OnNextPage) ON_COMMAND(AFX_ID_PREVIEW_PREV, &CPreviewView::OnPrevPage) ON_COMMAND(AFX_ID_PREVIEW_PRINT, &CPreviewView::OnPreviewPrint) ON_COMMAND(AFX_ID_PREVIEW_ZOOMIN, &CPreviewView::OnZoomIn) ON_COMMAND(AFX_ID_PREVIEW_ZOOMOUT, &CPreviewView::OnZoomOut) ON_UPDATE_COMMAND_UI(AFX_ID_PREVIEW_NUMPAGE, &CPreviewView::OnUpdateNumPageChange) ON_UPDATE_COMMAND_UI(AFX_ID_PREVIEW_NEXT, &CPreviewView::OnUpdateNextPage) ON_UPDATE_COMMAND_UI(AFX_ID_PREVIEW_PREV, &CPreviewView::OnUpdatePrevPage) ON_UPDATE_COMMAND_UI(AFX_ID_PREVIEW_ZOOMIN, &CPreviewView::OnUpdateZoomIn) ON_UPDATE_COMMAND_UI(AFX_ID_PREVIEW_ZOOMOUT, &CPreviewView::OnUpdateZoomOut) ON_WM_VSCROLL() ON_WM_HSCROLL() ON_WM_LBUTTONDOWN() ON_WM_ERASEBKGND() ON_WM_SETCURSOR() //}}AFX_MSG_MAP END_MESSAGE_MAP() CPreviewView::CPreviewView() { m_pToolBar = NULL; m_pPrintView = NULL; m_pOrigView = NULL; m_pPreviewInfo = NULL; m_pPreviewDC = NULL; m_pPreviewState = NULL; m_hMagnifyCursor = NULL; m_bPageNumDisplayed = FALSE; m_nZoomState = ZOOM_OUT; // default to pointing to embedded array. Allows for 2 pages m_pPageInfo = m_pageInfoArray; m_nMaxPages = 2; // initialize CScrollView members m_bCenter = TRUE; // Center Zoomed output in Scrollview m_nMapMode = MM_TEXT; } CPreviewView::PAGE_INFO::PAGE_INFO() { } CPreviewView::~CPreviewView() { m_dcPrint.Detach(); // print DC is deleted by CPrintInfo destructor delete m_pPreviewInfo; // get rid of preview info delete m_pPreviewState; // Get rid of preview state delete m_pPreviewDC; // Get rid of preview DC object if (m_hMagnifyCursor != NULL) { // make sure that m_hMagnifyCursor isn't the current cursor when we destroy it ::SetCursor(::LoadCursor(NULL, IDC_ARROW)); DestroyCursor(m_hMagnifyCursor); } } int CPreviewView::OnCreate(LPCREATESTRUCT lpCreateStruct) { int retVal = CView::OnCreate(lpCreateStruct); if (retVal == -1) return -1; // if -1 bag out CCreateContext* pContext = (CCreateContext*)lpCreateStruct->lpCreateParams; m_pOrigView = pContext->m_pLastView; ASSERT(m_pOrigView != NULL); ASSERT_KINDOF(CView, m_pOrigView); return retVal; } BOOL CPreviewView::SetPrintView(CView* pPrintView) { ASSERT_VALID(pPrintView); m_pPrintView = pPrintView; // allocate preview info m_pPreviewInfo = new CPrintInfo; m_pPreviewInfo->m_pPD->SetHelpID(AFX_IDD_PRINTSETUP); m_pPreviewInfo->m_pPD->m_pd.Flags |= PD_PRINTSETUP; m_pPreviewInfo->m_pPD->m_pd.Flags &= ~PD_RETURNDC; m_pPreviewInfo->m_bPreview = TRUE; // signal that this is preview ASSERT(m_pPreviewInfo->m_pPD != NULL); m_pPreviewDC = new CPreviewDC; // must be created before any // possible error returns if (!m_pPrintView->OnPreparePrinting(m_pPreviewInfo)) return FALSE; #ifdef _DEBUG if (m_pPreviewInfo->m_pPD->m_pd.hDC == NULL) { TRACE(traceAppMsg, 0, "Error: hDC not set for printing --\n"); TRACE(traceAppMsg, 0, "\tDid you remember to call DoPreparePrinting?\n"); ASSERT(FALSE); // common mistake gets trapped here } #endif //_DEBUG m_dcPrint.Attach(m_pPreviewInfo->m_pPD->m_pd.hDC); m_pPreviewDC->SetAttribDC(m_pPreviewInfo->m_pPD->m_pd.hDC); m_pPreviewDC->m_bPrinting = TRUE; m_dcPrint.m_bPrinting = TRUE; m_dcPrint.SaveDC(); // Save pristine state of DC HDC hDC = ::GetDC(m_hWnd); m_pPreviewDC->SetOutputDC(hDC); m_pPrintView->OnBeginPrinting(m_pPreviewDC, m_pPreviewInfo); m_pPreviewDC->ReleaseOutputDC(); ::ReleaseDC(m_hWnd, hDC); m_dcPrint.RestoreDC(-1); // restore to untouched state // Get Pixels per inch from Printer m_sizePrinterPPI.cx = m_dcPrint.GetDeviceCaps(LOGPIXELSX); m_sizePrinterPPI.cy = m_dcPrint.GetDeviceCaps(LOGPIXELSY); m_nPages = m_pPreviewInfo->m_nNumPreviewPages; if (m_nPages == 0) m_nPages = 1; else if (m_nPages > m_nMaxPages) m_nPages = m_nMaxPages; // Sanity Check! m_nZoomOutPages = m_nPages; SetScrollSizes(MM_TEXT, CSize(1, 1)); // initialize mapping mode only if (m_pPreviewInfo->GetMaxPage() < 0x8000 && m_pPreviewInfo->GetMaxPage() - m_pPreviewInfo->GetMinPage() <= 32767U) { SCROLLINFO info; info.fMask = SIF_PAGE|SIF_RANGE; info.nMin = m_pPreviewInfo->GetMinPage(); info.nMax = m_pPreviewInfo->GetMaxPage(); info.nPage = 1; if (!SetScrollInfo(SB_VERT, &info, FALSE)) SetScrollRange(SB_VERT, info.nMin, info.nMax, FALSE); } else ShowScrollBar(SB_VERT, FALSE); // if no range specified, or too // large don't show SetCurrentPage(m_pPreviewInfo->m_nCurPage, TRUE); return TRUE; } void CPreviewView::OnSize(UINT nType, int cx, int cy) { // CScrollView handles everything if zoomed in. if (m_nZoomState == ZOOM_OUT) { // Force recalc of scale ratios on next draw for (UINT i = 0; i < m_nMaxPages; i++) m_pPageInfo[i].sizeScaleRatio.cx = 0; // zero scale ratios CView::OnSize(nType, cx, cy); // No scroll functionality } else { // adjust scroll size to size of page m_pageDev.cx = cx; m_pageDev.cy = cy; m_lineDev.cx = cx / 10; m_lineDev.cy = cy / 10; CScrollView::OnSize(nType, cx, cy); } } void CPreviewView::OnActivateView(BOOL bActivate, CView*, CView*) { if (bActivate) { CWnd* pFocusWnd = GetFocus(); if (pFocusWnd == NULL || (m_pToolBar != NULL && IsWindow(m_pToolBar->m_hWnd))) { if (!m_pToolBar->IsChild(pFocusWnd)) { // focus is not already on a toolbar button - set it to one m_pToolBar->GetDlgItem(AFX_ID_PREVIEW_PRINT)->SetFocus(); } } } } void CPreviewView::OnPreviewClose() { CWnd* pMainWnd = GetParentFrame(); if (DYNAMIC_DOWNCAST(CFrameWnd, pMainWnd) == NULL) { // if it's not a frame, we'll try the main window pMainWnd = AfxGetMainWnd(); } CFrameWnd* pParent = DYNAMIC_DOWNCAST(CFrameWnd, pMainWnd); ASSERT_VALID(pParent); while (m_pToolBar && m_pToolBar->m_pInPlaceOwner) { COleIPFrameWnd *pInPlaceFrame = DYNAMIC_DOWNCAST(COleIPFrameWnd, pParent); if (!pInPlaceFrame) break; CDocument *pViewDoc = GetDocument(); if (!pViewDoc) break; // in place items must have a server document. COleServerDoc *pDoc = DYNAMIC_DOWNCAST(COleServerDoc, pViewDoc); if (!pDoc) break; // destroy our toolbar m_pToolBar->DestroyWindow(); m_pToolBar = NULL; pInPlaceFrame->SetPreviewMode(FALSE); // restore toolbars pDoc->OnDocWindowActivate(TRUE); break; } if (m_pToolBar) m_pToolBar->DestroyWindow(); m_pToolBar = NULL; m_pPreviewInfo->m_nCurPage = m_nCurrentPage; m_pOrigView->OnEndPrintPreview(m_pPreviewDC, m_pPreviewInfo, CPoint(0, 0), this); } #define PREVIEW_MARGIN 8 #define PREVIEW_PAGEGAP 8 // Return is actually the fraction cx/cy. Simply using CSize for convenience CSize CPreviewView::CalcScaleRatio(CSize screenSize, CSize actualSize) { // Test ratio based on vertical dimension to see if it is the one to use int nNum = screenSize.cy; int nDen = actualSize.cy; // If scaled width too large, choose width as primary dimension if (MulDiv(actualSize.cx, nNum, nDen) > screenSize.cx) { // wrong ratio--base on width nNum = screenSize.cx; nDen = actualSize.cx; } CSize ratio(nNum, nDen); return ratio; } // Position Page... // Generate a Screen MM_TEXT rectangle to enclose each page. Dimensions // of the rectangle must be 1 pixel Above and Left of the top/left corner // of the page and the rectangle width and height must be THREE pixels // larger than page in order to provide the correct placement of the // two pixel border. // // This routine is called once for each page with the preview DC set up for // that page void CPreviewView::PositionPage(UINT nPage) { CSize windowSize = CalcPageDisplaySize(); VERIFY(m_dcPrint.Escape(GETPHYSPAGESIZE, 0, NULL, (LPVOID)&m_pPageInfo[nPage].sizeUnscaled)); CSize* pSize = &m_pPageInfo[nPage].sizeUnscaled; // Convert page size to screen coordinates pSize->cx = MulDiv(pSize->cx, afxData.cxPixelsPerInch, m_sizePrinterPPI.cx); pSize->cy = MulDiv(pSize->cy, afxData.cyPixelsPerInch, m_sizePrinterPPI.cy); m_pPageInfo[nPage].sizeZoomOutRatio = CalcScaleRatio(windowSize, *pSize); SetScaledSize(nPage); } CSize CPreviewView::CalcPageDisplaySize() // calculate the current page size // set 'm_nSecondPageOffset' to start of second page // return size of current page less margins { CSize windowSize, scrollSize; GetTrueClientSize(windowSize, scrollSize); // subtract out vertical scrollbar if zoomed out and page range is known // and there is more than one page. if (m_nZoomState == ZOOM_OUT && (m_pPreviewInfo->GetMaxPage() != 0xffff) && (m_pPreviewInfo->GetMaxPage() - m_pPreviewInfo->GetMinPage() != 0)) windowSize.cx -= scrollSize.cx; m_nSecondPageOffset = (windowSize.cx - PREVIEW_MARGIN) / 2; windowSize.cx = (m_nPages == 2) ? (windowSize.cx - 3*PREVIEW_MARGIN) / 2 : windowSize.cx - 2*PREVIEW_MARGIN; windowSize.cy -= 2*PREVIEW_MARGIN; return windowSize; } void CPreviewView::SetScaledSize(UINT nPage) { CSize* pSize = &m_pPageInfo[nPage].sizeUnscaled; CSize* pRatio = &m_pPageInfo[nPage].sizeScaleRatio; CSize* pZoomOutRatio = &m_pPageInfo[nPage].sizeZoomOutRatio; CSize windowSize = CalcPageDisplaySize(); BOOL bPaperLarger = pZoomOutRatio->cx < pZoomOutRatio->cy; // whether the paper is larger than the screen, or vice versa switch (m_nZoomState) { case ZOOM_OUT: *pRatio = *pZoomOutRatio; break; case ZOOM_MIDDLE: // the middle zoom state is a ratio between cx/cy and // 1/1 (or cy/cy). It is, therefore: // // (cx + cy)/2 // ----------- // cy // // if the paper is larger than the screen, or // // (3*cx - cy)/2 // ------------- // cy // // if the paper is smaller than the screen. if (bPaperLarger) { pRatio->cy = pZoomOutRatio->cy; pRatio->cx = (pZoomOutRatio->cx + pRatio->cy) / 2; } else { pRatio->cy = pZoomOutRatio->cy; pRatio->cx = (3*pZoomOutRatio->cx - pRatio->cy) / 2; } break; case ZOOM_IN: if (bPaperLarger) pRatio->cx = pRatio->cy = 1; else { // if the paper is smaller than the screen space we're displaying // it in, then using a ratio of 1/1 will result in a smaller image // on the screen, not a larger one. To get a larger image in this // case we double the zoom out ratio. pRatio->cy = pZoomOutRatio->cy; pRatio->cx = 2*pZoomOutRatio->cx - pZoomOutRatio->cy; } break; default: ASSERT(FALSE); } // Convert to scaled size CSize scaledSize; scaledSize.cx = MulDiv(pSize->cx, pRatio->cx, pRatio->cy); scaledSize.cy = MulDiv(pSize->cy, pRatio->cx, pRatio->cy); CRect* pRect = &m_pPageInfo[nPage].rectScreen; pRect->SetRect(PREVIEW_MARGIN, PREVIEW_MARGIN, scaledSize.cx + PREVIEW_MARGIN + 3, scaledSize.cy + PREVIEW_MARGIN + 3); if (m_nZoomState == ZOOM_OUT) { pRect->OffsetRect((windowSize.cx - pRect->Size().cx) / 2 - 1, (windowSize.cy - pRect->Size().cy) / 2 - 1); if (nPage == 1) pRect->OffsetRect(m_nSecondPageOffset, 0); } else { // set up scroll size SetScrollSizes(MM_TEXT, pRect->Size() + CSize(PREVIEW_MARGIN * 2, PREVIEW_MARGIN * 2), windowSize); } } // Only use the PrepareDC from CScrollView if we are zoomed in void CPreviewView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo) { ASSERT_VALID(pDC); if (m_nZoomState == ZOOM_OUT) CView::OnPrepareDC(pDC, pInfo); else if (m_pPageInfo[0].sizeScaleRatio.cx != 0) CScrollView::OnPrepareDC(pDC, pInfo); } BOOL CPreviewView::OnEraseBkgnd(CDC* pDC) { ASSERT_VALID(pDC); // Fill background with APPWORKSPACE CBrush backBrush(GetSysColor(COLOR_APPWORKSPACE)); CBrush* pOldBrush = pDC->SelectObject(&backBrush); CRect rect; pDC->GetClipBox(&rect); // Erase the area needed pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY); pDC->SelectObject(pOldBrush); return TRUE; } void CPreviewView::OnDraw(CDC* pDC) { ASSERT_VALID(pDC); // don't do anything if not fully initialized if (m_pPrintView == NULL || m_dcPrint.m_hDC == NULL) return; CPoint ViewportOrg = pDC->GetViewportOrg(); CPen rectPen; rectPen.CreatePen(PS_SOLID, 2, GetSysColor(COLOR_WINDOWFRAME)); CPen shadowPen; shadowPen.CreatePen(PS_SOLID, 3, GetSysColor(COLOR_BTNSHADOW)); m_pPreviewInfo->m_bContinuePrinting = TRUE; // do this once each paint for (UINT nPage = 0; nPage < m_nPages; nPage++) { int nSavedState = m_dcPrint.SaveDC(); // Save pristine state of DC // Use paint DC for print preview output m_pPreviewDC->SetOutputDC(pDC->GetSafeHdc()); m_pPreviewInfo->m_nCurPage = m_nCurrentPage + nPage; // Only call PrepareDC if within page range, otherwise use default // rect to draw page rectangle if (m_nCurrentPage + nPage >= m_nCurrentPage && m_nCurrentPage + nPage >= nPage && m_nCurrentPage + nPage <= m_pPreviewInfo->GetMaxPage()) { m_pPrintView->OnPrepareDC(m_pPreviewDC, m_pPreviewInfo); } // Set up drawing rect to entire page (in logical coordinates) m_pPreviewInfo->m_rectDraw.SetRect(0, 0, m_pPreviewDC->GetDeviceCaps(HORZRES), m_pPreviewDC->GetDeviceCaps(VERTRES)); m_pPreviewDC->DPtoLP(&m_pPreviewInfo->m_rectDraw); // Draw empty page on screen pDC->SaveDC(); // save the output dc state CSize* pRatio = &m_pPageInfo[nPage].sizeScaleRatio; CRect* pRect = &m_pPageInfo[nPage].rectScreen; if (pRatio->cx == 0) { // page position has not been determined PositionPage(nPage); // compute page position if (m_nZoomState != ZOOM_OUT) { ViewportOrg = -GetDeviceScrollPosition(); if (m_bCenter) { CRect rect; GetClientRect(&rect); // if client area is larger than total device size, // override scroll positions to place origin such that // output is centered in the window if (m_totalDev.cx < rect.Width()) ViewportOrg.x = (rect.Width() - m_totalDev.cx) / 2; if (m_totalDev.cy < rect.Height()) ViewportOrg.y = (rect.Height() - m_totalDev.cy) / 2; } } } pDC->SetMapMode(MM_TEXT); // Page Rectangle is in screen device coords pDC->SetViewportOrg(ViewportOrg); pDC->SetWindowOrg(0, 0); pDC->SelectStockObject(HOLLOW_BRUSH); pDC->SelectObject(&rectPen); pDC->Rectangle(pRect); pDC->SelectObject(&shadowPen); pDC->MoveTo(pRect->right + 1, pRect->top + 3); pDC->LineTo(pRect->right + 1, pRect->bottom + 1); pDC->MoveTo(pRect->left + 3, pRect->bottom + 1); pDC->LineTo(pRect->right + 1, pRect->bottom + 1); // erase background to white (most paper is white) CRect rectFill = *pRect; rectFill.left += 1; rectFill.top += 1; rectFill.right -= 2; rectFill.bottom -= 2; ::FillRect(pDC->m_hDC, rectFill, (HBRUSH)GetStockObject(WHITE_BRUSH)); pDC->RestoreDC(-1); // restore to synchronized state if (!m_pPreviewInfo->m_bContinuePrinting || m_nCurrentPage + nPage > m_pPreviewInfo->GetMaxPage()) { m_pPreviewDC->ReleaseOutputDC(); m_dcPrint.RestoreDC(nSavedState); // restore to untouched state // if the first page is not displayable, back up one page // but never go below 1 if (nPage == 0 && m_nCurrentPage > 1) SetCurrentPage(m_nCurrentPage - 1, TRUE); break; } // Display page number OnDisplayPageNumber(m_nCurrentPage, nPage + 1); // Set scale ratio for this page m_pPreviewDC->SetScaleRatio(pRatio->cx, pRatio->cy); CSize PrintOffset; VERIFY(m_pPreviewDC->Escape(GETPRINTINGOFFSET, 0, NULL, (LPVOID)&PrintOffset)); m_pPreviewDC->PrinterDPtoScreenDP((LPPOINT)&PrintOffset); PrintOffset += (CSize)pRect->TopLeft(); PrintOffset += CSize(1, 1); PrintOffset += (CSize)ViewportOrg; // For Scrolling m_pPreviewDC->SetTopLeftOffset(PrintOffset); m_pPreviewDC->ClipToPage(); m_pPrintView->OnPrint(m_pPreviewDC, m_pPreviewInfo); m_pPreviewDC->ReleaseOutputDC(); m_dcPrint.RestoreDC(nSavedState); // restore to untouched state } rectPen.DeleteObject(); shadowPen.DeleteObject(); } void CPreviewView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { if (m_nZoomState != ZOOM_OUT) CScrollView::OnHScroll(nSBCode, nPos, pScrollBar); } void CPreviewView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { if (m_nZoomState != ZOOM_OUT) { CScrollView::OnVScroll(nSBCode, nPos, pScrollBar); return; } switch (nSBCode) { case SB_BOTTOM: SetCurrentPage(m_pPreviewInfo->GetMaxPage(), TRUE); break; case SB_TOP: SetCurrentPage(m_pPreviewInfo->GetMinPage(), TRUE); break; case SB_PAGEDOWN: SetCurrentPage(m_nCurrentPage + (m_pPreviewInfo->GetMaxPage() - m_pPreviewInfo->GetMinPage() + 9) / 10, TRUE); break; case SB_PAGEUP: SetCurrentPage(m_nCurrentPage - (m_pPreviewInfo->GetMaxPage() - m_pPreviewInfo->GetMinPage() + 9) / 10, TRUE); break; case SB_LINEDOWN: SetCurrentPage(m_nCurrentPage + 1, TRUE); break; case SB_LINEUP: SetCurrentPage(m_nCurrentPage - 1, TRUE); break; case SB_THUMBPOSITION: SetCurrentPage(nPos, TRUE); break; } } void CPreviewView::OnNumPageChange() { ASSERT(m_nPages == 1 || m_nPages == 2); m_nPages = 3 - m_nPages; // Toggle between 1 and 2 AfxGetApp()->m_nNumPreviewPages = m_nPages; m_nZoomOutPages = m_nPages; // Just do this to set the status correctly and invalidate SetCurrentPage(m_nCurrentPage, TRUE); } void CPreviewView::OnNextPage() { SetCurrentPage(m_nCurrentPage + 1, TRUE); } void CPreviewView::OnPrevPage() { SetCurrentPage(m_nCurrentPage - 1, TRUE); } void CPreviewView::OnPreviewPrint() { // cause print (can be overridden by catching the command) CFrameWnd *pOrigFrame = m_pOrigView->EnsureParentFrame(); OnPreviewClose(); CWinThread *pThread = AfxGetThread(); ASSERT( pThread ); CWnd *pMainWnd = pThread->m_pMainWnd; ASSERT_VALID(pMainWnd); COleIPFrameWnd *pInPlaceFrame = DYNAMIC_DOWNCAST(COleIPFrameWnd, pOrigFrame); if (pInPlaceFrame) { pInPlaceFrame->SendMessage(WM_COMMAND, ID_FILE_PRINT); } else { // ensure we print the correct frame pOrigFrame->SendMessage(WM_COMMAND, ID_FILE_PRINT); } } // Finds page pointed to and convert to 1:1 screen device units BOOL CPreviewView::FindPageRect(CPoint& point, UINT& nPage) { if (m_nZoomState != ZOOM_OUT) point += (CSize)GetDeviceScrollPosition(); for (nPage = 0; nPage < m_nPages; nPage++) { if (m_pPageInfo[nPage].rectScreen.PtInRect(point)) { // adjust point for page position point -= (CSize)m_pPageInfo[nPage].rectScreen.TopLeft(); // convert to 1:1 point.x = MulDiv(point.x, m_pPageInfo[nPage].sizeScaleRatio.cy, m_pPageInfo[nPage].sizeScaleRatio.cx); point.y = MulDiv(point.y, m_pPageInfo[nPage].sizeScaleRatio.cy, m_pPageInfo[nPage].sizeScaleRatio.cx); return TRUE; } } return FALSE; } void CPreviewView::OnLButtonDown(UINT, CPoint point) { UINT nPage; if (!FindPageRect(point, nPage)) return; // Didn't click on a page // Set new zoom state SetZoomState((m_nZoomState == ZOOM_IN) ? ZOOM_OUT : m_nZoomState + 1, nPage, point); } void CPreviewView::SetZoomState(UINT nNewState, UINT nPage, CPoint point) { if (m_nZoomState != nNewState) { m_nZoomState = nNewState; DoZoom(nPage, point); } } void CPreviewView::OnZoomIn() { if (m_nZoomState != ZOOM_IN) SetZoomState(m_nZoomState + 1, 0, CPoint(0, 0)); } void CPreviewView::OnZoomOut() { if (m_nZoomState != ZOOM_OUT) SetZoomState(m_nZoomState - 1, 0, CPoint(0, 0)); } // Actual zoom code. void CPreviewView::DoZoom(UINT nPage, CPoint point) { if (m_nZoomState == ZOOM_OUT) { // taking over scroll bars m_nPages = m_nZoomOutPages; ShowScrollBar(SB_HORZ, FALSE); //hide the horizontal bar BOOL bShowBar = m_pPreviewInfo->GetMaxPage() < 0x8000 && m_pPreviewInfo->GetMaxPage() - m_pPreviewInfo->GetMinPage() <= 32767U; ShowScrollBar(SB_VERT, bShowBar); //Show the vertical bar if (bShowBar) { SCROLLINFO info; info.fMask = SIF_PAGE|SIF_RANGE; info.nMin = m_pPreviewInfo->GetMinPage(); info.nMax = m_pPreviewInfo->GetMaxPage(); info.nPage = 1; if (!SetScrollInfo(SB_VERT, &info, FALSE)) SetScrollRange(SB_VERT, info.nMin, info.nMax, FALSE); } SetCurrentPage(m_nCurrentPage, TRUE); } else { m_nPages = 1; // only one page in zoomed states m_pPageInfo[0].sizeZoomOutRatio = m_pPageInfo[nPage].sizeZoomOutRatio; m_pPageInfo[0].sizeUnscaled = m_pPageInfo[nPage].sizeUnscaled; // Sets the printer page SetCurrentPage(m_nCurrentPage + nPage, FALSE); SetScaledSize(0); CSize* pRatio = &m_pPageInfo[nPage].sizeScaleRatio; // convert Hit Point from screen 1:1 point.x = MulDiv(point.x, pRatio->cx, pRatio->cy); point.y = MulDiv(point.y, pRatio->cx, pRatio->cy); // Adjust point for page position point += (CSize)m_pPageInfo[0].rectScreen.TopLeft(); // Scroll to center CenterOnPoint(point); } } void CPreviewView::SetCurrentPage(UINT nPage, BOOL bClearRatios) { m_nCurrentPage = nPage; if (m_nCurrentPage > m_pPreviewInfo->GetMaxPage()) m_nCurrentPage = m_pPreviewInfo->GetMaxPage(); if (m_nCurrentPage < m_pPreviewInfo->GetMinPage()) m_nCurrentPage = m_pPreviewInfo->GetMinPage(); if (m_nZoomState == ZOOM_OUT) SetScrollPos(SB_VERT, m_nCurrentPage); if (bClearRatios) { // Force Recalc of layout for (UINT i = 0; i < m_nMaxPages; i++) m_pPageInfo[i].sizeScaleRatio.cx = 0; // zero scale ratios } Invalidate(TRUE); } void CPreviewView::OnDisplayPageNumber(UINT nPage, UINT nPagesDisplayed) { UINT nEndPage = nPage + nPagesDisplayed - 1; CWinThread *pThread = AfxGetThread(); ASSERT(pThread); CFrameWnd* pParent = (CFrameWnd*)pThread->m_pMainWnd; ASSERT_VALID(pParent); ASSERT_KINDOF(CFrameWnd, pParent); int nSubString = (nPagesDisplayed == 1) ? 0 : 1; CString s; BOOL bOK = AfxExtractSubString(s, m_pPreviewInfo->m_strPageDesc, nSubString); if (bOK) { TCHAR szBuf[80]; int nResult; if (nSubString == 0) { nResult = _stprintf_s(szBuf, _countof(szBuf), s, nPage); } else { nResult = _stprintf_s(szBuf, _countof(szBuf), s, nPage, nEndPage); } if( nResult > 0 ) { pParent->SendMessage(WM_SETMESSAGESTRING, 0, (LPARAM)(LPVOID)szBuf); } else { bOK = FALSE; } } if(!bOK) { TRACE(traceAppMsg, 0, "Malformed Page Description string. Could not get string %d.\n", nSubString); } } void CPreviewView::OnUpdateNumPageChange(CCmdUI* pCmdUI) { // set text of button to opposite of current state CString text; UINT nPages = m_nZoomState == ZOOM_OUT ? m_nPages : m_nZoomOutPages; VERIFY(text.LoadString(nPages == 1 ? AFX_IDS_TWOPAGE : AFX_IDS_ONEPAGE)); pCmdUI->SetText(text); // enable it only if valid to display another page and not zoomed pCmdUI->Enable(m_nZoomState == ZOOM_OUT && m_nMaxPages != 1 && (m_pPreviewInfo->GetMaxPage() > 1 || m_nPages > 1)); } void CPreviewView::OnUpdateNextPage(CCmdUI* pCmdUI) { // enable if not showing last page pCmdUI->Enable(m_nCurrentPage+m_nPages-1 < m_pPreviewInfo->GetMaxPage()); } void CPreviewView::OnUpdatePrevPage(CCmdUI* pCmdUI) { // enable if not showing First page pCmdUI->Enable(m_nCurrentPage > m_pPreviewInfo->GetMinPage()); } void CPreviewView::OnUpdateZoomIn(CCmdUI* pCmdUI) { pCmdUI->Enable(m_nZoomState != ZOOM_IN); } void CPreviewView::OnUpdateZoomOut(CCmdUI* pCmdUI) { pCmdUI->Enable(m_nZoomState != ZOOM_OUT); } BOOL CPreviewView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { if (nHitTest != HTCLIENT) return CScrollView::OnSetCursor(pWnd, nHitTest, message); CPoint point; ::GetCursorPos(&point); ScreenToClient(&point); // client coordinates of mouse position UINT nPage; if (m_nZoomState != ZOOM_IN && FindPageRect(point, nPage)) { // On a page and not zoomed all the way in if (m_hMagnifyCursor == NULL) { HINSTANCE hInst = AfxFindResourceHandle( ATL_MAKEINTRESOURCE(AFX_IDC_MAGNIFY), ATL_RT_GROUP_CURSOR); m_hMagnifyCursor = ::LoadCursor(hInst, ATL_MAKEINTRESOURCE(AFX_IDC_MAGNIFY)); } ::SetCursor(m_hMagnifyCursor); } else { ::SetCursor(::LoadCursor(NULL, IDC_ARROW)); } return 0; } ///////////////////////////////////////////////////////////////////////////// // CPreviewView diagnostics #ifdef _DEBUG void CPreviewView::AssertValid() const { CView::AssertValid(); ASSERT_VALID(&m_dcPrint); if (m_pPreviewDC != NULL) ASSERT_VALID(m_pPreviewDC); switch (m_nZoomState) { case ZOOM_OUT: case ZOOM_IN: case ZOOM_MIDDLE: break; default: ASSERT(FALSE); // unknown zoom state } switch (m_nMapMode) { case MM_TEXT: case MM_LOMETRIC: case MM_HIMETRIC: case MM_LOENGLISH: case MM_HIENGLISH: case MM_TWIPS: case MM_ISOTROPIC: case MM_ANISOTROPIC: break; default: ASSERT(FALSE); // unknown mapping mode } } void CPreviewView::Dump(CDumpContext& dc) const { CView::Dump(dc); dc << "m_pPrintView = " << m_pPrintView; dc << "\nm_pOrigView = " << m_pOrigView; dc << "\nm_bPageNumDisplayed = " << m_bPageNumDisplayed; dc << "\nm_bCenter = " << m_bCenter; dc << "\nm_nPages = " << m_nPages; dc << "\nm_nCurrentPage " << m_nCurrentPage; dc << "\nm_nSecondPageOffset " << m_nSecondPageOffset; dc << "\nm_nMaxPages = " << m_nMaxPages; dc << "\nm_sizePrinterPPI = " << m_sizePrinterPPI; dc << "\nm_ptCenterPoint = " << m_ptCenterPoint; dc << "\nm_nZoomState = "; switch (m_nZoomState) { case ZOOM_OUT: dc << "ZOOM_OUT"; break; case ZOOM_IN: dc << "ZOOM_IN"; break; case ZOOM_MIDDLE: dc << "ZOOM_MIDDLE"; break; default: dc << "*unknown*"; break; } dc << "\nm_nMapMode = "; switch (m_nMapMode) { case MM_TEXT: dc << "MM_TEXT"; break; case MM_LOMETRIC: dc << "MM_LOMETRIC"; break; case MM_HIMETRIC: dc << "MM_HIMETRIC"; break; case MM_LOENGLISH: dc << "MM_LOENGLISH"; break; case MM_HIENGLISH: dc << "MM_HIENGLISH"; break; case MM_TWIPS: dc << "MM_TWIPS"; break; case MM_ISOTROPIC: dc << "MM_ISOTROPIC"; break; case MM_ANISOTROPIC: dc << "MM_ANISOTROPIC"; break; default: dc << "*unknown*"; break; } dc << "\nm_dcPrint = " << &m_dcPrint; dc << "\nm_pPreviewDC = " << m_pPreviewDC; dc << "\n"; } #endif //_DEBUG IMPLEMENT_DYNCREATE(CPreviewView, CScrollView) ///////////////////////////////////////////////////////////////////////////// #endif // !_AFX_NO_PRINTING_SUPPORT