home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / viewrich.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-06-16  |  56.3 KB  |  2,145 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12.  
  13. #ifdef AFX_CORE4_SEG
  14. #pragma code_seg(AFX_CORE4_SEG)
  15. #endif
  16.  
  17. #ifdef _DEBUG
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. #define new DEBUG_NEW
  23.  
  24. /////////////////////////////////////////////////////////////////////////////
  25. // CReObject
  26.  
  27. class CReObject : public _reobject
  28. {
  29. public:
  30.     CReObject();
  31.     CReObject(CRichEditCntrItem* pItem);
  32.     ~CReObject();
  33. };
  34.  
  35. CReObject::CReObject()
  36. {
  37.     cbStruct = sizeof(REOBJECT);
  38.     memset(&cbStruct+1, 0, sizeof(*this)-sizeof(cbStruct));
  39. }
  40.  
  41. CReObject::CReObject(CRichEditCntrItem* pItem)
  42. {
  43.     ASSERT(pItem != NULL);
  44.     cbStruct = sizeof(REOBJECT);
  45.  
  46.     pItem->GetClassID(&clsid);
  47.     poleobj = pItem->m_lpObject;
  48.     pstg = pItem->m_lpStorage;
  49.     polesite = pItem->m_lpClientSite;
  50.     ASSERT(poleobj != NULL);
  51.     ASSERT(pstg != NULL);
  52.     ASSERT(polesite != NULL);
  53.     poleobj->AddRef();
  54.     pstg->AddRef();
  55.     polesite->AddRef();
  56.  
  57.     sizel.cx = sizel.cy = 0; // let richedit determine initial size
  58.     dvaspect = pItem->GetDrawAspect();
  59.     dwFlags = REO_RESIZABLE;
  60.     dwUser = 0;
  61. }
  62.  
  63. CReObject::~CReObject()
  64. {
  65.     if (poleobj != NULL)
  66.         poleobj->Release();
  67.     if (pstg != NULL)
  68.         pstg->Release();
  69.     if (polesite != NULL)
  70.         polesite->Release();
  71. }
  72.  
  73. /////////////////////////////////////////////////////////////////////////////
  74. // CRichEditView
  75.  
  76. AFX_STATIC const UINT _afxMsgFindReplace2 = ::RegisterWindowMessage(FINDMSGSTRING);
  77.  
  78. BEGIN_MESSAGE_MAP(CRichEditView, CCtrlView)
  79.     //{{AFX_MSG_MAP(CRichEditView)
  80.     ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateNeedSel)
  81.     ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateNeedClip)
  82.     ON_UPDATE_COMMAND_UI(ID_EDIT_FIND, OnUpdateNeedText)
  83.     ON_UPDATE_COMMAND_UI(ID_EDIT_REPEAT, OnUpdateNeedFind)
  84.     ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
  85.     ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE_SPECIAL, OnUpdateEditPasteSpecial)
  86.     ON_UPDATE_COMMAND_UI(ID_OLE_EDIT_PROPERTIES, OnUpdateEditProperties)
  87.     ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateNeedSel)
  88.     ON_UPDATE_COMMAND_UI(ID_EDIT_CLEAR, OnUpdateNeedSel)
  89.     ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_ALL, OnUpdateNeedText)
  90.     ON_UPDATE_COMMAND_UI(ID_EDIT_REPLACE, OnUpdateNeedText)
  91.     ON_COMMAND(ID_EDIT_CUT, OnEditCut)
  92.     ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
  93.     ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
  94.     ON_COMMAND(ID_EDIT_CLEAR, OnEditClear)
  95.     ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
  96.     ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
  97.     ON_COMMAND(ID_EDIT_FIND, OnEditFind)
  98.     ON_COMMAND(ID_EDIT_REPLACE, OnEditReplace)
  99.     ON_COMMAND(ID_EDIT_REPEAT, OnEditRepeat)
  100.     ON_COMMAND(ID_EDIT_PASTE_SPECIAL, OnEditPasteSpecial)
  101.     ON_COMMAND(ID_OLE_EDIT_PROPERTIES, OnEditProperties)
  102.     ON_COMMAND(ID_OLE_INSERT_NEW, OnInsertObject)
  103.     ON_COMMAND(ID_FORMAT_FONT, OnFormatFont)
  104.     ON_WM_SIZE()
  105.     ON_WM_CREATE()
  106.     ON_WM_DESTROY()
  107.     //}}AFX_MSG_MAP
  108.     ON_NOTIFY_REFLECT(EN_SELCHANGE, OnSelChange)
  109.     ON_REGISTERED_MESSAGE(_afxMsgFindReplace2, OnFindReplaceCmd)
  110. END_MESSAGE_MAP()
  111.  
  112. // richedit buffer limit -- let's set it at 16M
  113. AFX_DATADEF ULONG CRichEditView::lMaxSize = 0xffffff;
  114.  
  115. /////////////////////////////////////////////////////////////////////////////
  116. // CRichEditView construction/destruction
  117.  
  118. CRichEditView::CRichEditView() : CCtrlView(_T("RICHEDIT"), AFX_WS_DEFAULT_VIEW |
  119.     WS_HSCROLL | WS_VSCROLL | ES_AUTOHSCROLL | ES_AUTOVSCROLL |
  120.     ES_MULTILINE | ES_NOHIDESEL | ES_SAVESEL | ES_SELECTIONBAR)
  121. {
  122.     m_bSyncCharFormat = m_bSyncParaFormat = TRUE;
  123.     m_lpRichEditOle = NULL;
  124.     m_nBulletIndent = 720; // 1/2 inch
  125.     m_nWordWrap = WrapToWindow;
  126.     m_nPasteType = 0;
  127.     SetPaperSize(CSize(8*1440+720, 11*1440));
  128.     SetMargins(CRect(0,0,0,0));
  129.     m_charformat.cbSize = sizeof(CHARFORMAT);
  130.     m_paraformat.cbSize = sizeof(PARAFORMAT);
  131. }
  132.  
  133. BOOL CRichEditView::PreCreateWindow(CREATESTRUCT& cs)
  134. {
  135.     if (!AfxInitRichEdit())
  136.         return FALSE;
  137.     CCtrlView::PreCreateWindow(cs);
  138.     cs.lpszName = _T("");
  139.  
  140.     cs.cx = cs.cy = 100; // necessary to avoid bug with ES_SELECTIONBAR and zero for cx and cy
  141.     cs.style |= WS_CLIPSIBLINGS;
  142.  
  143.     return TRUE;
  144. }
  145.  
  146. int CRichEditView::OnCreate(LPCREATESTRUCT lpcs)
  147. {
  148.     if (CCtrlView::OnCreate(lpcs) != 0)
  149.         return -1;
  150.     GetRichEditCtrl().LimitText(lMaxSize);
  151.     GetRichEditCtrl().SetEventMask(ENM_SELCHANGE | ENM_CHANGE | ENM_SCROLL);
  152.     VERIFY(GetRichEditCtrl().SetOLECallback(&m_xRichEditOleCallback));
  153.     m_lpRichEditOle = GetRichEditCtrl().GetIRichEditOle();
  154.     DragAcceptFiles();
  155.     GetRichEditCtrl().SetOptions(ECOOP_OR, ECO_AUTOWORDSELECTION);
  156.     WrapChanged();
  157.     ASSERT(m_lpRichEditOle != NULL);
  158.     return 0;
  159. }
  160.  
  161. void CRichEditView::OnInitialUpdate()
  162. {
  163.     CCtrlView::OnInitialUpdate();
  164.     m_bSyncCharFormat = m_bSyncParaFormat = TRUE;
  165. }
  166.  
  167. /////////////////////////////////////////////////////////////////////////////
  168. // CRichEditView document like functions
  169.  
  170. void CRichEditView::DeleteContents()
  171. {
  172.     ASSERT_VALID(this);
  173.     ASSERT(m_hWnd != NULL);
  174.     SetWindowText(_T(""));
  175.     GetRichEditCtrl().EmptyUndoBuffer();
  176.     m_bSyncCharFormat = m_bSyncParaFormat = TRUE;
  177.     ASSERT_VALID(this);
  178. }
  179.  
  180. void CRichEditView::WrapChanged()
  181. {
  182.     CWaitCursor wait;
  183.     CRichEditCtrl& ctrl = GetRichEditCtrl();
  184.     if (m_nWordWrap == WrapNone)
  185.         ctrl.SetTargetDevice(NULL, 1);
  186.     else if (m_nWordWrap == WrapToWindow)
  187.         ctrl.SetTargetDevice(NULL, 0);
  188.     else if (m_nWordWrap == WrapToTargetDevice) // wrap to ruler
  189.     {
  190.         AfxGetApp()->CreatePrinterDC(m_dcTarget);
  191.         if (m_dcTarget.m_hDC == NULL)
  192.             m_dcTarget.CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
  193.         ctrl.SetTargetDevice(m_dcTarget, GetPrintWidth());
  194.     }
  195. }
  196.  
  197. /////////////////////////////////////////////////////////////////////////////
  198. // CRichEditView serialization support
  199.  
  200. class _afxRichEditCookie
  201. {
  202. public:
  203.     CArchive& m_ar;
  204.     DWORD m_dwError;
  205.     _afxRichEditCookie(CArchive& ar) : m_ar(ar) {m_dwError=0;}
  206. };
  207.  
  208. void CRichEditView::Serialize(CArchive& ar)
  209.     // Read and write CRichEditView object to archive, with length prefix.
  210. {
  211.     ASSERT_VALID(this);
  212.     ASSERT(m_hWnd != NULL);
  213.     Stream(ar, FALSE);
  214.     ASSERT_VALID(this);
  215. }
  216.  
  217. void CRichEditView::Stream(CArchive& ar, BOOL bSelection)
  218. {
  219.     EDITSTREAM es = {0, 0, EditStreamCallBack};
  220.     _afxRichEditCookie cookie(ar);
  221.     es.dwCookie = (DWORD)&cookie;
  222.     int nFormat = GetDocument()->GetStreamFormat();
  223.     if (bSelection)
  224.         nFormat |= SFF_SELECTION;
  225.     if (ar.IsStoring())
  226.         GetRichEditCtrl().StreamOut(nFormat, es);
  227.     else
  228.     {
  229.         GetRichEditCtrl().StreamIn(nFormat, es);
  230.         Invalidate();
  231.     }
  232.     if (cookie.m_dwError != 0)
  233.         AfxThrowFileException(cookie.m_dwError);
  234. }
  235.  
  236. // return 0 for no error, otherwise return error code
  237. DWORD CALLBACK CRichEditView::EditStreamCallBack(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb)
  238. {
  239.     _afxRichEditCookie* pCookie = (_afxRichEditCookie*)dwCookie;
  240.     CArchive& ar = pCookie->m_ar;
  241.     ar.Flush();
  242.     DWORD dw = 0;
  243.     *pcb = cb;
  244.     TRY
  245.     {
  246.         if (ar.IsStoring())
  247.             ar.GetFile()->WriteHuge(pbBuff, cb);
  248.         else
  249.             *pcb = ar.GetFile()->ReadHuge(pbBuff, cb);
  250.     }
  251.     CATCH(CFileException, e)
  252.     {
  253.         *pcb = 0;
  254.         pCookie->m_dwError = (DWORD)e->m_cause;
  255.         dw = 1;
  256.         DELETE_EXCEPTION(e);
  257.     }
  258.     AND_CATCH_ALL(e)
  259.     {
  260.         *pcb = 0;
  261.         pCookie->m_dwError = (DWORD)CFileException::generic;
  262.         dw = 1;
  263.         DELETE_EXCEPTION(e);
  264.     }
  265.     END_CATCH_ALL
  266.     return dw;
  267. }
  268.  
  269. /////////////////////////////////////////////////////////////////////////////
  270. // CRichEditView Printing support
  271.  
  272. void CRichEditView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo*)
  273. {
  274.     ASSERT_VALID(this);
  275. //  ASSERT_VALID(pDC);
  276.     // initialize page start vector
  277.     ASSERT(m_aPageStart.GetSize() == 0);
  278.     m_aPageStart.Add(0);
  279.     ASSERT(m_aPageStart.GetSize() > 0);
  280.     GetRichEditCtrl().FormatRange(NULL, FALSE); // required by RichEdit to clear out cache
  281.  
  282.     ASSERT_VALID(this);
  283. }
  284.  
  285. BOOL CRichEditView::PaginateTo(CDC* pDC, CPrintInfo* pInfo)
  286.     // attempts pagination to pInfo->m_nCurPage, TRUE == success
  287. {
  288.     ASSERT_VALID(this);
  289.     ASSERT_VALID(pDC);
  290.  
  291.     CRect rectSave = pInfo->m_rectDraw;
  292.     UINT nPageSave = pInfo->m_nCurPage;
  293.     ASSERT(nPageSave > 1);
  294.     ASSERT(nPageSave >= (UINT)m_aPageStart.GetSize());
  295.     VERIFY(pDC->SaveDC() != 0);
  296.     pDC->IntersectClipRect(0, 0, 0, 0);
  297.     pInfo->m_nCurPage = m_aPageStart.GetSize();
  298.     while (pInfo->m_nCurPage < nPageSave)
  299.     {
  300.         ASSERT(pInfo->m_nCurPage == (UINT)m_aPageStart.GetSize());
  301.         OnPrepareDC(pDC, pInfo);
  302.         ASSERT(pInfo->m_bContinuePrinting);
  303.         pInfo->m_rectDraw.SetRect(0, 0,
  304.             pDC->GetDeviceCaps(HORZRES), pDC->GetDeviceCaps(VERTRES));
  305.         pDC->DPtoLP(&pInfo->m_rectDraw);
  306.         OnPrint(pDC, pInfo);
  307.         if (pInfo->m_nCurPage == (UINT)m_aPageStart.GetSize())
  308.             break;
  309.         ++pInfo->m_nCurPage;
  310.     }
  311.     BOOL bResult = pInfo->m_nCurPage == nPageSave;
  312.     pDC->RestoreDC(-1);
  313.     pInfo->m_nCurPage = nPageSave;
  314.     pInfo->m_rectDraw = rectSave;
  315.     ASSERT_VALID(this);
  316.     return bResult;
  317. }
  318.  
  319. void CRichEditView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
  320. {
  321.     ASSERT_VALID(this);
  322.     ASSERT_VALID(pDC);
  323.     ASSERT(pInfo != NULL);  // overriding OnPaint -- never get this.
  324.  
  325.     pDC->SetMapMode(MM_TEXT);
  326.  
  327.     if (pInfo->m_nCurPage > (UINT)m_aPageStart.GetSize() &&
  328.         !PaginateTo(pDC, pInfo))
  329.     {
  330.         // can't paginate to that page, thus cannot print it.
  331.         pInfo->m_bContinuePrinting = FALSE;
  332.     }
  333.     ASSERT_VALID(this);
  334. }
  335.  
  336. long CRichEditView::PrintPage(CDC* pDC, long nIndexStart, long nIndexStop)
  337.     // worker function for laying out text in a rectangle.
  338. {
  339.     ASSERT_VALID(this);
  340.     ASSERT_VALID(pDC);
  341.     FORMATRANGE fr;
  342.  
  343.     // offset by printing offset
  344.     pDC->SetViewportOrg(-pDC->GetDeviceCaps(PHYSICALOFFSETX),
  345.         -pDC->GetDeviceCaps(PHYSICALOFFSETY));
  346.     // adjust DC because richedit doesn't do things like MFC
  347.     if (::GetDeviceCaps(pDC->m_hDC, TECHNOLOGY) != DT_METAFILE && pDC->m_hAttribDC != NULL)
  348.     {
  349.         ::ScaleWindowExtEx(pDC->m_hDC,
  350.             ::GetDeviceCaps(pDC->m_hDC, LOGPIXELSX),
  351.             ::GetDeviceCaps(pDC->m_hAttribDC, LOGPIXELSX),
  352.             ::GetDeviceCaps(pDC->m_hDC, LOGPIXELSY),
  353.             ::GetDeviceCaps(pDC->m_hAttribDC, LOGPIXELSY), NULL);
  354.     }
  355.  
  356.     fr.hdcTarget = pDC->m_hAttribDC;
  357.     fr.hdc = pDC->m_hDC;
  358.     fr.rcPage = GetPageRect();
  359.     fr.rc = GetPrintRect();
  360.  
  361.     fr.chrg.cpMin = nIndexStart;
  362.     fr.chrg.cpMax = nIndexStop;
  363.     long lRes = GetRichEditCtrl().FormatRange(&fr,TRUE);
  364.     return lRes;
  365. }
  366.  
  367. long CRichEditView::PrintInsideRect(CDC* pDC, RECT& rectLayout,
  368.     long nIndexStart, long nIndexStop, BOOL bOutput)
  369. {
  370.     ASSERT_VALID(this);
  371.     ASSERT_VALID(pDC);
  372.     FORMATRANGE fr;
  373.  
  374.     // adjust DC because richedit doesn't do things like MFC
  375.     if (::GetDeviceCaps(pDC->m_hDC, TECHNOLOGY) != DT_METAFILE && pDC->m_hAttribDC != NULL)
  376.     {
  377.         ::ScaleWindowExtEx(pDC->m_hDC,
  378.             ::GetDeviceCaps(pDC->m_hDC, LOGPIXELSX),
  379.             ::GetDeviceCaps(pDC->m_hAttribDC, LOGPIXELSX),
  380.             ::GetDeviceCaps(pDC->m_hDC, LOGPIXELSY),
  381.             ::GetDeviceCaps(pDC->m_hAttribDC, LOGPIXELSY), NULL);
  382.     }
  383.  
  384.     fr.hdcTarget = pDC->m_hAttribDC;
  385.     fr.hdc = pDC->m_hDC;
  386.     // convert rect to twips
  387.     fr.rcPage = rectLayout;
  388.     fr.rc = rectLayout;
  389.  
  390.     fr.chrg.cpMin = nIndexStart;
  391.     fr.chrg.cpMax = nIndexStop;
  392.     GetRichEditCtrl().FormatRange(NULL, FALSE); // required by RichEdit to clear out cache
  393.     // if bOutput is FALSE, we only measure
  394.     long lres = GetRichEditCtrl().FormatRange(&fr, bOutput);
  395.     GetRichEditCtrl().FormatRange(NULL, FALSE); // required by RichEdit to clear out cache
  396.  
  397.     rectLayout = fr.rc;
  398.     return lres;
  399. }
  400.  
  401. void CRichEditView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
  402. {
  403.     ASSERT_VALID(this);
  404.     ASSERT_VALID(pDC);
  405.     ASSERT(pInfo != NULL);
  406.     ASSERT(pInfo->m_bContinuePrinting);
  407.  
  408.     UINT nPage = pInfo->m_nCurPage;
  409.     ASSERT(nPage <= (UINT)m_aPageStart.GetSize());
  410.     long nIndex = (long) m_aPageStart[nPage-1];
  411.  
  412.     // print as much as possible in the current page.
  413.     nIndex = PrintPage(pDC, nIndex, 0xFFFFFFFF);
  414.  
  415.     if (nIndex >= GetTextLength())
  416.     {
  417.         TRACE0("End of Document\n");
  418.         pInfo->SetMaxPage(nPage);
  419.     }
  420.  
  421.     // update pagination information for page just printed
  422.     if (nPage == (UINT)m_aPageStart.GetSize())
  423.     {
  424.         if (nIndex < GetTextLength())
  425.             m_aPageStart.Add(nIndex);
  426.     }
  427.     else
  428.     {
  429.         ASSERT(nPage+1 <= (UINT)m_aPageStart.GetSize());
  430.         ASSERT(nIndex == (long)m_aPageStart[nPage+1-1]);
  431.     }
  432. }
  433.  
  434.  
  435. void CRichEditView::OnEndPrinting(CDC*, CPrintInfo*)
  436. {
  437.     ASSERT_VALID(this);
  438.     GetRichEditCtrl().FormatRange(NULL, FALSE); // required by RichEdit to clear out cache
  439.     m_aPageStart.RemoveAll();
  440. }
  441.  
  442. /////////////////////////////////////////////////////////////////////////////
  443. // CRichEditView::XRichEditOleCallback
  444.  
  445. BEGIN_INTERFACE_MAP(CRichEditView, CCtrlView)
  446.     // we use IID_IUnknown because richedit doesn't define an IID
  447.     INTERFACE_PART(CRichEditView, IID_IUnknown, RichEditOleCallback)
  448. END_INTERFACE_MAP()
  449.  
  450. STDMETHODIMP_(ULONG) CRichEditView::XRichEditOleCallback::AddRef()
  451. {
  452.     METHOD_PROLOGUE_EX_(CRichEditView, RichEditOleCallback)
  453.     return (ULONG)pThis->InternalAddRef();
  454. }
  455.  
  456. STDMETHODIMP_(ULONG) CRichEditView::XRichEditOleCallback::Release()
  457. {
  458.     METHOD_PROLOGUE_EX_(CRichEditView, RichEditOleCallback)
  459.     return (ULONG)pThis->InternalRelease();
  460. }
  461.  
  462. STDMETHODIMP CRichEditView::XRichEditOleCallback::QueryInterface(
  463.     REFIID iid, LPVOID* ppvObj)
  464. {
  465.     METHOD_PROLOGUE_EX_(CRichEditView, RichEditOleCallback)
  466.     return (HRESULT)pThis->InternalQueryInterface(&iid, ppvObj);
  467. }
  468.  
  469. STDMETHODIMP CRichEditView::XRichEditOleCallback::GetNewStorage(LPSTORAGE* ppstg)
  470. {
  471.     METHOD_PROLOGUE_EX_(CRichEditView, RichEditOleCallback)
  472.  
  473.     // Create a flat storage and steal it from the client item
  474.     // the client item is only used for creating the storage
  475.     COleClientItem item;
  476.     item.GetItemStorageFlat();
  477.     *ppstg = item.m_lpStorage;
  478.     HRESULT hRes = E_OUTOFMEMORY;
  479.     if (item.m_lpStorage != NULL)
  480.     {
  481.         item.m_lpStorage = NULL;
  482.         hRes = S_OK;
  483.     }
  484.     pThis->GetDocument()->InvalidateObjectCache();
  485.     return hRes;
  486. }
  487.  
  488. STDMETHODIMP CRichEditView::XRichEditOleCallback::GetInPlaceContext(
  489.     LPOLEINPLACEFRAME* lplpFrame, LPOLEINPLACEUIWINDOW* lplpDoc,
  490.     LPOLEINPLACEFRAMEINFO lpFrameInfo)
  491. {
  492.     METHOD_PROLOGUE_EX(CRichEditView, RichEditOleCallback)
  493.     return pThis->GetWindowContext(lplpFrame, lplpDoc, lpFrameInfo);
  494. }
  495.  
  496. STDMETHODIMP CRichEditView::XRichEditOleCallback::ShowContainerUI(BOOL fShow)
  497. {
  498.     METHOD_PROLOGUE_EX(CRichEditView, RichEditOleCallback)
  499.     return pThis->ShowContainerUI(fShow);
  500. }
  501.  
  502. STDMETHODIMP CRichEditView::XRichEditOleCallback::QueryInsertObject(
  503.     LPCLSID /*lpclsid*/, LPSTORAGE /*pstg*/, LONG /*cp*/)
  504. {
  505.     METHOD_PROLOGUE_EX(CRichEditView, RichEditOleCallback)
  506.     pThis->GetDocument()->InvalidateObjectCache();
  507.     return S_OK;
  508. }
  509.  
  510. STDMETHODIMP CRichEditView::XRichEditOleCallback::DeleteObject(LPOLEOBJECT /*lpoleobj*/)
  511. {
  512.     METHOD_PROLOGUE_EX_(CRichEditView, RichEditOleCallback)
  513.     pThis->GetDocument()->InvalidateObjectCache();
  514.     return S_OK;
  515. }
  516.  
  517. STDMETHODIMP CRichEditView::XRichEditOleCallback::QueryAcceptData(
  518.     LPDATAOBJECT lpdataobj, CLIPFORMAT* lpcfFormat, DWORD reco,
  519.     BOOL fReally, HGLOBAL hMetaPict)
  520. {
  521.     METHOD_PROLOGUE_EX(CRichEditView, RichEditOleCallback)
  522.     return pThis->QueryAcceptData(lpdataobj, lpcfFormat, reco,
  523.         fReally, hMetaPict);
  524. }
  525.  
  526. STDMETHODIMP CRichEditView::XRichEditOleCallback::ContextSensitiveHelp(BOOL /*fEnterMode*/)
  527. {
  528.     return E_NOTIMPL;
  529. }
  530.  
  531. STDMETHODIMP CRichEditView::XRichEditOleCallback::GetClipboardData(
  532.     CHARRANGE* lpchrg, DWORD reco, LPDATAOBJECT* lplpdataobj)
  533. {
  534.     METHOD_PROLOGUE_EX(CRichEditView, RichEditOleCallback)
  535.     LPDATAOBJECT lpOrigDataObject = NULL;
  536.  
  537.     // get richedit's data object
  538.     if (FAILED(pThis->m_lpRichEditOle->GetClipboardData(lpchrg, reco,
  539.         &lpOrigDataObject)))
  540.     {
  541.         return E_NOTIMPL;
  542.     }
  543.  
  544.     // allow changes
  545.     HRESULT hRes = pThis->GetClipboardData(lpchrg, reco, lpOrigDataObject,
  546.         lplpdataobj);
  547.  
  548.     // if changed then free original object
  549.     if (SUCCEEDED(hRes))
  550.     {
  551.         if (lpOrigDataObject!=NULL)
  552.             lpOrigDataObject->Release();
  553.         return hRes;
  554.     }
  555.     else
  556.     {
  557.         // use richedit's data object
  558.         *lplpdataobj = lpOrigDataObject;
  559.         return S_OK;
  560.     }
  561. }
  562.  
  563. STDMETHODIMP CRichEditView::XRichEditOleCallback::GetDragDropEffect(
  564.     BOOL fDrag, DWORD grfKeyState, LPDWORD pdwEffect)
  565. {
  566.     if (!fDrag) // allowable dest effects
  567.     {
  568.         DWORD dwEffect;
  569.         // check for force link
  570.         if ((grfKeyState & (MK_CONTROL|MK_SHIFT)) == (MK_CONTROL|MK_SHIFT))
  571.             dwEffect = DROPEFFECT_LINK;
  572.         // check for force copy
  573.         else if ((grfKeyState & MK_CONTROL) == MK_CONTROL)
  574.             dwEffect = DROPEFFECT_COPY;
  575.         // check for force move
  576.         else if ((grfKeyState & MK_ALT) == MK_ALT)
  577.             dwEffect = DROPEFFECT_MOVE;
  578.         // default -- recommended action is move
  579.         else
  580.             dwEffect = DROPEFFECT_MOVE;
  581.         if (dwEffect & *pdwEffect) // make sure allowed type
  582.             *pdwEffect = dwEffect;
  583.     }
  584.     return S_OK;
  585. }
  586.  
  587. STDMETHODIMP CRichEditView::XRichEditOleCallback::GetContextMenu(
  588.     WORD seltype, LPOLEOBJECT lpoleobj, CHARRANGE* lpchrg,
  589.     HMENU* lphmenu)
  590. {
  591.     METHOD_PROLOGUE_EX(CRichEditView, RichEditOleCallback)
  592.     HMENU hMenu = pThis->GetContextMenu(seltype, lpoleobj, lpchrg);
  593.     if (hMenu == NULL)
  594.         return E_NOTIMPL;
  595.     *lphmenu = hMenu;
  596.     return S_OK;
  597. }
  598.  
  599. /////////////////////////////////////////////////////////////////////////////
  600. // CRichEditView command helpers
  601.  
  602. void CRichEditView::OnCharEffect(DWORD dwMask, DWORD dwEffect)
  603. {
  604.     GetCharFormatSelection();
  605.     if (m_charformat.dwMask & dwMask) // selection is all the same
  606.         m_charformat.dwEffects ^= dwEffect;
  607.     else
  608.         m_charformat.dwEffects |= dwEffect;
  609.     m_charformat.dwMask = dwMask;
  610.     SetCharFormat(m_charformat);
  611. }
  612.  
  613. void CRichEditView::OnUpdateCharEffect(CCmdUI* pCmdUI, DWORD dwMask, DWORD dwEffect)
  614. {
  615.     GetCharFormatSelection();
  616.     pCmdUI->SetCheck((m_charformat.dwMask & dwMask) ?
  617.         ((m_charformat.dwEffects & dwEffect) ? 1 : 0) : 2);
  618. }
  619.  
  620. void CRichEditView::OnParaAlign(WORD wAlign)
  621. {
  622.     GetParaFormatSelection();
  623.     m_paraformat.dwMask = PFM_ALIGNMENT;
  624.     m_paraformat.wAlignment = wAlign;
  625.     SetParaFormat(m_paraformat);
  626. }
  627.  
  628. void CRichEditView::OnUpdateParaAlign(CCmdUI* pCmdUI, WORD wAlign)
  629. {
  630.     GetParaFormatSelection();
  631.     // disable if no word wrap since alignment is meaningless
  632.     pCmdUI->Enable( (m_nWordWrap == WrapNone) ?
  633.         FALSE : TRUE);
  634.     pCmdUI->SetCheck( (m_paraformat.dwMask & PFM_ALIGNMENT) ?
  635.         ((m_paraformat.wAlignment == wAlign) ? 1 : 0) : 2);
  636. }
  637.  
  638. /////////////////////////////////////////////////////////////////////////////
  639. // CRichEditView commands
  640.  
  641. void CRichEditView::OnUpdateNeedSel(CCmdUI* pCmdUI)
  642. {
  643.     ASSERT_VALID(this);
  644.     long nStartChar, nEndChar;
  645.     GetRichEditCtrl().GetSel(nStartChar, nEndChar);
  646.     pCmdUI->Enable(nStartChar != nEndChar);
  647.     ASSERT_VALID(this);
  648. }
  649.  
  650. void CRichEditView::OnUpdateNeedClip(CCmdUI* pCmdUI)
  651. {
  652.     ASSERT_VALID(this);
  653.     pCmdUI->Enable(CanPaste());
  654. }
  655.  
  656. void CRichEditView::OnUpdateNeedText(CCmdUI* pCmdUI)
  657. {
  658.     ASSERT_VALID(this);
  659.     pCmdUI->Enable(GetTextLength() != 0);
  660. }
  661.  
  662. void CRichEditView::OnUpdateNeedFind(CCmdUI* pCmdUI)
  663. {
  664.     ASSERT_VALID(this);
  665.     _AFX_RICHEDIT_STATE* pEditState = _afxRichEditState;
  666.     pCmdUI->Enable(GetTextLength() != 0 &&
  667.         !pEditState->strFind.IsEmpty());
  668. }
  669.  
  670. void CRichEditView::OnUpdateEditUndo(CCmdUI* pCmdUI)
  671. {
  672.     ASSERT_VALID(this);
  673.     pCmdUI->Enable(GetRichEditCtrl().CanUndo());
  674. }
  675.  
  676. void CRichEditView::OnEditCut()
  677. {
  678.     ASSERT_VALID(this);
  679.     GetRichEditCtrl().Cut();
  680. }
  681.  
  682. void CRichEditView::OnEditCopy()
  683. {
  684.     ASSERT_VALID(this);
  685.     GetRichEditCtrl().Copy();
  686. }
  687.  
  688. void CRichEditView::OnEditPaste()
  689. {
  690.     ASSERT_VALID(this);
  691.     m_nPasteType = 0;
  692.     GetRichEditCtrl().Paste();
  693. }
  694.  
  695. void CRichEditView::OnEditClear()
  696. {
  697.     ASSERT_VALID(this);
  698.     GetRichEditCtrl().Clear();
  699. }
  700.  
  701. void CRichEditView::OnEditUndo()
  702. {
  703.     ASSERT_VALID(this);
  704.     GetRichEditCtrl().Undo();
  705.     m_bSyncCharFormat = m_bSyncParaFormat = TRUE;
  706. }
  707.  
  708. void CRichEditView::OnEditSelectAll()
  709. {
  710.     ASSERT_VALID(this);
  711.     GetRichEditCtrl().SetSel(0, -1);
  712. }
  713.  
  714. void CRichEditView::OnEditFind()
  715. {
  716.     ASSERT_VALID(this);
  717.     OnEditFindReplace(TRUE);
  718. }
  719.  
  720. void CRichEditView::OnEditReplace()
  721. {
  722.     ASSERT_VALID(this);
  723.     OnEditFindReplace(FALSE);
  724. }
  725.  
  726. void CRichEditView::OnEditRepeat()
  727. {
  728.     ASSERT_VALID(this);
  729.     _AFX_RICHEDIT_STATE* pEditState = _afxRichEditState;
  730.     if (!FindText(pEditState))
  731.         TextNotFound(pEditState->strFind);
  732. }
  733.  
  734. void CRichEditView::OnCancelEditCntr()
  735. {
  736.     m_lpRichEditOle->InPlaceDeactivate();
  737. }
  738.  
  739. void CRichEditView::OnInsertObject()
  740. {
  741.     // Invoke the standard Insert Object dialog box to obtain information
  742.     COleInsertDialog dlg;
  743.     if (dlg.DoModal() != IDOK)
  744.         return;
  745.  
  746.     CWaitCursor wait;
  747.  
  748.     CRichEditCntrItem* pItem = NULL;
  749.     TRY
  750.     {
  751.         // create item from dialog results
  752.         pItem = GetDocument()->CreateClientItem();
  753.         pItem->m_bLock = TRUE;
  754.         if (!dlg.CreateItem(pItem))
  755.         {
  756.             pItem->m_bLock = FALSE;
  757.             AfxThrowMemoryException();  // any exception will do
  758.         }
  759.  
  760.         HRESULT hr = InsertItem(pItem);
  761.         pItem->UpdateItemType();
  762.  
  763.         pItem->m_bLock = FALSE;
  764.  
  765.         if (hr != NOERROR)
  766.             AfxThrowOleException(hr);
  767.  
  768.         // if insert new object -- initially show the object
  769.         if (dlg.GetSelectionType() == COleInsertDialog::createNewItem)
  770.             pItem->DoVerb(OLEIVERB_SHOW, this);
  771.     }
  772.     CATCH(CException, e)
  773.     {
  774.         if (pItem != NULL)
  775.         {
  776.             ASSERT_VALID(pItem);
  777.             pItem->Delete();
  778.         }
  779.         AfxMessageBox(AFX_IDP_FAILED_TO_CREATE);
  780.     }
  781.     END_CATCH
  782. }
  783.  
  784. void CRichEditView::OnSelChange(NMHDR* pNMHDR, LRESULT* pResult)
  785. {
  786.     ASSERT(pNMHDR->code == EN_SELCHANGE);
  787.     UNUSED(pNMHDR); // not used in release builds
  788.  
  789.     m_bSyncCharFormat = m_bSyncParaFormat = TRUE;
  790.     *pResult = 0;
  791. }
  792.  
  793. void CRichEditView::OnDestroy()
  794. {
  795.     if (m_lpRichEditOle != NULL)
  796.         m_lpRichEditOle->Release();
  797.     CCtrlView::OnDestroy();
  798. }
  799.  
  800. void CRichEditView::OnEditProperties()
  801. {
  802.     ASSERT(m_lpRichEditOle != NULL);
  803.     CRichEditCntrItem* pSelection = GetSelectedItem();
  804.  
  805.     // make sure item is in sync with richedit's item
  806.     CReObject reo;
  807.     m_lpRichEditOle->GetObject(REO_IOB_SELECTION, &reo,
  808.         REO_GETOBJ_NO_INTERFACES);
  809.  
  810.     pSelection->SyncToRichEditObject(reo);
  811.     COlePropertiesDialog dlg(pSelection);
  812.     dlg.DoModal();
  813. }
  814.  
  815. void CRichEditView::OnUpdateEditProperties(CCmdUI* pCmdUI)
  816. {
  817.     pCmdUI->Enable(GetSelectedItem() != NULL);
  818. }
  819.  
  820. void CRichEditView::OnCharBold()
  821. {
  822.     OnCharEffect(CFM_BOLD, CFE_BOLD);
  823. }
  824.  
  825. void CRichEditView::OnUpdateCharBold(CCmdUI* pCmdUI)
  826. {
  827.     OnUpdateCharEffect(pCmdUI, CFM_BOLD, CFE_BOLD);
  828. }
  829.  
  830. void CRichEditView::OnCharItalic()
  831. {
  832.     OnCharEffect(CFM_ITALIC, CFE_ITALIC);
  833. }
  834.  
  835. void CRichEditView::OnUpdateCharItalic(CCmdUI* pCmdUI)
  836. {
  837.     OnUpdateCharEffect(pCmdUI, CFM_ITALIC, CFE_ITALIC);
  838. }
  839.  
  840. void CRichEditView::OnCharUnderline()
  841. {
  842.     OnCharEffect(CFM_UNDERLINE, CFE_UNDERLINE);
  843. }
  844.  
  845. void CRichEditView::OnUpdateCharUnderline(CCmdUI* pCmdUI)
  846. {
  847.     OnUpdateCharEffect(pCmdUI, CFM_UNDERLINE, CFE_UNDERLINE);
  848. }
  849.  
  850. void CRichEditView::OnParaCenter()
  851. {
  852.     OnParaAlign(PFA_CENTER);
  853. }
  854.  
  855. void CRichEditView::OnUpdateParaCenter(CCmdUI* pCmdUI)
  856. {
  857.     OnUpdateParaAlign(pCmdUI, PFA_CENTER);
  858. }
  859.  
  860. void CRichEditView::OnParaLeft()
  861. {
  862.     OnParaAlign(PFA_LEFT);
  863. }
  864.  
  865. void CRichEditView::OnUpdateParaLeft(CCmdUI* pCmdUI)
  866. {
  867.     OnUpdateParaAlign(pCmdUI, PFA_LEFT);
  868. }
  869.  
  870. void CRichEditView::OnParaRight()
  871. {
  872.     OnParaAlign(PFA_RIGHT);
  873. }
  874.  
  875. void CRichEditView::OnUpdateParaRight(CCmdUI* pCmdUI)
  876. {
  877.     OnUpdateParaAlign(pCmdUI, PFA_RIGHT);
  878. }
  879.  
  880. void CRichEditView::OnBullet()
  881. {
  882.     GetParaFormatSelection();
  883.     if (m_paraformat.dwMask & PFM_NUMBERING && m_paraformat.wNumbering == PFN_BULLET)
  884.     {
  885.         m_paraformat.wNumbering = 0;
  886.         m_paraformat.dxOffset = 0;
  887.         m_paraformat.dxStartIndent = 0;
  888.         m_paraformat.dwMask = PFM_NUMBERING | PFM_STARTINDENT | PFM_OFFSET;
  889.     }
  890.     else
  891.     {
  892.         m_paraformat.wNumbering = PFN_BULLET;
  893.         m_paraformat.dwMask = PFM_NUMBERING;
  894.         if (m_paraformat.dxOffset == 0)
  895.         {
  896.             m_paraformat.dxOffset = m_nBulletIndent;
  897.             m_paraformat.dwMask = PFM_NUMBERING | PFM_STARTINDENT | PFM_OFFSET;
  898.         }
  899.     }
  900.     SetParaFormat(m_paraformat);
  901. }
  902.  
  903. void CRichEditView::OnUpdateBullet(CCmdUI* pCmdUI)
  904. {
  905.     GetParaFormatSelection();
  906.     pCmdUI->SetCheck( (m_paraformat.dwMask & PFM_NUMBERING) ? ((m_paraformat.wNumbering & PFN_BULLET) ? 1 : 0) : 2);
  907. }
  908.  
  909. void CRichEditView::OnFormatFont()
  910. {
  911.     GetCharFormatSelection();
  912.     CFontDialog dlg(m_charformat, CF_BOTH|CF_NOOEMFONTS);
  913.     if (dlg.DoModal() == IDOK)
  914.     {
  915.         dlg.GetCharFormat(m_charformat);
  916.         SetCharFormat(m_charformat);
  917.     }
  918. }
  919.  
  920. void CRichEditView::OnColorPick(COLORREF cr)
  921. {
  922.     GetCharFormatSelection();
  923.     m_charformat.dwMask = CFM_COLOR;
  924.     m_charformat.dwEffects = NULL;
  925.     m_charformat.crTextColor = cr;
  926.     SetCharFormat(m_charformat);
  927. }
  928.  
  929. void CRichEditView::OnColorDefault()
  930. {
  931.     GetCharFormatSelection();
  932.     m_charformat.dwMask = CFM_COLOR;
  933.     m_charformat.dwEffects = CFE_AUTOCOLOR;
  934.     SetCharFormat(m_charformat);
  935. }
  936.  
  937. void CRichEditView::OnEditPasteSpecial()
  938. {
  939.     COlePasteSpecialDialog dlg;
  940.     dlg.AddStandardFormats();
  941.     dlg.AddFormat(_oleData.cfRichTextFormat, TYMED_HGLOBAL, AFX_IDS_RTF_FORMAT, FALSE, FALSE);
  942.     dlg.AddFormat(CF_TEXT, TYMED_HGLOBAL, AFX_IDS_TEXT_FORMAT, FALSE, FALSE);
  943.  
  944.     if (dlg.DoModal() != IDOK)
  945.         return;
  946.  
  947.     DVASPECT dv = dlg.GetDrawAspect();
  948.     HMETAFILE hMF = (HMETAFILE)dlg.GetIconicMetafile();
  949.     CLIPFORMAT cf =
  950.         dlg.m_ps.arrPasteEntries[dlg.m_ps.nSelectedIndex].fmtetc.cfFormat;
  951.  
  952.     CWaitCursor wait;
  953.     SetCapture();
  954.  
  955.     // we set the target type so that QueryAcceptData know what to paste
  956.     m_nPasteType = dlg.GetSelectionType();
  957.     GetRichEditCtrl().PasteSpecial(cf, dv, hMF);
  958.     m_nPasteType = 0;
  959.  
  960.     ReleaseCapture();
  961. }
  962.  
  963. void CRichEditView::OnUpdateEditPasteSpecial(CCmdUI* pCmdUI)
  964. {
  965.     pCmdUI->Enable(CanPaste());
  966. }
  967.  
  968. void CRichEditView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  969. {
  970.     if (nChar == VK_F10 && GetKeyState(VK_SHIFT) < 0)
  971.     {
  972.         CRect rect;
  973.         GetClientRect(rect);
  974.         CPoint pt = rect.CenterPoint();
  975.         SendMessage(WM_CONTEXTMENU, (WPARAM)m_hWnd, MAKELPARAM(pt.x, pt.y));
  976.     }
  977.     else
  978.         CCtrlView::OnKeyDown(nChar, nRepCnt, nFlags);
  979. }
  980.  
  981. void CRichEditView::OnDropFiles(HDROP hDropInfo)
  982. {
  983.     TCHAR szFileName[_MAX_PATH];
  984.     UINT nFileCount = ::DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0);
  985.     ASSERT(nFileCount != 0);
  986.     CHARRANGE cr;
  987.     GetRichEditCtrl().GetSel(cr);
  988.     int nMin = cr.cpMin;
  989.     for (UINT i=0;i<nFileCount;i++)
  990.     {
  991.         ::DragQueryFile(hDropInfo, i, szFileName, _MAX_PATH);
  992.         InsertFileAsObject(szFileName);
  993.         GetRichEditCtrl().GetSel(cr);
  994.         cr.cpMin = cr.cpMax;
  995.         GetRichEditCtrl().SetSel(cr);
  996.         UpdateWindow();
  997.     }
  998.     cr.cpMin = nMin;
  999.     GetRichEditCtrl().SetSel(cr);
  1000.     ::DragFinish(hDropInfo);
  1001. }
  1002.  
  1003. void CRichEditView::OnDevModeChange(LPTSTR /*lpDeviceName*/)
  1004. {
  1005.     // WM_DEVMODECHANGE forwarded by the main window of the app
  1006.     CDC dc;
  1007.     AfxGetApp()->CreatePrinterDC(dc);
  1008.     OnPrinterChanged(dc);
  1009. }
  1010.  
  1011. /////////////////////////////////////////////////////////////////////////////
  1012. // CRichEditView attributes
  1013.  
  1014. BOOL AFX_CDECL CRichEditView::IsRichEditFormat(CLIPFORMAT cf)
  1015. {
  1016.     return ((cf == _oleData.cfRichTextFormat) ||
  1017.         (cf == _oleData.cfRichTextAndObjects) || (cf == CF_TEXT));
  1018. }
  1019.  
  1020. BOOL CRichEditView::CanPaste() const
  1021. {
  1022.     return (CountClipboardFormats() != 0) &&
  1023.         (IsClipboardFormatAvailable(CF_TEXT) ||
  1024.         IsClipboardFormatAvailable(_oleData.cfRichTextFormat) ||
  1025.         IsClipboardFormatAvailable(_oleData.cfEmbedSource) ||
  1026.         IsClipboardFormatAvailable(_oleData.cfEmbeddedObject) ||
  1027.         IsClipboardFormatAvailable(_oleData.cfFileName) ||
  1028.         IsClipboardFormatAvailable(_oleData.cfFileNameW) ||
  1029.         IsClipboardFormatAvailable(CF_METAFILEPICT) ||
  1030.         IsClipboardFormatAvailable(CF_DIB) ||
  1031.         IsClipboardFormatAvailable(CF_BITMAP) ||
  1032.         GetRichEditCtrl().CanPaste());
  1033. }
  1034.  
  1035. CHARFORMAT& CRichEditView::GetCharFormatSelection()
  1036. {
  1037.     if (m_bSyncCharFormat)
  1038.     {
  1039.         GetRichEditCtrl().GetSelectionCharFormat(m_charformat);
  1040.         m_bSyncCharFormat = FALSE;
  1041.     }
  1042.     return m_charformat;
  1043. }
  1044.  
  1045. PARAFORMAT& CRichEditView::GetParaFormatSelection()
  1046. {
  1047.     if (m_bSyncParaFormat)
  1048.     {
  1049.         GetRichEditCtrl().GetParaFormat(m_paraformat);
  1050.         m_bSyncParaFormat = FALSE;
  1051.     }
  1052.     return m_paraformat;
  1053. }
  1054.  
  1055. void CRichEditView::SetCharFormat(CHARFORMAT cf)
  1056. {
  1057.     CWaitCursor wait;
  1058.     GetRichEditCtrl().SetSelectionCharFormat(cf);
  1059.     m_bSyncCharFormat = TRUE;
  1060. }
  1061.  
  1062. BOOL CRichEditView::SetParaFormat(PARAFORMAT& pf)
  1063. {
  1064.     BOOL bRetVal;
  1065.     CWaitCursor wait;
  1066.     bRetVal = GetRichEditCtrl().SetParaFormat(pf);
  1067.     m_bSyncParaFormat = TRUE;
  1068.     return bRetVal;
  1069. }
  1070.  
  1071. CRichEditCntrItem* CRichEditView::GetSelectedItem() const
  1072. {
  1073.     ASSERT(m_lpRichEditOle != NULL);
  1074.     CRichEditDoc* pDoc = GetDocument();
  1075.     CRichEditCntrItem* pItem = NULL;
  1076.  
  1077.     CReObject reo;
  1078.     HRESULT hr = m_lpRichEditOle->GetObject(REO_IOB_SELECTION, &reo,
  1079.         REO_GETOBJ_ALL_INTERFACES);
  1080.     //reo's interfaces are all in UNICODE
  1081.     if (GetScode(hr) == S_OK)
  1082.     {
  1083.         pItem = pDoc->LookupItem(reo.poleobj);
  1084.         if (pItem == NULL)
  1085.             pItem = pDoc->CreateClientItem(&reo);
  1086.         ASSERT(pItem != NULL);
  1087.     }
  1088.     return pItem;
  1089. }
  1090.  
  1091. CRichEditCntrItem* CRichEditView::GetInPlaceActiveItem() const
  1092. {
  1093.     ASSERT(m_lpRichEditOle != NULL);
  1094.     CRichEditDoc* pDoc = GetDocument();
  1095.     CRichEditCntrItem* pItem = NULL;
  1096.  
  1097.     CReObject reo;
  1098.     HRESULT hr = m_lpRichEditOle->GetObject(REO_IOB_SELECTION, &reo,
  1099.         REO_GETOBJ_ALL_INTERFACES);
  1100.     //reo's interfaces are all in UNICODE
  1101.     if (GetScode(hr) == S_OK && (reo.dwFlags & REO_INPLACEACTIVE))
  1102.     {
  1103.         pItem = pDoc->LookupItem(reo.poleobj);
  1104.         if (pItem == NULL)
  1105.             pItem = pDoc->CreateClientItem(&reo);
  1106.         ASSERT(pItem != NULL);
  1107.     }
  1108.     return pItem;
  1109. }
  1110.  
  1111. /////////////////////////////////////////////////////////////////////////////
  1112. // CRichEditView operations
  1113. HRESULT CRichEditView::InsertItem(CRichEditCntrItem* pItem)
  1114. {
  1115.     ASSERT(m_lpRichEditOle != NULL);
  1116.     CReObject reo(pItem);
  1117.     reo.cp = REO_CP_SELECTION;
  1118.  
  1119.     HRESULT hr = m_lpRichEditOle->InsertObject(&reo);
  1120.  
  1121.     CHARRANGE cr;
  1122.     GetRichEditCtrl().GetSel(cr);
  1123.     cr.cpMin = cr.cpMax -1;
  1124.     GetRichEditCtrl().SetSel(cr);
  1125.     return hr;
  1126. }
  1127.  
  1128. void CRichEditView::InsertFileAsObject(LPCTSTR lpszFileName)
  1129. {
  1130.     CString str = lpszFileName;
  1131.     CWaitCursor wait;
  1132.     CRichEditCntrItem* pItem = NULL;
  1133.     TRY
  1134.     {
  1135.         // create item from dialog results
  1136.         pItem = GetDocument()->CreateClientItem();
  1137.         pItem->m_bLock = TRUE;
  1138.         if (!pItem->CreateFromFile(str))
  1139.             AfxThrowMemoryException();  // any exception will do
  1140.         pItem->UpdateLink();
  1141.         InsertItem(pItem);
  1142.         pItem->m_bLock = FALSE;
  1143.     }
  1144.     CATCH(CException, e)
  1145.     {
  1146.         if (pItem != NULL)
  1147.         {
  1148.             pItem->m_bLock = FALSE;
  1149.             ASSERT_VALID(pItem);
  1150.             pItem->Delete();
  1151.         }
  1152.     }
  1153.     END_CATCH
  1154. }
  1155.  
  1156. void CRichEditView::DoPaste(COleDataObject& dataobj, CLIPFORMAT cf, HMETAFILEPICT hMetaPict)
  1157. {
  1158.     CWaitCursor wait;
  1159.  
  1160.     CRichEditCntrItem* pItem = NULL;
  1161.     TRY
  1162.     {
  1163.         // create item from dialog results
  1164.         pItem = GetDocument()->CreateClientItem();
  1165.         pItem->m_bLock = TRUE;
  1166.  
  1167.         if (m_nPasteType == COlePasteSpecialDialog::pasteLink)      // paste link
  1168.         {
  1169.             if (!pItem->CreateLinkFromData(&dataobj))
  1170.                 AfxThrowMemoryException();  // any exception will do
  1171.         }
  1172.         else if (m_nPasteType == COlePasteSpecialDialog::pasteNormal)
  1173.         {
  1174.             if (!pItem->CreateFromData(&dataobj))
  1175.                 AfxThrowMemoryException();      // any exception will do
  1176.         }
  1177.         else if (m_nPasteType == COlePasteSpecialDialog::pasteStatic)
  1178.         {
  1179.             if (!pItem->CreateStaticFromData(&dataobj))
  1180.                 AfxThrowMemoryException();      // any exception will do
  1181.         }
  1182.         else
  1183.         {
  1184.             // paste embedded
  1185.             if (!pItem->CreateFromData(&dataobj) &&
  1186.                 !pItem->CreateStaticFromData(&dataobj))
  1187.             {
  1188.                 AfxThrowMemoryException();      // any exception will do
  1189.             }
  1190.         }
  1191.  
  1192.         if (cf == 0)
  1193.         {
  1194.             // copy the current iconic representation
  1195.             FORMATETC fmtetc;
  1196.             fmtetc.cfFormat = CF_METAFILEPICT;
  1197.             fmtetc.dwAspect = DVASPECT_ICON;
  1198.             fmtetc.ptd = NULL;
  1199.             fmtetc.tymed = TYMED_MFPICT;
  1200.             fmtetc.lindex = 1;
  1201.             HGLOBAL hObj = dataobj.GetGlobalData(CF_METAFILEPICT, &fmtetc);
  1202.             if (hObj != NULL)
  1203.             {
  1204.                 pItem->SetIconicMetafile(hObj);
  1205.                 // the following code is an easy way to free a metafile pict
  1206.                 STGMEDIUM stgMed;
  1207.                 memset(&stgMed, 0, sizeof(stgMed));
  1208.                 stgMed.tymed = TYMED_MFPICT;
  1209.                 stgMed.hGlobal = hObj;
  1210.                 ReleaseStgMedium(&stgMed);
  1211.             }
  1212.  
  1213.             // set the current drawing aspect
  1214.             hObj = dataobj.GetGlobalData((CLIPFORMAT)_oleData.cfObjectDescriptor);
  1215.             if (hObj != NULL)
  1216.             {
  1217.                 ASSERT(hObj != NULL);
  1218.                 // got CF_OBJECTDESCRIPTOR ok.  Lock it down and extract size.
  1219.                 LPOBJECTDESCRIPTOR pObjDesc = (LPOBJECTDESCRIPTOR)GlobalLock(hObj);
  1220.                 ASSERT(pObjDesc != NULL);
  1221.                 ((COleClientItem*)pItem)->SetDrawAspect((DVASPECT)pObjDesc->dwDrawAspect);
  1222.                 GlobalUnlock(hObj);
  1223.                 GlobalFree(hObj);
  1224.             }
  1225.         }
  1226.         else
  1227.         {
  1228.             if (hMetaPict != NULL)
  1229.             {
  1230.                 pItem->SetIconicMetafile(hMetaPict);
  1231.                 ((COleClientItem*)pItem)->SetDrawAspect(DVASPECT_ICON);
  1232.             }
  1233.             else
  1234.                 ((COleClientItem*)pItem)->SetDrawAspect(DVASPECT_CONTENT);
  1235.         }
  1236.  
  1237.  
  1238.         HRESULT hr = InsertItem(pItem);
  1239.         pItem->UpdateItemType();
  1240.  
  1241.         pItem->m_bLock = FALSE;
  1242.  
  1243.         if (hr != NOERROR)
  1244.             AfxThrowOleException(hr);
  1245.  
  1246.     }
  1247.     CATCH(CException, e)
  1248.     {
  1249.         if (pItem != NULL)
  1250.         {
  1251.             pItem->m_bLock = FALSE;
  1252.             ASSERT_VALID(pItem);
  1253.             pItem->Delete();
  1254.         }
  1255.     }
  1256.     END_CATCH
  1257. }
  1258.  
  1259. /////////////////////////////////////////////////////////////////////////////
  1260. // CRichEditView virtuals
  1261.  
  1262. void CRichEditView::OnPrinterChanged(const CDC& dcPrinter)
  1263. {
  1264.     // this is typically called by the view when it gets a WM_DEVMODECHANGE
  1265.     // also called during page setup
  1266.     CSize size;
  1267.     if (dcPrinter.m_hDC != NULL)
  1268.     {
  1269.         // this will fill in the page size
  1270.         size.cx = MulDiv(dcPrinter.GetDeviceCaps(PHYSICALWIDTH), 1440,
  1271.             dcPrinter.GetDeviceCaps(LOGPIXELSX));
  1272.         size.cy = MulDiv(dcPrinter.GetDeviceCaps(PHYSICALHEIGHT), 1440,
  1273.             dcPrinter.GetDeviceCaps(LOGPIXELSY));
  1274.     }
  1275.     else
  1276.         size = CSize(8*1440+720, 11*1440); // 8.5" by 11"
  1277.     if (GetPaperSize() != size)
  1278.     {
  1279.         SetPaperSize(size);
  1280.         if (m_nWordWrap == WrapToTargetDevice) //wrap to ruler
  1281.             WrapChanged();
  1282.     }
  1283. }
  1284.  
  1285. BOOL CRichEditView::OnPasteNativeObject(LPSTORAGE)
  1286. {
  1287.     // use this function to pull out native data from an embedded object
  1288.     // one would typically do this by create a COleStreamFile and attaching it
  1289.     // to an archive
  1290.     return FALSE;
  1291. }
  1292.  
  1293. HMENU CRichEditView::GetContextMenu(WORD, LPOLEOBJECT, CHARRANGE* )
  1294. {
  1295.     return NULL;
  1296. }
  1297.  
  1298. HRESULT CRichEditView::GetClipboardData(CHARRANGE* /*lpchrg*/, DWORD /*reco*/,
  1299.     LPDATAOBJECT /*lpRichDataObj*/, LPDATAOBJECT* /*lplpdataobj*/)
  1300. {
  1301.     return E_NOTIMPL;
  1302. }
  1303.  
  1304. HRESULT CRichEditView::QueryAcceptData(LPDATAOBJECT lpdataobj,
  1305.     CLIPFORMAT* lpcfFormat, DWORD /*dwReco*/, BOOL bReally, HGLOBAL hMetaPict)
  1306. {
  1307.     ASSERT(lpcfFormat != NULL);
  1308.     if (!bReally) // not actually pasting
  1309.         return S_OK;
  1310.     // if direct pasting a particular native format allow it
  1311.     if (IsRichEditFormat(*lpcfFormat))
  1312.         return S_OK;
  1313.  
  1314.     COleDataObject dataobj;
  1315.     dataobj.Attach(lpdataobj, FALSE);
  1316.     // if format is 0, then force particular formats if available
  1317.     if (*lpcfFormat == 0 && (m_nPasteType == 0))
  1318.     {
  1319.         if (dataobj.IsDataAvailable((CLIPFORMAT)_oleData.cfRichTextAndObjects)) // native avail, let richedit do as it wants
  1320.             return S_OK;
  1321.         else if (dataobj.IsDataAvailable((CLIPFORMAT)_oleData.cfRichTextFormat))
  1322.         {
  1323.             *lpcfFormat = (CLIPFORMAT)_oleData.cfRichTextFormat;
  1324.             return S_OK;
  1325.         }
  1326.         else if (dataobj.IsDataAvailable(CF_TEXT))
  1327.         {
  1328.             *lpcfFormat = CF_TEXT;
  1329.             return S_OK;
  1330.         }
  1331.     }
  1332.     // paste OLE formats
  1333.  
  1334.     DoPaste(dataobj, *lpcfFormat, hMetaPict);
  1335.     return S_FALSE;
  1336. }
  1337.  
  1338. HRESULT CRichEditView::GetWindowContext(LPOLEINPLACEFRAME* lplpFrame,
  1339.     LPOLEINPLACEUIWINDOW* lplpDoc, LPOLEINPLACEFRAMEINFO lpFrameInfo)
  1340. {
  1341.     CRichEditCntrItem* pItem = GetSelectedItem();
  1342.     if (pItem == NULL)
  1343.         return E_FAIL;
  1344.     pItem->m_pView = this;
  1345.     HRESULT hr = pItem->GetWindowContext(lplpFrame, lplpDoc, lpFrameInfo);
  1346.     pItem->m_pView = NULL;
  1347.     return hr;
  1348. }
  1349.  
  1350. HRESULT CRichEditView::ShowContainerUI(BOOL b)
  1351. {
  1352.     CRichEditCntrItem* pItem = GetSelectedItem();
  1353.     if (pItem == NULL)
  1354.         return E_FAIL;
  1355.     if (b)
  1356.         pItem->m_pView = this;
  1357.     HRESULT hr = pItem->ShowContainerUI(b);
  1358.     if (FAILED(hr) || !b)
  1359.         pItem->m_pView = NULL;
  1360.     return hr;
  1361. }
  1362.  
  1363.  
  1364. /////////////////////////////////////////////////////////////////////////////
  1365. // CRichEditView Find & Replace
  1366.  
  1367. void CRichEditView::AdjustDialogPosition(CDialog* pDlg)
  1368. {
  1369.     ASSERT(pDlg != NULL);
  1370.     long lStart, lEnd;
  1371.     GetRichEditCtrl().GetSel(lStart, lEnd);
  1372.     CPoint point = GetRichEditCtrl().GetCharPos(lStart);
  1373.     ClientToScreen(&point);
  1374.     CRect rectDlg;
  1375.     pDlg->GetWindowRect(&rectDlg);
  1376.     if (rectDlg.PtInRect(point))
  1377.     {
  1378.         if (point.y > rectDlg.Height())
  1379.             rectDlg.OffsetRect(0, point.y - rectDlg.bottom - 20);
  1380.         else
  1381.         {
  1382.             int nVertExt = GetSystemMetrics(SM_CYSCREEN);
  1383.             if (point.y + rectDlg.Height() < nVertExt)
  1384.                 rectDlg.OffsetRect(0, 40 + point.y - rectDlg.top);
  1385.         }
  1386.         pDlg->MoveWindow(&rectDlg);
  1387.     }
  1388. }
  1389.  
  1390. void CRichEditView::OnEditFindReplace(BOOL bFindOnly)
  1391. {
  1392.     ASSERT_VALID(this);
  1393.     m_bFirstSearch = TRUE;
  1394.     _AFX_RICHEDIT_STATE* pEditState = _afxRichEditState;
  1395.     if (pEditState->pFindReplaceDlg != NULL)
  1396.     {
  1397.         if (pEditState->bFindOnly == bFindOnly)
  1398.         {
  1399.             pEditState->pFindReplaceDlg->SetActiveWindow();
  1400.             pEditState->pFindReplaceDlg->ShowWindow(SW_SHOW);
  1401.             return;
  1402.         }
  1403.         else
  1404.         {
  1405.             ASSERT(pEditState->bFindOnly != bFindOnly);
  1406.             pEditState->pFindReplaceDlg->SendMessage(WM_CLOSE);
  1407.             ASSERT(pEditState->pFindReplaceDlg == NULL);
  1408.             ASSERT_VALID(this);
  1409.         }
  1410.     }
  1411.     CString strFind = GetRichEditCtrl().GetSelText();
  1412.     // if selection is empty or spans multiple lines use old find text
  1413.     if (strFind.IsEmpty() || (strFind.FindOneOf(_T("\n\r")) != -1))
  1414.         strFind = pEditState->strFind;
  1415.     CString strReplace = pEditState->strReplace;
  1416.     pEditState->pFindReplaceDlg = new CFindReplaceDialog;
  1417.     ASSERT(pEditState->pFindReplaceDlg != NULL);
  1418.     DWORD dwFlags = NULL;
  1419.     if (pEditState->bNext)
  1420.         dwFlags |= FR_DOWN;
  1421.     if (pEditState->bCase)
  1422.         dwFlags |= FR_MATCHCASE;
  1423.     if (pEditState->bWord)
  1424.         dwFlags |= FR_WHOLEWORD;
  1425.     // hide stuff that RichEdit doesn't support
  1426.     dwFlags |= FR_HIDEUPDOWN;
  1427.     if (!pEditState->pFindReplaceDlg->Create(bFindOnly, strFind,
  1428.         strReplace, dwFlags, this))
  1429.     {
  1430.         pEditState->pFindReplaceDlg = NULL;
  1431.         ASSERT_VALID(this);
  1432.         return;
  1433.     }
  1434.     ASSERT(pEditState->pFindReplaceDlg != NULL);
  1435.     pEditState->bFindOnly = bFindOnly;
  1436.     pEditState->pFindReplaceDlg->SetActiveWindow();
  1437.     pEditState->pFindReplaceDlg->ShowWindow(SW_SHOW);
  1438.     ASSERT_VALID(this);
  1439. }
  1440.  
  1441. void CRichEditView::OnFindNext(LPCTSTR lpszFind, BOOL bNext, BOOL bCase, BOOL bWord)
  1442. {
  1443.     ASSERT_VALID(this);
  1444.  
  1445.     _AFX_RICHEDIT_STATE* pEditState = _afxRichEditState;
  1446.     pEditState->strFind = lpszFind;
  1447.     pEditState->bCase = bCase;
  1448.     pEditState->bWord = bWord;
  1449.     pEditState->bNext = bNext;
  1450.  
  1451.     if (!FindText(pEditState))
  1452.         TextNotFound(pEditState->strFind);
  1453.     else
  1454.         AdjustDialogPosition(pEditState->pFindReplaceDlg);
  1455.     ASSERT_VALID(this);
  1456. }
  1457.  
  1458. void CRichEditView::OnReplaceSel(LPCTSTR lpszFind, BOOL bNext, BOOL bCase,
  1459.     BOOL bWord, LPCTSTR lpszReplace)
  1460. {
  1461.     ASSERT_VALID(this);
  1462.     _AFX_RICHEDIT_STATE* pEditState = _afxRichEditState;
  1463.     pEditState->strFind = lpszFind;
  1464.     pEditState->strReplace = lpszReplace;
  1465.     pEditState->bCase = bCase;
  1466.     pEditState->bWord = bWord;
  1467.     pEditState->bNext = bNext;
  1468.  
  1469.     if (!SameAsSelected(pEditState->strFind, pEditState->bCase, pEditState->bWord))
  1470.     {
  1471.         if (!FindText(pEditState))
  1472.             TextNotFound(pEditState->strFind);
  1473.         else
  1474.             AdjustDialogPosition(pEditState->pFindReplaceDlg);
  1475.         return;
  1476.     }
  1477.  
  1478.     GetRichEditCtrl().ReplaceSel(pEditState->strReplace);
  1479.     if (!FindText(pEditState))
  1480.         TextNotFound(pEditState->strFind);
  1481.     else
  1482.         AdjustDialogPosition(pEditState->pFindReplaceDlg);
  1483.     ASSERT_VALID(this);
  1484. }
  1485.  
  1486. void CRichEditView::OnReplaceAll(LPCTSTR lpszFind, LPCTSTR lpszReplace, BOOL bCase, BOOL bWord)
  1487. {
  1488.     ASSERT_VALID(this);
  1489.     _AFX_RICHEDIT_STATE* pEditState = _afxRichEditState;
  1490.     pEditState->strFind = lpszFind;
  1491.     pEditState->strReplace = lpszReplace;
  1492.     pEditState->bCase = bCase;
  1493.     pEditState->bWord = bWord;
  1494.     pEditState->bNext = TRUE;
  1495.  
  1496.     CWaitCursor wait;
  1497.     // no selection or different than what looking for
  1498.     if (!SameAsSelected(pEditState->strFind, pEditState->bCase, pEditState->bWord))
  1499.     {
  1500.         if (!FindText(pEditState))
  1501.         {
  1502.             TextNotFound(pEditState->strFind);
  1503.             return;
  1504.         }
  1505.     }
  1506.  
  1507.     GetRichEditCtrl().HideSelection(TRUE, FALSE);
  1508.     do
  1509.     {
  1510.         GetRichEditCtrl().ReplaceSel(pEditState->strReplace);
  1511.     } while (FindTextSimple(pEditState));
  1512.     TextNotFound(pEditState->strFind);
  1513.     GetRichEditCtrl().HideSelection(FALSE, FALSE);
  1514.  
  1515.     ASSERT_VALID(this);
  1516. }
  1517.  
  1518. LRESULT CRichEditView::OnFindReplaceCmd(WPARAM, LPARAM lParam)
  1519. {
  1520.     ASSERT_VALID(this);
  1521.     CFindReplaceDialog* pDialog = CFindReplaceDialog::GetNotifier(lParam);
  1522.     ASSERT(pDialog != NULL);
  1523.     _AFX_RICHEDIT_STATE* pEditState = _afxRichEditState;
  1524.     ASSERT(pDialog == pEditState->pFindReplaceDlg);
  1525.     if (pDialog->IsTerminating())
  1526.         pEditState->pFindReplaceDlg = NULL;
  1527.     else if (pDialog->FindNext())
  1528.     {
  1529.         OnFindNext(pDialog->GetFindString(), pDialog->SearchDown(),
  1530.             pDialog->MatchCase(), pDialog->MatchWholeWord());
  1531.     }
  1532.     else if (pDialog->ReplaceCurrent())
  1533.     {
  1534.         ASSERT(!pEditState->bFindOnly);
  1535.         OnReplaceSel(pDialog->GetFindString(),
  1536.             pDialog->SearchDown(), pDialog->MatchCase(), pDialog->MatchWholeWord(),
  1537.             pDialog->GetReplaceString());
  1538.     }
  1539.     else if (pDialog->ReplaceAll())
  1540.     {
  1541.         ASSERT(!pEditState->bFindOnly);
  1542.         OnReplaceAll(pDialog->GetFindString(), pDialog->GetReplaceString(),
  1543.             pDialog->MatchCase(), pDialog->MatchWholeWord());
  1544.     }
  1545.     ASSERT_VALID(this);
  1546.     return 0;
  1547. }
  1548.  
  1549. BOOL CRichEditView::SameAsSelected(LPCTSTR lpszCompare, BOOL bCase, BOOL /*bWord*/)
  1550. {
  1551.     // check length first
  1552.     size_t nLen = lstrlen(lpszCompare);
  1553.     long lStartChar, lEndChar;
  1554.     GetRichEditCtrl().GetSel(lStartChar, lEndChar);
  1555.     if (nLen != (size_t)(lEndChar - lStartChar))
  1556.         return FALSE;
  1557.  
  1558.     // length is the same, check contents
  1559.     CString strSelect = GetRichEditCtrl().GetSelText();
  1560.     return (bCase && lstrcmp(lpszCompare, strSelect) == 0) ||
  1561.         (!bCase && lstrcmpi(lpszCompare, strSelect) == 0);
  1562. }
  1563.  
  1564. BOOL CRichEditView::FindText(_AFX_RICHEDIT_STATE* pEditState)
  1565. {
  1566.     ASSERT(pEditState != NULL);
  1567.     return FindText(pEditState->strFind, pEditState->bCase, pEditState->bWord);
  1568. }
  1569.  
  1570. BOOL CRichEditView::FindText(LPCTSTR lpszFind, BOOL bCase, BOOL bWord)
  1571. {
  1572.     ASSERT_VALID(this);
  1573.     CWaitCursor wait;
  1574.     return FindTextSimple(lpszFind, bCase, bWord);
  1575. }
  1576.  
  1577. BOOL CRichEditView::FindTextSimple(_AFX_RICHEDIT_STATE* pEditState)
  1578. {
  1579.     ASSERT(pEditState != NULL);
  1580.     return FindTextSimple(pEditState->strFind, pEditState->bCase, pEditState->bWord);
  1581. }
  1582.  
  1583. BOOL CRichEditView::FindTextSimple(LPCTSTR lpszFind, BOOL bCase, BOOL bWord)
  1584. {
  1585.     USES_CONVERSION;
  1586.     ASSERT(lpszFind != NULL);
  1587.     FINDTEXTEX ft;
  1588.  
  1589.     GetRichEditCtrl().GetSel(ft.chrg);
  1590.     if (m_bFirstSearch)
  1591.     {
  1592.         m_lInitialSearchPos = ft.chrg.cpMin;
  1593.         m_bFirstSearch = FALSE;
  1594.     }
  1595.  
  1596.     ft.lpstrText = T2A((LPTSTR)lpszFind);
  1597.     if (ft.chrg.cpMin != ft.chrg.cpMax) // i.e. there is a selection
  1598.     {
  1599.  
  1600. #ifndef _UNICODE
  1601.         // If byte at beginning of selection is a DBCS lead byte,
  1602.         // increment by one extra byte.
  1603.         TEXTRANGE textRange;
  1604.         TCHAR ch[2];
  1605.         textRange.chrg.cpMin = ft.chrg.cpMin;
  1606.         textRange.chrg.cpMax = ft.chrg.cpMin + 1;
  1607.         textRange.lpstrText = ch;
  1608.         GetRichEditCtrl().SendMessage(EM_GETTEXTRANGE, 0, (LPARAM)&textRange);
  1609.         if (_istlead(ch[0]))
  1610.         {
  1611.             ASSERT(ft.chrg.cpMax - ft.chrg.cpMin >= 2);
  1612.             ft.chrg.cpMin++;
  1613.         }
  1614. #endif
  1615.  
  1616.         ft.chrg.cpMin++;
  1617.     }
  1618.  
  1619.     if (m_lInitialSearchPos >= 0)
  1620.         ft.chrg.cpMax = GetTextLength();
  1621.     else
  1622.         ft.chrg.cpMax = GetTextLength()+m_lInitialSearchPos;
  1623.  
  1624.     DWORD dwFlags = bCase ? FR_MATCHCASE : 0;
  1625.     dwFlags |= bWord ? FR_WHOLEWORD : 0;
  1626.  
  1627.     // if we find the text return TRUE
  1628.     if (FindAndSelect(dwFlags, ft) != -1)
  1629.         return TRUE;
  1630.     // if the original starting point was not the beginning of the buffer
  1631.     // and we haven't already been here
  1632.     else if (m_lInitialSearchPos > 0)
  1633.     {
  1634.         ft.chrg.cpMin = 0;
  1635.         ft.chrg.cpMax = m_lInitialSearchPos;
  1636.         m_lInitialSearchPos = m_lInitialSearchPos - GetTextLength();
  1637.         return FindAndSelect(dwFlags, ft) != -1;
  1638.     }
  1639.     // not found
  1640.     else
  1641.         return FALSE;
  1642. }
  1643.  
  1644. long CRichEditView::FindAndSelect(DWORD dwFlags, FINDTEXTEX& ft)
  1645. {
  1646.     long index = GetRichEditCtrl().FindText(dwFlags, &ft);
  1647.     if (index != -1) // i.e. we found something
  1648.         GetRichEditCtrl().SetSel(ft.chrgText);
  1649.     return index;
  1650. }
  1651.  
  1652. void CRichEditView::TextNotFound(LPCTSTR lpszFind)
  1653. {
  1654.     ASSERT_VALID(this);
  1655.     m_bFirstSearch = TRUE;
  1656.     OnTextNotFound(lpszFind);
  1657. }
  1658.  
  1659. void CRichEditView::OnTextNotFound(LPCTSTR)
  1660. {
  1661.     MessageBeep(MB_ICONHAND);
  1662. }
  1663.  
  1664. /////////////////////////////////////////////////////////////////////////////
  1665. // CRichEditView diagnostics
  1666.  
  1667. #ifdef _DEBUG
  1668. void CRichEditView::AssertValid() const
  1669. {
  1670.     CCtrlView::AssertValid();
  1671.     ASSERT_VALID(&m_aPageStart);
  1672.     _AFX_RICHEDIT_STATE* pEditState = _afxRichEditState;
  1673.     if (pEditState->pFindReplaceDlg != NULL)
  1674.         ASSERT_VALID(pEditState->pFindReplaceDlg);
  1675. }
  1676.  
  1677. void CRichEditView::Dump(CDumpContext& dc) const
  1678. {
  1679.     CCtrlView::Dump(dc);
  1680.     AFX_DUMP1(dc, "\nm_aPageStart ", &m_aPageStart);
  1681.     AFX_DUMP0(dc, "\n Static Member Data:");
  1682.     _AFX_RICHEDIT_STATE* pEditState = _afxRichEditState;
  1683.     if (pEditState->pFindReplaceDlg != NULL)
  1684.     {
  1685.         AFX_DUMP1(dc, "\npFindReplaceDlg = ",
  1686.             (void*)pEditState->pFindReplaceDlg);
  1687.         AFX_DUMP1(dc, "\nbFindOnly = ", pEditState->bFindOnly);
  1688.     }
  1689.     AFX_DUMP1(dc, "\nstrFind = ", pEditState->strFind);
  1690.     AFX_DUMP1(dc, "\nstrReplace = ", pEditState->strReplace);
  1691.     AFX_DUMP1(dc, "\nbCase = ", pEditState->bCase);
  1692.     AFX_DUMP1(dc, "\nbWord = ", pEditState->bWord);
  1693.     AFX_DUMP1(dc, "\nbNext = ", pEditState->bNext);
  1694. }
  1695. #endif //_DEBUG
  1696.  
  1697. /////////////////////////////////////////////////////////////////////////////
  1698. // OLE Client support and commands
  1699.  
  1700. BOOL CRichEditView::IsSelected(const CObject* pDocItem) const
  1701. {
  1702.     return (pDocItem == GetSelectedItem());
  1703. }
  1704.  
  1705. /////////////////////////////////////////////////////////////////////////////
  1706. // CRichEditDoc
  1707.  
  1708. CRichEditDoc::CRichEditDoc()
  1709. {
  1710.     m_bRTF = TRUE;
  1711.     m_bUpdateObjectCache = FALSE;
  1712.     ASSERT_VALID(this);
  1713. }
  1714.  
  1715. CRichEditView* CRichEditDoc::GetView() const
  1716. {
  1717.     // find the first view - if there are no views
  1718.     // we must return NULL
  1719.  
  1720.     POSITION pos = GetFirstViewPosition();
  1721.     if (pos == NULL)
  1722.         return NULL;
  1723.  
  1724.     // find the first view that is a CRichEditView
  1725.  
  1726.     CView* pView;
  1727.     while (pos != NULL)
  1728.     {
  1729.         pView = GetNextView(pos);
  1730.         if (pView->IsKindOf(RUNTIME_CLASS(CRichEditView)))
  1731.             return (CRichEditView*) pView;
  1732.     }
  1733.  
  1734.     // can't find one--return NULL
  1735.  
  1736.     return NULL;
  1737. }
  1738.  
  1739. BOOL CRichEditDoc::IsModified()
  1740. {
  1741.     return GetView()->GetRichEditCtrl().GetModify();
  1742. }
  1743.  
  1744. void CRichEditDoc::SetModifiedFlag(BOOL bModified)
  1745. {
  1746.     GetView()->GetRichEditCtrl().SetModify(bModified);
  1747.     ASSERT(!!GetView()->GetRichEditCtrl().GetModify() == !!bModified);
  1748. }
  1749.  
  1750. COleClientItem* CRichEditDoc::GetInPlaceActiveItem(CWnd* pWnd)
  1751. {
  1752.     ASSERT_KINDOF(CRichEditView, pWnd);
  1753.     CRichEditView* pView = (CRichEditView*)pWnd;
  1754.     return pView->GetInPlaceActiveItem();
  1755. }
  1756.  
  1757. void CRichEditDoc::SetPathName(LPCTSTR lpszPathName, BOOL bAddToMRU)
  1758. {
  1759.     // we call CDocument and not COleServerDoc because we don't want to do the
  1760.     // SetHostNames stuff here.  The richedit will do it. And we tell the richedit
  1761.     // in SetTitle
  1762.     CDocument::SetPathName(lpszPathName, bAddToMRU);
  1763. }
  1764.  
  1765. void CRichEditDoc::SetTitle(LPCTSTR lpszTitle)
  1766. {
  1767.     USES_CONVERSION;
  1768.     COleServerDoc::SetTitle(lpszTitle);
  1769.     CRichEditView *pView = GetView();
  1770.     ASSERT(pView != NULL);
  1771.     ASSERT(pView->m_lpRichEditOle != NULL);
  1772.     pView->m_lpRichEditOle->SetHostNames(T2CA(AfxGetAppName()),
  1773.         T2CA(lpszTitle));
  1774. }
  1775.  
  1776. CRichEditCntrItem* CRichEditDoc::LookupItem(LPOLEOBJECT lpobj) const
  1777. {
  1778.     POSITION pos = COleServerDoc::GetStartPosition();
  1779.     CRichEditCntrItem* pItem;
  1780.     while (pos != NULL)
  1781.     {
  1782.         pItem = (CRichEditCntrItem*) COleServerDoc::GetNextItem(pos);
  1783.         // delete item is right type and not under construction
  1784.         if (pItem->IsKindOf(RUNTIME_CLASS(CRichEditCntrItem)) &&
  1785.             pItem->m_lpObject == lpobj)
  1786.         {
  1787.             return pItem;
  1788.         }
  1789.     }
  1790.     return NULL;
  1791. }
  1792.  
  1793. CRichEditCntrItem* CRichEditDoc::CreateClientItem(REOBJECT* preo) const
  1794. {
  1795.     // cast away constness of this
  1796.     return new CRichEditCntrItem(preo, (CRichEditDoc*)this);
  1797.     // a derived class typically needs  to return its own item of a class
  1798.     // derived from CRichEditCntrItem
  1799. }
  1800.  
  1801. void CRichEditDoc::MarkItemsClear() const
  1802. {
  1803.     POSITION pos = COleServerDoc::GetStartPosition();
  1804.     CRichEditCntrItem* pItem;
  1805.     while (pos != NULL)
  1806.     {
  1807.         pItem = (CRichEditCntrItem*) COleServerDoc::GetNextItem(pos);
  1808.         // Mark item as not in use unless under construction (i.e. m_lpObject == NULL)
  1809.         if (pItem->IsKindOf(RUNTIME_CLASS(CRichEditCntrItem)))
  1810.             pItem->Mark(pItem->m_lpObject == NULL);
  1811.     }
  1812. }
  1813.  
  1814. void CRichEditDoc::DeleteUnmarkedItems() const
  1815. {
  1816.     POSITION pos = COleServerDoc::GetStartPosition();
  1817.     CRichEditCntrItem* pItem;
  1818.     while (pos != NULL)
  1819.     {
  1820.         pItem = (CRichEditCntrItem*) COleServerDoc::GetNextItem(pos);
  1821.         // Mark item as not in use unless under construction (i.e. m_lpObject == NULL)
  1822.         if (pItem->IsKindOf(RUNTIME_CLASS(CRichEditCntrItem)) && !pItem->IsMarked())
  1823.             delete pItem;
  1824.     }
  1825. }
  1826.  
  1827. POSITION CRichEditDoc::GetStartPosition() const
  1828. {
  1829.     if (m_bUpdateObjectCache)
  1830.         ((CRichEditDoc*)this)->UpdateObjectCache(); //cast away const
  1831.     return COleServerDoc::GetStartPosition();
  1832. }
  1833.  
  1834. void CRichEditDoc::UpdateObjectCache()
  1835. {
  1836.     CRichEditView* pView = GetView();
  1837.     CRichEditCntrItem* pItem;
  1838.     if (pView != NULL)
  1839.     {
  1840.         ASSERT(pView->m_lpRichEditOle != NULL);
  1841.         MarkItemsClear();
  1842.         long i,nCount = pView->m_lpRichEditOle->GetObjectCount();
  1843.         for (i=0;i<nCount;i++)
  1844.         {
  1845.             CReObject reo; // needs to be in here so destructor called to release interfaces
  1846.             HRESULT hr = pView->m_lpRichEditOle->GetObject(i, &reo, REO_GETOBJ_ALL_INTERFACES);
  1847.             //reo interfaces are UNICODE
  1848.             ASSERT(SUCCEEDED(hr));
  1849.             if (GetScode(hr) == S_OK)
  1850.             {
  1851.                 pItem = LookupItem(reo.poleobj);
  1852.                 if (pItem == NULL)
  1853.                 {
  1854.                     pItem = ((CRichEditDoc*)this)->CreateClientItem(&reo);
  1855.                     pItem->UpdateItemType();
  1856.                 }
  1857.                 ASSERT(pItem != NULL);
  1858.                 pItem->Mark(TRUE);
  1859.             }
  1860.         }
  1861.         DeleteUnmarkedItems();
  1862.     }
  1863.     m_bUpdateObjectCache = FALSE;
  1864. }
  1865. /////////////////////////////////////////////////////////////////////////////
  1866. // CRichEditDoc Attributes
  1867.  
  1868. COleClientItem* CRichEditDoc::GetPrimarySelectedItem(CView* pView)
  1869. {
  1870.     ASSERT(pView->IsKindOf(RUNTIME_CLASS(CRichEditView)));
  1871.     return ((CRichEditView*)pView)->GetSelectedItem();
  1872. }
  1873.  
  1874. /////////////////////////////////////////////////////////////////////////////
  1875. // CRichEditDoc Operations
  1876.  
  1877. void CRichEditDoc::DeleteContents()
  1878. {
  1879.     COleServerDoc::DeleteContents();
  1880.     CWaitCursor wait;
  1881.     CRichEditView *pView = GetView();
  1882.     if (pView != NULL)
  1883.     {
  1884.         pView->DeleteContents();
  1885.         pView->GetRichEditCtrl().SetModify(FALSE);
  1886.         ASSERT(!pView->GetRichEditCtrl().GetModify());
  1887.     }
  1888. }
  1889.  
  1890. /////////////////////////////////////////////////////////////////////////////
  1891. // CRichEditDoc serialization
  1892.  
  1893. void CRichEditDoc::Serialize(CArchive& ar)
  1894. {
  1895.     CRichEditView *pView = GetView();
  1896.     if (pView != NULL)
  1897.         pView->Serialize(ar);
  1898.     // we don't call the base class COleServerDoc::Serialize
  1899.     // because we don't want the client items serialized
  1900.     // the client items are handled directly by the RichEdit control
  1901. }
  1902.  
  1903. /////////////////////////////////////////////////////////////////////////////
  1904. // CRichEditDoc diagnostics
  1905.  
  1906. #ifdef _DEBUG
  1907. void CRichEditDoc::AssertValid() const
  1908. {
  1909.     COleServerDoc::AssertValid();
  1910. }
  1911.  
  1912. void CRichEditDoc::Dump(CDumpContext& dc) const
  1913. {
  1914.     COleServerDoc::Dump(dc);
  1915. }
  1916. #endif //_DEBUG
  1917.  
  1918. /////////////////////////////////////////////////////////////////////////////
  1919. // CRichEditDoc commands
  1920.  
  1921. void CRichEditDoc::PreCloseFrame(CFrameWnd* pFrameArg)
  1922. {
  1923.     ASSERT_VALID(this);
  1924.     ASSERT_VALID(pFrameArg);
  1925.  
  1926.     // turn off redraw so the user doesn't see the deactivation happening
  1927.     BOOL bSetRedraw = FALSE;
  1928.     if (pFrameArg->GetStyle() & WS_VISIBLE)
  1929.     {
  1930.         pFrameArg->SendMessage(WM_SETREDRAW, (WPARAM)FALSE);
  1931.         bSetRedraw = TRUE;
  1932.     }
  1933.  
  1934.     // deactivate any inplace active items on this frame
  1935.     GetView()->m_lpRichEditOle->InPlaceDeactivate();
  1936.  
  1937.     POSITION pos = GetStartPosition();
  1938.     CRichEditCntrItem* pItem;
  1939.     while (pos != NULL)
  1940.     {
  1941.         pItem = (CRichEditCntrItem*) GetNextClientItem(pos);
  1942.         if (pItem == NULL)
  1943.             break;
  1944.         ASSERT(pItem->IsKindOf(RUNTIME_CLASS(CRichEditCntrItem)));
  1945.         pItem->Close();
  1946.     }
  1947.  
  1948.     // turn redraw back on
  1949.     if (bSetRedraw)
  1950.         pFrameArg->SendMessage(WM_SETREDRAW, (WPARAM)TRUE);
  1951. }
  1952.  
  1953. void CRichEditDoc::UpdateModifiedFlag()
  1954. {
  1955.     // don't do anything here
  1956.     // let the richedit handle all of this
  1957. }
  1958.  
  1959. COleServerItem* CRichEditDoc::OnGetEmbeddedItem()
  1960. {
  1961.     ASSERT(FALSE);
  1962.     return NULL;
  1963. }
  1964.  
  1965. /////////////////////////////////////////////////////////////////////////////
  1966. // CRichEditCntrItem implementation
  1967.  
  1968. CRichEditCntrItem::CRichEditCntrItem(REOBJECT *preo, CRichEditDoc* pContainer)
  1969.     : COleClientItem(pContainer)
  1970. {
  1971.     m_bMark = FALSE;
  1972.     m_bLock = FALSE;
  1973.     if (preo != NULL)
  1974.     {
  1975.         ASSERT(preo->poleobj != NULL);
  1976.         ASSERT(preo->pstg != NULL);
  1977.         ASSERT(preo->polesite != NULL);
  1978.         m_lpObject = preo->poleobj;
  1979.         m_lpStorage = preo->pstg;
  1980.         m_lpClientSite = preo->polesite;
  1981.         m_lpObject->AddRef();
  1982.         m_lpStorage->AddRef();
  1983.         m_lpClientSite->AddRef();
  1984.     }
  1985.     else
  1986.     {
  1987.         m_lpObject = NULL;
  1988.         m_lpStorage = NULL;
  1989.         m_lpClientSite = NULL;
  1990.     }
  1991. }
  1992.  
  1993. CRichEditCntrItem::~CRichEditCntrItem()
  1994. {
  1995.     if (m_lpClientSite != NULL)
  1996.         m_lpClientSite->Release();
  1997. }
  1998.  
  1999. void CRichEditCntrItem::OnDeactivateUI(BOOL bUndoable)
  2000. {
  2001.     CView* pView = GetActiveView();
  2002.     if (pView != NULL)
  2003.     {
  2004.         ASSERT(pView->GetParentFrame() != NULL);
  2005.         pView->GetParentFrame()->SendMessage(WM_SETMESSAGESTRING,
  2006.             (WPARAM)AFX_IDS_IDLEMESSAGE);
  2007.     }
  2008.     COleClientItem::OnDeactivateUI(bUndoable);
  2009. }
  2010.  
  2011. HRESULT CRichEditCntrItem::ShowContainerUI(BOOL b)
  2012. {
  2013.     if (!CanActivate())
  2014.         return E_NOTIMPL;
  2015.     if (b)
  2016.     {
  2017.         OnDeactivateUI(FALSE);
  2018.         OnDeactivate();
  2019.     }
  2020.     else
  2021.     {
  2022.         OnActivate();
  2023.         OnActivateUI();
  2024.     }
  2025.     return S_OK;
  2026. }
  2027.  
  2028. BOOL CRichEditCntrItem::OnChangeItemPosition(const CRect& /*rectPos*/)
  2029. {
  2030.     ASSERT_VALID(this);
  2031.  
  2032.     // richedit handles this
  2033.     return FALSE;
  2034. }
  2035.  
  2036. BOOL CRichEditCntrItem::CanActivate()
  2037. {
  2038.     // Editing in-place while the server itself is being edited in-place
  2039.     //  does not work and is not supported.  So, disable in-place
  2040.     //  activation in this case.
  2041.     COleServerDoc* pDoc = DYNAMIC_DOWNCAST(COleServerDoc, GetDocument());
  2042.     if (pDoc != NULL && pDoc->IsInPlaceActive())
  2043.         return FALSE;
  2044.  
  2045.     // otherwise, rely on default behavior
  2046.     return COleClientItem::CanActivate();
  2047. }
  2048.  
  2049. HRESULT CRichEditCntrItem::GetWindowContext(LPOLEINPLACEFRAME* lplpFrame,
  2050.     LPOLEINPLACEUIWINDOW* lplpDoc, LPOLEINPLACEFRAMEINFO lpFrameInfo)
  2051. {
  2052.     CRect rc1,rc2;
  2053.     if (!CanActivate())
  2054.         return E_NOTIMPL;
  2055.     return m_xOleIPSite.GetWindowContext(lplpFrame, lplpDoc, &rc1, &rc2, lpFrameInfo);
  2056. }
  2057.  
  2058. BOOL CRichEditCntrItem::ConvertTo(REFCLSID clsidNew)
  2059. {
  2060.     USES_CONVERSION;
  2061.     LPRICHEDITOLE preole = GetDocument()->GetView()->m_lpRichEditOle;
  2062.     LPOLESTR lpOleStr;
  2063.     OleRegGetUserType(clsidNew, USERCLASSTYPE_FULL, &lpOleStr);
  2064.     LPCTSTR lpsz = OLE2CT(lpOleStr);
  2065.     HRESULT hRes = preole->ConvertObject(REO_IOB_SELECTION, clsidNew, T2CA(lpsz));
  2066.     CoTaskMemFree(lpOleStr);
  2067.     return (SUCCEEDED(hRes));
  2068. }
  2069.  
  2070. BOOL CRichEditCntrItem::ActivateAs(LPCTSTR, REFCLSID clsidOld,
  2071.     REFCLSID clsidNew)
  2072. {
  2073.     LPRICHEDITOLE preole = GetDocument()->GetView()->m_lpRichEditOle;
  2074.     HRESULT hRes = preole->ActivateAs(clsidOld, clsidNew);
  2075.     return (SUCCEEDED(hRes));
  2076. }
  2077.  
  2078. void CRichEditCntrItem::SetDrawAspect(DVASPECT nDrawAspect)
  2079. {
  2080.     LPRICHEDITOLE preole = GetDocument()->GetView()->m_lpRichEditOle;
  2081.     preole->SetDvaspect(REO_IOB_SELECTION, nDrawAspect);
  2082.     COleClientItem::SetDrawAspect(nDrawAspect);
  2083. }
  2084.  
  2085. void CRichEditCntrItem::SyncToRichEditObject(REOBJECT& reo)
  2086. {
  2087.     COleClientItem::SetDrawAspect((DVASPECT)reo.dvaspect);
  2088. }
  2089.  
  2090. /////////////////////////////////////////////////////////////////////////////
  2091. // CRichEditCntrItem diagnostics
  2092.  
  2093. #ifdef _DEBUG
  2094. void CRichEditCntrItem::AssertValid() const
  2095. {
  2096.     COleClientItem::AssertValid();
  2097. }
  2098.  
  2099. void CRichEditCntrItem::Dump(CDumpContext& dc) const
  2100. {
  2101.     COleClientItem::Dump(dc);
  2102. }
  2103. #endif
  2104.  
  2105. /////////////////////////////////////////////////////////////////////////////
  2106.  
  2107. LPOLECLIENTSITE CRichEditCntrItem::GetClientSite()
  2108. {
  2109.     if (m_lpClientSite == NULL)
  2110.     {
  2111.         CRichEditDoc* pDoc = DYNAMIC_DOWNCAST(CRichEditDoc, GetDocument());
  2112.         CRichEditView* pView = DYNAMIC_DOWNCAST(CRichEditView, pDoc->GetView());
  2113.         ASSERT(pView->m_lpRichEditOle != NULL);
  2114.         HRESULT hr = pView->m_lpRichEditOle->GetClientSite(&m_lpClientSite);
  2115.         if (hr != S_OK)
  2116.             AfxThrowOleException(hr);
  2117.     }
  2118.     ASSERT(m_lpClientSite != NULL);
  2119.     return m_lpClientSite;
  2120. }
  2121.  
  2122. /////////////////////////////////////////////////////////////////////////////
  2123.  
  2124. #ifndef _AFX_ENABLE_INLINES
  2125.  
  2126. static const char _szAfxWinInl[] = "afxrich.inl";
  2127. #undef THIS_FILE
  2128. #define THIS_FILE _szAfxWinInl
  2129. #define _AFXRICH_INLINE
  2130. #include "afxrich.inl"
  2131.  
  2132. #endif //_AFX_ENABLE_INLINES
  2133.  
  2134. /////////////////////////////////////////////////////////////////////////////
  2135.  
  2136. #ifdef AFX_INIT_SEG
  2137. #pragma code_seg(AFX_INIT_SEG)
  2138. #endif
  2139.  
  2140. IMPLEMENT_SERIAL(CRichEditCntrItem, COleClientItem, 0)
  2141. IMPLEMENT_DYNAMIC(CRichEditDoc, COleServerDoc)
  2142. IMPLEMENT_DYNCREATE(CRichEditView, CCtrlView)
  2143.  
  2144. /////////////////////////////////////////////////////////////////////////////
  2145.