// mainview.cpp : implementation of the CMainView class // // This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992-1998 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 "oclient.h" #include "maindoc.h" #include "mainview.h" #include "rectitem.h" #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CMainView CBrush NEAR CMainView::m_brHatch; CLIPFORMAT CMainView::m_cfObjectDescriptor=NULL; IMPLEMENT_DYNCREATE(CMainView, CScrollView) BEGIN_MESSAGE_MAP(CMainView, CScrollView) //{{AFX_MSG_MAP(CMainView) ON_COMMAND(ID_EDIT_PASTE, OnPaste) ON_COMMAND(ID_EDIT_PASTE_LINK, OnPasteLink) ON_COMMAND(ID_OLE_INSERT_NEW, OnInsertObject) ON_UPDATE_COMMAND_UI(ID_EDIT_CLEAR, OnUpdateEditMenu) ON_COMMAND(ID_EDIT_CLEAR, OnEditClear) ON_COMMAND(ID_EDIT_COPY, OnEditCopy) ON_COMMAND(ID_EDIT_CUT, OnEditCut) ON_WM_LBUTTONDBLCLK() ON_WM_LBUTTONDOWN() ON_WM_SETCURSOR() ON_WM_RBUTTONDOWN() ON_WM_CHAR() ON_WM_SETFOCUS() ON_WM_CREATE() ON_WM_SIZE() ON_COMMAND(ID_OBJECT_DISPLAYCONTENT, OnObjectDisplayContent) ON_UPDATE_COMMAND_UI(ID_OBJECT_DISPLAYCONTENT, OnUpdateObjectDisplayContent) ON_COMMAND(ID_OBJECT_DISPLAYASICON, OnObjectDisplayAsIcon) ON_UPDATE_COMMAND_UI(ID_OBJECT_DISPLAYASICON, OnUpdateObjectDisplayAsIcon) ON_COMMAND(ID_EDIT_PASTE_SPECIAL, OnPasteSpecial) ON_UPDATE_COMMAND_UI(ID_EDIT_CLONE, OnUpdateEditClone) ON_COMMAND(ID_EDIT_CLONE, OnEditClone) ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE_SPECIAL, OnUpdateEditPaste) ON_COMMAND(ID_OBJECT_RESETSIZE, OnObjectResetsize) ON_COMMAND(ID_CANCEL_INPLACE, OnCancelInplace) ON_WM_DESTROY() ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste) ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditMenu) ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditMenu) ON_UPDATE_COMMAND_UI(ID_OBJECT_RESETSIZE, OnUpdateEditMenu) ON_COMMAND(ID_OLE_CHANGE_SOURCE, OnOleChangeSource) ON_UPDATE_COMMAND_UI(ID_OLE_CHANGE_SOURCE, OnUpdateOleChangeSource) ON_COMMAND(ID_OLE_EDIT_PROPERTIES, OnOleEditProperties) ON_UPDATE_COMMAND_UI(ID_OLE_EDIT_PROPERTIES, OnUpdateOleEditProperties) //}}AFX_MSG_MAP // Standard printing commands ON_COMMAND(ID_FILE_PRINT, CScrollView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CScrollView::OnFilePrintPreview) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CMainView construction/destruction CMainView::CMainView() { if (m_brHatch.m_hObject == NULL) m_brHatch.CreateHatchBrush(HS_DIAGCROSS, RGB(0,0,0)); if (m_cfObjectDescriptor == NULL) m_cfObjectDescriptor = (CLIPFORMAT)::RegisterClipboardFormat(_T("Object Descriptor")); m_pSelection = NULL; m_prevDropEffect = DROPEFFECT_NONE; m_bInDrag = FALSE; } CMainView::~CMainView() { } void CMainView::OnInitialUpdate() { CScrollView::OnInitialUpdate(); // We can't pass MM_ANISOTROPIC to SetScrollSizes so we have to convert to MM_TEXT CSize size = GetDocument()->GetDocumentSize(); CClientDC dc(NULL); size.cx = MulDiv(size.cx, dc.GetDeviceCaps(LOGPIXELSX), 100); size.cy = MulDiv(size.cy, dc.GetDeviceCaps(LOGPIXELSY), 100); SetScrollSizes(MM_TEXT, size); } ///////////////////////////////////////////////////////////////////////////// // CMainView drawing void CMainView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo) { CScrollView::OnPrepareDC(pDC, pInfo); // set up a reasonable default context pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT)); pDC->SetBkColor(::GetSysColor(COLOR_WINDOW)); // LOENGLISH units are based on physical inches // We want logical inches so we have to do it differently pDC->SetMapMode(MM_ANISOTROPIC); pDC->SetViewportExt( pDC->GetDeviceCaps(LOGPIXELSX), pDC->GetDeviceCaps(LOGPIXELSY)); pDC->SetWindowExt(100,-100); } void CMainView::SetupTracker(CRectTracker* pTracker, CRectItem* pItem, CRect* pTrueRect) { ASSERT(pTracker != NULL); ASSERT(pItem != NULL); pTracker->m_rect = pItem->GetRect(); DocToClient(pTracker->m_rect); // set minimum size for our OLE items pTracker->m_sizeMin.cx = 8; pTracker->m_sizeMin.cy = 8; pTracker->m_nStyle = 0; // setup resize handles if item is selected if (pItem == m_pSelection) pTracker->m_nStyle |= CRectTracker::resizeInside; // put correct border depending on item type if (pItem->GetType() == OT_LINK) pTracker->m_nStyle |= CRectTracker::dottedLine; else pTracker->m_nStyle |= CRectTracker::solidLine; // put hatching over the item if it is currently open if (pItem->GetItemState() == COleClientItem::openState || pItem->GetItemState() == COleClientItem::activeUIState) { pTracker->m_nStyle |= CRectTracker::hatchInside; } if (pTrueRect != NULL) pTracker->GetTrueRect(pTrueRect); } void CMainView::OnDraw(CDC* pDC) { CMainDoc* pDoc = GetDocument(); ASSERT_VALID(pDC); if (!pDC->IsPrinting()) { m_brHatch.UnrealizeObject(); CPoint point(0, 0); pDC->LPtoDP(&point); pDC->SetBrushOrg(point.x % 8, point.y % 8); CRect rcClip; GetClientRect(&rcClip); ClientToDoc(rcClip); CSize docSize = pDoc->GetDocumentSize(); if (rcClip.right > docSize.cx) { CRect rcFill(rcClip); rcFill.left = max(rcFill.left,docSize.cx); pDC->FillRect(rcFill,&m_brHatch); } if (rcClip.bottom < -docSize.cy) { CRect rcFill(rcClip); rcFill.top = min(rcFill.top, -docSize.cy); pDC->FillRect(rcFill,&m_brHatch); } } // Draw all the CRectItems POSITION pos = pDoc->GetStartPosition(); while (pos != NULL) { CRectItem* pItem = DYNAMIC_DOWNCAST(CRectItem, pDoc->GetNextItem(pos)); if (pItem != NULL) { pItem->Draw(pDC, pItem->GetRect()); if (!pDC->IsPrinting()) { // draw the tracker CRectTracker tracker; CRect rectTrue; SetupTracker(&tracker, pItem, &rectTrue); ClientToDoc(rectTrue); if (pDC->RectVisible(&rectTrue)) tracker.Draw(pDC); } } } } // pHint is the deleted item or NULL if deselect/delete all void CMainView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) { if (pHint == NULL && lHint == 0) { // some sort of clear all m_pSelection = NULL; } if (pHint != NULL && pHint->IsKindOf(RUNTIME_CLASS(CRectItem))) { // just invalidate the one item InvalidateItem((CRectItem*)pHint); // clear selection if pointing to deleted item if (lHint == 1 && pHint == m_pSelection) { // specific case of pHint being deleted m_pSelection = NULL; } } else if (lHint != 0) { // invalidate arbitrary rectangle InvalidateRect((CRect*)lHint); } else { // complete update CScrollView::OnUpdate(pSender, lHint, pHint); } } void CMainView::InvalidateItem(CRectItem* pItem) { if (m_nMapMode != 0) { CRectTracker tracker; CRect rect; SetupTracker(&tracker, pItem, &rect); InvalidateRect(&rect); } } BOOL CMainView::OnScrollBy(CSize sizeScroll, BOOL bDoScroll) { // remove drag/drop feedback before scrolling if (bDoScroll && m_prevDropEffect != DROPEFFECT_NONE) { CClientDC dc(this); dc.DrawFocusRect(CRect(m_dragPoint, m_dragSize)); // erase previous focus rect m_prevDropEffect = DROPEFFECT_NONE; } // do the scroll if (!CScrollView::OnScrollBy(sizeScroll, bDoScroll)) return FALSE; // update the position of any in-place active item if (bDoScroll) { UpdateActiveItem(); UpdateWindow(); } return TRUE; } ///////////////////////////////////////////////////////////////////////////// // CMainView printing BOOL CMainView::OnPreparePrinting(CPrintInfo* pInfo) { // default preparation return DoPreparePrinting(pInfo); } ///////////////////////////////////////////////////////////////////////////// // Selection support BOOL CMainView::IsSelected(const CObject* pDocItem) const { return (pDocItem == m_pSelection); } void CMainView::SetSelection(CRectItem* pNewSel, BOOL bSafeSelect) { if (pNewSel != NULL && pNewSel == m_pSelection) return; // deactivate any in-place active item on this view! COleClientItem* pActiveItem = GetDocument()->GetInPlaceActiveItem(this); if (pActiveItem != NULL && pNewSel != pActiveItem) { if (bSafeSelect) return; // if we found one, deactivate it pActiveItem->Close(); ASSERT(GetDocument()->GetInPlaceActiveItem(this) == NULL); } if (m_pSelection != NULL) // invalidate the old item InvalidateItem(m_pSelection); if ((m_pSelection = pNewSel) != NULL) // invalidate the new item InvalidateItem(m_pSelection); } ///////////////////////////////////////////////////////////////////////////// // CMainView diagnostics #ifdef _DEBUG void CMainView::AssertValid() const { CScrollView::AssertValid(); } void CMainView::Dump(CDumpContext& dc) const { CScrollView::Dump(dc); } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // Main 'Edit' menu commands void CMainView::OnUpdateEditMenu(CCmdUI* pCmdUI) { // most Edit menu commands are enabled only if we have a selection // and there are no in-place activations for this view pCmdUI->Enable(m_pSelection != NULL && GetDocument()->GetInPlaceActiveItem(this) == NULL); } void CMainView::OnEditCut() { ASSERT(m_pSelection != NULL); TRY { m_pSelection->CopyToClipboard(TRUE); OnEditClear(); } CATCH_ALL(e) { AfxMessageBox(IDP_CLIPBOARD_CUT_FAILED); } END_CATCH_ALL } void CMainView::OnEditCopy() { ASSERT(m_pSelection != NULL); TRY { m_pSelection->CopyToClipboard(TRUE); } CATCH_ALL(e) { AfxMessageBox(IDP_CLIPBOARD_COPY_FAILED); } END_CATCH_ALL } void CMainView::OnEditClear() { if (m_pSelection != NULL) GetDocument()->DeleteItem(m_pSelection); } void CMainView::OnPaste() { if (DoPasteItem(FALSE, NULL, NULL) == NULL) AfxMessageBox(IDP_GET_FROM_CLIPBOARD_FAILED); } void CMainView::OnPasteLink() { if (DoPasteItem(TRUE, NULL, NULL) == NULL) AfxMessageBox(IDP_GET_FROM_CLIPBOARD_FAILED); } void CMainView::DoPasteNative( COleDataObject* pDataObject, CPoint* pPoint, CRectItem* pItem) { // get file refering to clipboard data CFile* pFile = pDataObject->GetFileData(CMainDoc::m_cfPrivate); if (pFile == NULL) { // if the file failed to open, throw an exception // to force cleanup in DoPasteItem. the exact // type of exception thrown here is unimportant... AfxThrowFileException(CFileException::generic); } CArchive ar(pFile, CArchive::load); TRY { // connect the file to an archive and read the data ar.m_pDocument = GetDocument(); // for COleClientItem serialize pItem->Serialize(ar); } CATCH_ALL(e) { ar.Close(); delete pFile; THROW_LAST(); } END_CATCH_ALL ar.Close(); delete pFile; // adjust position to that specified by point if (pPoint != NULL) pItem->m_ptPos = *pPoint; } void CMainView::DoPasteStandard(BOOL bLink, COleDataObject* pDataObject, CPoint* pPoint, CRectItem* pItem, CLIPFORMAT cfFormat) { if (bLink) // paste link { if (!pItem->CreateLinkFromData(pDataObject)) AfxThrowMemoryException(); // any exception will do } // paste embedded else if (!pItem->CreateFromData(pDataObject) && !pItem->CreateStaticFromData(pDataObject, OLERENDER_DRAW, cfFormat)) { AfxThrowMemoryException(); // any exception will do } // copy the current iconic representation FORMATETC fmtetc; fmtetc.cfFormat = CF_METAFILEPICT; fmtetc.dwAspect = DVASPECT_ICON; fmtetc.ptd = NULL; fmtetc.tymed = TYMED_MFPICT; fmtetc.lindex = 1; HGLOBAL hObj = pDataObject->GetGlobalData(CF_METAFILEPICT, &fmtetc); if (hObj != NULL) { pItem->SetIconicMetafile(hObj); // the following code is an easy way to free a metafile pict STGMEDIUM stgMed; memset(&stgMed, 0, sizeof(stgMed)); stgMed.tymed = TYMED_MFPICT; stgMed.hGlobal = hObj; ReleaseStgMedium(&stgMed); } // set the current drawing aspect hObj = pDataObject->GetGlobalData(m_cfObjectDescriptor); if (hObj != NULL) { ASSERT(hObj != NULL); // got CF_OBJECTDESCRIPTOR ok. Lock it down and extract size. LPOBJECTDESCRIPTOR pObjDesc = (LPOBJECTDESCRIPTOR)GlobalLock(hObj); ASSERT(pObjDesc != NULL); pItem->SetDrawAspect((DVASPECT)pObjDesc->dwDrawAspect); GlobalUnlock(hObj); GlobalFree(hObj); } // set top-left based on point of drop if (pPoint != NULL) pItem->m_ptPos = *pPoint; // get size from drag/drop operation CSize size; if (GetObjectInfo(pDataObject, &size, NULL) && size.cx != 0 && size.cy != 0) { // use size obtained from object instead of default size.cx = MulDiv(size.cx, 10, 254); size.cy = -MulDiv(size.cy, 10, 254); pItem->SetSize(size); CSize sizeExtent; pItem->GetCachedExtent(&sizeExtent); pItem->SetBaseSize(sizeExtent); } else { // no extent from CF_OBJECTDESCRIPTOR, use extent from object pItem->UpdateExtent(); } } // Helper for paste/pastelink // // bLink pDataObject pPoint cfFormat // EditPaste FALSE NULL(clipboard) NULL(default) 0 // Drag/Drop TRUE/FALSE X X 0 // PasteLink TRUE NULL(clipboard) NULL(default) 0 // PasteSpecial TRUE/FALSE X NULL(default) X CRectItem* CMainView::DoPasteItem(BOOL bLink, COleDataObject* pDataObject, CPoint* pPoint, CLIPFORMAT cfFormat) { BeginWaitCursor(); CRectItem* pItem = GetDocument()->CreateItem(); ASSERT_VALID(pItem); BOOL bAllowAdjust = (pPoint == NULL) ? TRUE : FALSE; // use clipboard data if not doing drag/drop COleDataObject clipboardData; if (pDataObject == NULL) { clipboardData.AttachClipboard(); pDataObject = &clipboardData; } TRY { if (cfFormat == CMainDoc::m_cfPrivate) { // if format specified (i.e. PasteSpecial) then use that one DoPasteNative(pDataObject, pPoint, pItem); } else if (!bLink && cfFormat == 0 && pDataObject->IsDataAvailable(CMainDoc::m_cfPrivate)) { // if we're not pasting a link, cfFormat was unspecified, // and private format is available use it DoPasteNative(pDataObject, pPoint, pItem); } // otherwise perform a standard paste else if (bAllowAdjust) { CPoint ptDef(10, -10); DoPasteStandard(bLink, pDataObject, &ptDef, pItem, cfFormat); } else { DoPasteStandard(bLink, pDataObject, pPoint, pItem, cfFormat); } if (bAllowAdjust) { // allow document to adjust position of item so that it doesn't // lay directly over an item of the same size // this only occurs if the drop point is not specified GetDocument()->AdjustItemPosition(pItem); } } CATCH_ALL(e) { // general cleanup TRACE0("failed to embed/link an OLE object\n"); pItem->Delete(); pItem = NULL; } END_CATCH_ALL // set the selection with bSafeSelect = TRUE SetSelection(pItem, TRUE); // update the document and views GetDocument()->SetModifiedFlag(); GetDocument()->UpdateAllViews(NULL, 0, pItem); // including this view EndWaitCursor(); return pItem; } ///////////////////////////////////////////////////////////////////////////// // Insert New Object and Activate Object void CMainView::OnInsertObject() { COleInsertDialog dlg; if (dlg.DoModal() != IDOK) return; BeginWaitCursor(); CRectItem* pItem = NULL; TRY { // create item from dialog results pItem = GetDocument()->CreateItem(); if (!dlg.CreateItem(pItem)) AfxThrowMemoryException(); // any exception will do // try to get initial presentation data pItem->UpdateLink(); pItem->UpdateExtent(); // if insert new object -- initially show the object if (dlg.GetSelectionType() == COleInsertDialog::createNewItem) pItem->DoVerb(OLEIVERB_SHOW, this); SetSelection(pItem); } CATCH_ALL(e) { // cleanup item, if allocated if (pItem != NULL) GetDocument()->DeleteItem(pItem); AfxMessageBox(IDP_FAILED_TO_CREATE); } END_CATCH_ALL EndWaitCursor(); } void CMainView::OnLButtonDblClk(UINT, CPoint) { // Double click will activate the main verb if (m_pSelection != NULL) { BeginWaitCursor(); LONG iVerb = OLEIVERB_PRIMARY; if (GetKeyState(VK_CONTROL) < 0) iVerb = OLEIVERB_OPEN; m_pSelection->DoVerb(iVerb, this); EndWaitCursor(); } } ///////////////////////////////////////////////////////////////////////////// // Hit detection, moving and resizing items CRectItem* CMainView::GetHitItem(CPoint point) { CMainDoc* pDoc = GetDocument(); CRectItem* pItemHit = NULL; // Find the item hit by the mouse POSITION pos = pDoc->GetStartPosition(); while (pos != NULL) { CRectItem* pItem = DYNAMIC_DOWNCAST(CRectItem, pDoc->GetNextItem(pos)); if (pItem != NULL) { CRectTracker tracker; SetupTracker(&tracker, pItem); if (tracker.HitTest(point) >= 0) { pItemHit = pItem; // items later in the list are drawn on top - so keep looking } } } return pItemHit; } void CMainView::DocToClient(CRect& rect) { CClientDC dc(this); OnPrepareDC(&dc); dc.LPtoDP(&rect); // convert logical rect to device rect rect.NormalizeRect(); } void CMainView::ClientToDoc(CRect& rect) { CClientDC dc(this); OnPrepareDC(&dc); dc.DPtoLP(&rect); // convert device rect to logical rect } void CMainView::DocToClient(CSize& size) { CClientDC dc(this); OnPrepareDC(&dc); dc.LPtoDP(&size); // convert logical size to device size size.cx = abs(size.cx); size.cy = abs(size.cy); } void CMainView::ClientToDoc(CSize& size) { CClientDC dc(this); OnPrepareDC(&dc); dc.DPtoLP(&size); // convert device rect to logical rect size.cx = abs(size.cx); size.cy = abs(size.cy); } void CMainView::DocToClient(CPoint& point) { CClientDC dc(this); OnPrepareDC(&dc); dc.LPtoDP(&point); // convert logical point to device point } void CMainView::ClientToDoc(CPoint& point) { CClientDC dc(this); OnPrepareDC(&dc); dc.DPtoLP(&point); // convert device point to logical point } void CMainView::OnLButtonDown(UINT /*nFlags*/, CPoint point) { CRectItem* pItemHit = GetHitItem(point); SetSelection(pItemHit); if (pItemHit == NULL) return; CRect rectLimit; GetClientRect(rectLimit); CRectTracker tracker; SetupTracker(&tracker, pItemHit); UpdateWindow(); // update before entering the tracker if (tracker.HitTest(point) == CRectTracker::hitMiddle) // moving, not sizing { // determine mouse position offset from the item itself CRect rect = pItemHit->GetRect(); DocToClient(rect); CPoint ptOffset(point.x - rect.left, point.y - rect.top); // determine sensitivity rectangle (determines when drag starts) CRect rectDrag(rect.left, rect.top, rect.left+1, rect.top+1); // execute the drag/drop operation m_bInDrag = TRUE; ClientToScreen(&rect); // must be in screen co-ordinates ClientToScreen(&rectDrag); DROPEFFECT dropEffect = pItemHit->DoDragDrop(rect, ptOffset, TRUE, DROPEFFECT_COPY|DROPEFFECT_MOVE, &rectDrag); if (m_bInDrag == FALSE) // move in same window return; m_bInDrag = FALSE; if (dropEffect == DROPEFFECT_MOVE) { // the item was moved (essentially a copy w/delete) pItemHit->Invalidate(); if (m_pSelection == pItemHit) m_pSelection = NULL; GetDocument()->DeleteItem(pItemHit); } } else if (tracker.Track(this, point)) { ClientToDoc(tracker.m_rect); pItemHit->Move(tracker.m_rect); GetDocument()->SetModifiedFlag(); } } BOOL CMainView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) { if (pWnd == this && m_pSelection != NULL) { // give the tracker for the selection a chance CRectTracker tracker; SetupTracker(&tracker, m_pSelection); if (tracker.SetCursor(this, nHitTest)) return TRUE; } return CScrollView::OnSetCursor(pWnd, nHitTest, message); } ///////////////////////////////////////////////////////////////////////////// // Right mouse for popup context sensitive menu void CMainView::OnRButtonDown(UINT, CPoint point) { // make sure window is active GetParentFrame()->ActivateFrame(); SetSelection(GetHitItem(point)); // reselect item if appropriate UpdateWindow(); if (m_pSelection != NULL) { CMenu bar; if (bar.LoadMenu(ID_OBJECT_POPUP_MENU)) { CMenu& popup = *bar.GetSubMenu(0); ASSERT(popup.m_hMenu != NULL); ClientToScreen(&point); popup.TrackPopupMenu(TPM_RIGHTBUTTON, point.x, point.y, AfxGetMainWnd()); // route commands through main window } } } void CMainView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { MessageBeep(0); // to test for proper focus transfer CScrollView::OnChar(nChar, nRepCnt, nFlags); } void CMainView::OnSetFocus(CWnd* pOldWnd) { COleClientItem* pActiveItem = GetDocument()->GetInPlaceActiveItem(this); if (pActiveItem != NULL && pActiveItem->GetItemState() == COleClientItem::activeUIState) { // need to set focus to this item if it is in the same view CWnd* pWnd = pActiveItem->GetInPlaceWindow(); if (pWnd != NULL) { pWnd->SetFocus(); return; } } CScrollView::OnSetFocus(pOldWnd); } void CMainView::OnSize(UINT nType, int cx, int cy) { CScrollView::OnSize(nType, cx, cy); UpdateActiveItem(); } ///////////////////////////////////////////////////////////////////////////// // support for drag/drop int CMainView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CScrollView::OnCreate(lpCreateStruct) == -1) return -1; // register drop target m_dropTarget.Register(this); return 0; } BOOL CMainView::OnDrop(COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point) { ASSERT_VALID(this); // clean up focus rect OnDragLeave(); // offset point as appropriate for dragging GetObjectInfo(pDataObject, &m_dragSize, &m_dragOffset); CClientDC dc(NULL); dc.HIMETRICtoDP(&m_dragSize); dc.HIMETRICtoDP(&m_dragOffset); point -= m_dragOffset; // if move within the view ClientToDoc(point); if ((dropEffect & DROPEFFECT_MOVE) && m_bInDrag) { ASSERT(m_pSelection != NULL); m_bInDrag = FALSE; // signal drag code that a move happened // set top-left based on point of drop CRect rect = m_pSelection->GetRect(); if (rect.TopLeft() != point) // if moved { m_pSelection->Move(CRect(point,rect.Size())); GetDocument()->SetModifiedFlag(); } } // check and paste link else if ((dropEffect & DROPEFFECT_LINK) && DoPasteItem(TRUE, pDataObject, &point)) return TRUE; // paste embedding/static else if (DoPasteItem(FALSE, pDataObject, &point)) return TRUE; return FALSE; } BOOL CMainView::GetObjectInfo(COleDataObject* pDataObject, CSize* pSize, CSize* pOffset) { ASSERT(pSize != NULL); // get object descriptor data HGLOBAL hObjDesc = pDataObject->GetGlobalData(m_cfObjectDescriptor); if (hObjDesc == NULL) { if (pOffset != NULL) *pOffset = CSize(0, 0); // fill in defaults instead *pSize = CSize(0, 0); return FALSE; } ASSERT(hObjDesc != NULL); // otherwise, got CF_OBJECTDESCRIPTOR ok. Lock it down and extract size. LPOBJECTDESCRIPTOR pObjDesc = (LPOBJECTDESCRIPTOR)GlobalLock(hObjDesc); ASSERT(pObjDesc != NULL); pSize->cx = (int)pObjDesc->sizel.cx; pSize->cy = (int)pObjDesc->sizel.cy; if (pOffset != NULL) { pOffset->cx = (int)pObjDesc->pointl.x; pOffset->cy = (int)pObjDesc->pointl.y; } GlobalUnlock(hObjDesc); GlobalFree(hObjDesc); // successfully retrieved pSize & pOffset info return TRUE; } DROPEFFECT CMainView::OnDragEnter(COleDataObject* pDataObject, DWORD grfKeyState, CPoint point) { ASSERT(m_prevDropEffect == DROPEFFECT_NONE); GetObjectInfo(pDataObject, &m_dragSize, &m_dragOffset); CClientDC dc(NULL); dc.HIMETRICtoDP(&m_dragSize); dc.HIMETRICtoDP(&m_dragOffset); return OnDragOver(pDataObject, grfKeyState, point); } DROPEFFECT CMainView::OnDragOver(COleDataObject*, DWORD grfKeyState, CPoint point) { point -= m_dragOffset; // adjust target rect by original cursor offset // check for point outside logical area -- i.e. in hatched region // GetTotalSize() returns the size passed to SetScrollSizes CRect rectScroll(CPoint(0, 0), GetTotalSize()); CRect rectItem(point,m_dragSize); if (rectItem.IsRectEmpty()) { // some apps might have a null size in the object descriptor... rectItem.InflateRect(1,1); } rectItem.OffsetRect(GetDeviceScrollPosition()); DROPEFFECT de = DROPEFFECT_NONE; CRect rectTemp; if (rectTemp.IntersectRect(rectScroll, rectItem)) { // check for force link if ((grfKeyState & (MK_CONTROL|MK_SHIFT)) == (MK_CONTROL|MK_SHIFT)) de = DROPEFFECT_LINK; // check for force copy else if ((grfKeyState & MK_CONTROL) == MK_CONTROL) de = DROPEFFECT_COPY; // check for force move else if ((grfKeyState & MK_ALT) == MK_ALT) de = DROPEFFECT_MOVE; // default -- recommended action is move else de = DROPEFFECT_MOVE; } if (point == m_dragPoint) return de; // otherwise, cursor has moved -- need to update the drag feedback CClientDC dc(this); if (m_prevDropEffect != DROPEFFECT_NONE) { // erase previous focus rect dc.DrawFocusRect(CRect(m_dragPoint, m_dragSize)); } m_prevDropEffect = de; if (m_prevDropEffect != DROPEFFECT_NONE) { m_dragPoint = point; dc.DrawFocusRect(CRect(point, m_dragSize)); } return de; } void CMainView::OnDragLeave() { CClientDC dc(this); if (m_prevDropEffect != DROPEFFECT_NONE) { dc.DrawFocusRect(CRect(m_dragPoint,m_dragSize)); // erase previous focus rect m_prevDropEffect = DROPEFFECT_NONE; } } ///////////////////////////////////////////////////////////////////////////// // Commands for switching display aspects void CMainView::OnObjectDisplayContent() { if (m_pSelection == NULL) return; ASSERT_VALID(m_pSelection); m_pSelection->Invalidate(); m_pSelection->SetDrawAspect(DVASPECT_CONTENT); m_pSelection->UpdateExtent(); m_pSelection->Invalidate(); } void CMainView::OnUpdateObjectDisplayContent(CCmdUI* pCmdUI) { if (m_pSelection == NULL) { pCmdUI->Enable(FALSE); return; } ASSERT_VALID(m_pSelection); pCmdUI->SetCheck(m_pSelection->GetDrawAspect() == DVASPECT_CONTENT); pCmdUI->Enable(TRUE); } void CMainView::OnObjectDisplayAsIcon() { if (m_pSelection == NULL) return; ASSERT_VALID(m_pSelection); m_pSelection->Invalidate(); m_pSelection->SetDrawAspect(DVASPECT_ICON); m_pSelection->UpdateExtent(); m_pSelection->Invalidate(); } void CMainView::OnUpdateObjectDisplayAsIcon(CCmdUI* pCmdUI) { if (m_pSelection == NULL) { pCmdUI->Enable(FALSE); return; } ASSERT_VALID(m_pSelection); pCmdUI->SetCheck(m_pSelection->GetDrawAspect() == DVASPECT_ICON); pCmdUI->Enable(TRUE); } void CMainView::UpdateActiveItem() { // when there is an active item visible, sizing the window may cause // more/less of the in-place object to become visible. // (ie. the clipping rectangle changes with the size of the window) // a container supporting scrolling would also have to do this // when scrolling the contents of the window. COleClientItem* pActiveItem = GetDocument()->GetInPlaceActiveItem(this); if (pActiveItem != NULL && pActiveItem->GetItemState() == COleClientItem::activeUIState && pActiveItem->GetActiveView() == this) { // this will update the item rectangles by calling // OnGetPosRect & OnGetClipRect. pActiveItem->SetItemRects(); } } void CMainView::OnUpdateEditClone(CCmdUI* pCmdUI) { pCmdUI->Enable(m_pSelection != NULL); } void CMainView::OnEditClone() { if (m_pSelection == NULL) return; BeginWaitCursor(); CRectItem* pItem = NULL; TRY { // create item from dialog results pItem = GetDocument()->CreateItem(); if (!pItem->CreateCloneFrom(m_pSelection)) AfxThrowMemoryException(); // any exception will do // offset it so we can see the clone easier CRect rect(20, 20, 0, 0); ClientToDoc(rect); pItem->m_ptPos.x += rect.left; pItem->m_ptPos.y += rect.top; ASSERT_VALID(pItem); } CATCH_ALL(e) { // cleanup item, if allocated if (pItem != NULL) GetDocument()->DeleteItem(pItem); AfxMessageBox(IDP_FAILED_TO_CREATE); } END_CATCH_ALL EndWaitCursor(); } void CMainView::OnPasteSpecial() { COlePasteSpecialDialog dlg; dlg.AddFormat(CMainDoc::m_cfPrivate, TYMED_HGLOBAL, IDS_PRIVATE_CF_DESCR, FALSE, FALSE); dlg.AddStandardFormats(); if (dlg.DoModal() != IDOK) return; CRectItem* pItem = NULL; TRY { // Get the clipboard format of the selected CLIPFORMAT cf = dlg.m_ps.arrPasteEntries[dlg.m_ps.nSelectedIndex].fmtetc.cfFormat; if (cf == CMainDoc::m_cfPrivate) { BOOL bLink = dlg.GetSelectionType() == COlePasteSpecialDialog::pasteLink; COleDataObject dataObject; dataObject.Attach(dlg.m_ps.lpSrcDataObj, FALSE); pItem = DoPasteItem(bLink, &dataObject, NULL, cf); // try to get initial presentation data pItem->UpdateLink(); } else { pItem = GetDocument()->CreateItem(); if (!dlg.CreateItem(pItem)) { TRACE0("Warning: paste special failed to create item.\n"); AfxThrowMemoryException(); } // try to get initial presentation data pItem->UpdateLink(); // try to get initial extent pItem->UpdateExtent(); // allow document to offset item to avoid direct superimposition GetDocument()->AdjustItemPosition(pItem); // set the selection with bSafeSelect = TRUE SetSelection(pItem, TRUE); GetDocument()->SetModifiedFlag(); GetDocument()->UpdateAllViews(NULL, 0, pItem); } } CATCH_ALL(e) { // cleanup item, if allocated if (pItem != NULL) GetDocument()->DeleteItem(pItem); AfxMessageBox(IDP_FAILED_TO_CREATE); return; } END_CATCH_ALL } void CMainView::OnUpdateEditPaste(CCmdUI* pCmdUI) { // determine if private or standard OLE formats are on the clipboard COleDataObject dataObj; BOOL bEnable = dataObj.AttachClipboard() && (dataObj.IsDataAvailable(CMainDoc::m_cfPrivate) || COleClientItem::CanCreateFromData(&dataObj)); // enable command based on availability pCmdUI->Enable(bEnable); } void CMainView::OnObjectResetsize() { ASSERT(m_pSelection != NULL); m_pSelection->ResetSize(); } void CMainView::OnCancelInplace() { // deactivate the inplace active item on this frame/view COleClientItem* pActiveItem = GetDocument()->GetInPlaceActiveItem(this); if (pActiveItem != NULL) pActiveItem->Deactivate(); ASSERT(GetDocument()->GetInPlaceActiveItem(this) == NULL); } void CMainView::OnDestroy() { CScrollView::OnDestroy(); // deactivate the inplace active item on this view COleClientItem* pActiveItem = GetDocument()->GetInPlaceActiveItem(this); if (pActiveItem != NULL && pActiveItem->GetActiveView() == this) { pActiveItem->Deactivate(); ASSERT(GetDocument()->GetInPlaceActiveItem(this) == NULL); } } void CMainView::OnUpdateOleEditProperties(CCmdUI* pCmdUI) { pCmdUI->Enable(m_pSelection != NULL); } // edit properties dialog specific to OCLIENT class COlePropertiesEx : public COlePropertiesDialog { public: COlePropertiesEx(COleClientItem* pItem, UINT nScaleMin = 10, UINT nScaleMax = 500, CWnd* pParentWnd = NULL) : COlePropertiesDialog(pItem, nScaleMin, nScaleMax, pParentWnd) { } virtual BOOL OnApplyScale( COleClientItem* pItem, int nCurrentScale, BOOL bRelativeToOrig); }; BOOL COlePropertiesEx::OnApplyScale( COleClientItem* pItem, int nCurrentScale, BOOL bRelativeToOrig) { if (nCurrentScale != -1) { ASSERT_VALID(pItem); CRectItem* pRectItem = (CRectItem*)pItem; ASSERT_KINDOF(CRectItem, pRectItem); // reset to original size if necessary if (bRelativeToOrig) pRectItem->ResetSize(); // update extent to reflect scaling factor pRectItem->Invalidate(); CSize size = pRectItem->GetSize(); size.cx = MulDiv(size.cx, nCurrentScale, 100); size.cy = MulDiv(size.cy, nCurrentScale, 100); pRectItem->SetSize(size); pRectItem->Invalidate(); } return TRUE; } void CMainView::OnOleEditProperties() { ASSERT(m_pSelection != NULL); COlePropertiesEx dlg(m_pSelection); dlg.DoModal(); } void CMainView::OnUpdateOleChangeSource(CCmdUI* pCmdUI) { pCmdUI->Enable(m_pSelection != NULL && m_pSelection->GetType() == OT_LINK); } void CMainView::OnOleChangeSource() { ASSERT(m_pSelection != NULL && m_pSelection->GetType() == OT_LINK); COleChangeSourceDialog dlg(m_pSelection); dlg.DoModal(); }