home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / viewedit.cpp < prev    next >
C/C++ Source or Header  |  1998-06-16  |  34KB  |  1,322 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. #include <ctype.h>
  13.  
  14. #ifdef AFX_CORE4_SEG
  15. #pragma code_seg(AFX_CORE4_SEG)
  16. #endif
  17.  
  18. #ifdef _DEBUG
  19. #undef THIS_FILE
  20. static char THIS_FILE[] = __FILE__;
  21. #endif
  22.  
  23. /////////////////////////////////////////////////////////////////////////////
  24. // CEditView
  25.  
  26. #define new DEBUG_NEW
  27.  
  28. AFX_STATIC const UINT _afxMsgFindReplace = ::RegisterWindowMessage(FINDMSGSTRING);
  29.  
  30. #ifdef _UNICODE
  31.  
  32. AFX_STATIC_DATA HFONT _afxUnicodeFont = 0;
  33.  
  34. void AFX_CDECL AfxEditviewTerm()
  35. {
  36.     AfxDeleteObject((HGDIOBJ*)&_afxUnicodeFont);
  37. }
  38. char _afxEditviewTerm = (char)atexit(&AfxEditviewTerm);
  39.  
  40. #endif //_UNICODE
  41.  
  42. BEGIN_MESSAGE_MAP(CEditView, CCtrlView)
  43.     //{{AFX_MSG_MAP(CEditView)
  44.     ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateNeedSel)
  45.     ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateNeedClip)
  46.     ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_ALL, OnUpdateNeedText)
  47.     ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
  48.     ON_UPDATE_COMMAND_UI(ID_EDIT_FIND, OnUpdateNeedText)
  49.     ON_UPDATE_COMMAND_UI(ID_EDIT_REPLACE, OnUpdateNeedText)
  50.     ON_UPDATE_COMMAND_UI(ID_EDIT_REPEAT, OnUpdateNeedFind)
  51.     ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateNeedSel)
  52.     ON_UPDATE_COMMAND_UI(ID_EDIT_CLEAR, OnUpdateNeedSel)
  53.     ON_CONTROL_REFLECT_EX(EN_CHANGE, OnEditChange)
  54.     ON_WM_CREATE()
  55.     ON_MESSAGE(WM_SETFONT, OnSetFont)
  56.     ON_COMMAND(ID_EDIT_CUT, OnEditCut)
  57.     ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
  58.     ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
  59.     ON_COMMAND(ID_EDIT_CLEAR, OnEditClear)
  60.     ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
  61.     ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
  62.     ON_COMMAND(ID_EDIT_FIND, OnEditFind)
  63.     ON_COMMAND(ID_EDIT_REPLACE, OnEditReplace)
  64.     ON_COMMAND(ID_EDIT_REPEAT, OnEditRepeat)
  65.     ON_WM_DESTROY()
  66.     //}}AFX_MSG_MAP
  67.     // Special registered message for Find and Replace
  68.     ON_REGISTERED_MESSAGE(_afxMsgFindReplace, OnFindReplaceCmd)
  69.     // Standard Print commands (print only - not preview)
  70.     ON_COMMAND(ID_FILE_PRINT, CCtrlView::OnFilePrint)
  71.     ON_COMMAND(ID_FILE_PRINT_DIRECT, CCtrlView::OnFilePrint)
  72. END_MESSAGE_MAP()
  73.  
  74. const AFX_DATADEF DWORD CEditView::dwStyleDefault =
  75.     AFX_WS_DEFAULT_VIEW |
  76.     WS_HSCROLL | WS_VSCROLL |
  77.     ES_AUTOHSCROLL | ES_AUTOVSCROLL |
  78.     ES_MULTILINE | ES_NOHIDESEL;
  79.  
  80. // Operating system specific maximum buffer limit
  81. const AFX_DATADEF UINT CEditView::nMaxSize = 1024U*1024U-1;
  82.  
  83. /////////////////////////////////////////////////////////////////////////////
  84. // _AFX_EDIT_STATE
  85.  
  86. _AFX_EDIT_STATE::_AFX_EDIT_STATE()
  87. {
  88.     // Note: it is only necessary to initialize non-zero data.
  89.  
  90.     bNext = TRUE;
  91. }
  92.  
  93. _AFX_EDIT_STATE::~_AFX_EDIT_STATE()
  94. {
  95. }
  96.  
  97. EXTERN_PROCESS_LOCAL(_AFX_EDIT_STATE, _afxEditState)
  98.  
  99. /////////////////////////////////////////////////////////////////////////////
  100. // CEditView construction/destruction
  101.  
  102. // pass a NULL style because dwStyleDefault stays for backward compatibility
  103. CEditView::CEditView() : CCtrlView(_T("EDIT"), NULL)
  104. {
  105.     m_nTabStops = 8*4;  // default 8 character positions
  106.     m_hPrinterFont = NULL;
  107.     m_hMirrorFont = NULL;
  108.     m_pShadowBuffer = NULL;
  109.     m_nShadowSize = 0;
  110. }
  111.  
  112. CEditView::~CEditView()
  113. {
  114.     ASSERT(m_hWnd == NULL);
  115.     ASSERT(m_pShadowBuffer == NULL || afxData.bWin95);
  116.     delete[] m_pShadowBuffer;
  117. }
  118.  
  119. BOOL CEditView::PreCreateWindow(CREATESTRUCT& cs)
  120. {
  121.     m_dwDefaultStyle = dwStyleDefault;
  122.     return CCtrlView::PreCreateWindow(cs);
  123. }
  124.  
  125. int CEditView::OnCreate(LPCREATESTRUCT lpcs)
  126. {
  127.     if (CCtrlView::OnCreate(lpcs) != 0)
  128.         return -1;
  129.  
  130. #ifdef _UNICODE
  131.     AfxLockGlobals(CRIT_EDITVIEW);
  132.     if (_afxUnicodeFont == NULL)
  133.     {
  134.         // get unicode font same size as system font
  135.         HFONT hSystemFont = (HFONT)GetStockObject(SYSTEM_FONT);
  136.         LOGFONT systemFont;
  137.         VERIFY(::GetObject(hSystemFont, sizeof(LOGFONT), (void*)&systemFont));
  138.  
  139.         // default size and facename, but allow customization
  140.         LOGFONT logFont; memset(&logFont, 0, sizeof(LOGFONT));
  141.         logFont.lfHeight = systemFont.lfHeight;
  142.         logFont.lfWeight = systemFont.lfWeight;
  143.         logFont.lfCharSet = DEFAULT_CHARSET;
  144.         lstrcpy(logFont.lfFaceName, _T("Lucida Sans Unicode"));
  145.         AfxCustomLogFont(AFX_IDS_UNICODE_FONT, &logFont);
  146.  
  147.         // attempt to create the font
  148.         _afxUnicodeFont = ::CreateFontIndirect(&logFont);
  149.         if (_afxUnicodeFont == NULL)
  150.             TRACE1("Unable to create unicode font '%s'.\n", logFont.lfFaceName);
  151.     }
  152.     AfxUnlockGlobals(CRIT_EDITVIEW);
  153.     // set unicode font instead of using system font
  154.     if (_afxUnicodeFont != NULL)
  155.         SendMessage(WM_SETFONT, (WPARAM)_afxUnicodeFont);
  156. #endif
  157.  
  158.     GetEditCtrl().LimitText(nMaxSize);
  159.     GetEditCtrl().SetTabStops(m_nTabStops);
  160.  
  161.     return 0;
  162. }
  163.  
  164. void CEditView::OnDestroy()
  165. {
  166.     _AFX_EDIT_STATE* pEditState = _afxEditState;
  167.     pEditState->pFindReplaceDlg = NULL;
  168.  
  169.     CView::OnDestroy();
  170. }
  171.  
  172. // EDIT controls always turn off WS_BORDER and draw it themselves
  173. void CEditView::CalcWindowRect(LPRECT lpClientRect, UINT nAdjustType)
  174. {
  175.     if (nAdjustType != 0)
  176.     {
  177.         // default behavior for in-place editing handles scrollbars
  178.         DWORD dwStyle = GetStyle();
  179.         if (dwStyle & WS_VSCROLL)
  180.             lpClientRect->right += afxData.cxVScroll - CX_BORDER;
  181.         if (dwStyle & WS_HSCROLL)
  182.             lpClientRect->bottom += afxData.cyHScroll - CY_BORDER;
  183.         return;
  184.     }
  185.  
  186.     ::AdjustWindowRectEx(lpClientRect, GetStyle() | WS_BORDER, FALSE,
  187.         GetExStyle() & ~(WS_EX_CLIENTEDGE));
  188. }
  189.  
  190. /////////////////////////////////////////////////////////////////////////////
  191. // CEditView document like functions
  192.  
  193. void CEditView::DeleteContents()
  194. {
  195.     ASSERT_VALID(this);
  196.     ASSERT(m_hWnd != NULL);
  197.     SetWindowText(NULL);
  198.     ASSERT_VALID(this);
  199. }
  200.  
  201. void CEditView::Serialize(CArchive& ar)
  202.     // Read and write CEditView object to archive, with length prefix.
  203. {
  204.     ASSERT_VALID(this);
  205.     ASSERT(m_hWnd != NULL);
  206.     if (ar.IsStoring())
  207.     {
  208.         UINT nLen = GetBufferLength();
  209.         ar << (DWORD)nLen;
  210.         WriteToArchive(ar);
  211.     }
  212.     else
  213.     {
  214.         DWORD dwLen;
  215.         ar >> dwLen;
  216.         if (dwLen > nMaxSize)
  217.             AfxThrowArchiveException(CArchiveException::badIndex);
  218.         UINT nLen = (UINT)dwLen;
  219.         ReadFromArchive(ar, nLen);
  220.     }
  221.     ASSERT_VALID(this);
  222. }
  223.  
  224. void CEditView::ReadFromArchive(CArchive& ar, UINT nLen)
  225.     // Read certain amount of text from the file, assume at least nLen
  226.     // characters (not bytes) are in the file.
  227. {
  228.     ASSERT_VALID(this);
  229.  
  230.     LPVOID hText = LocalAlloc(LMEM_MOVEABLE, (nLen+1)*sizeof(TCHAR));
  231.     if (hText == NULL)
  232.         AfxThrowMemoryException();
  233.  
  234.     LPTSTR lpszText = (LPTSTR)LocalLock(hText);
  235.     ASSERT(lpszText != NULL);
  236.     if (ar.Read(lpszText, nLen*sizeof(TCHAR)) != nLen*sizeof(TCHAR))
  237.     {
  238.         LocalUnlock(hText);
  239.         LocalFree(hText);
  240.         AfxThrowArchiveException(CArchiveException::endOfFile);
  241.     }
  242.     // Replace the editing edit buffer with the newly loaded data
  243.     lpszText[nLen] = '\0';
  244. #ifndef _UNICODE
  245.     if (afxData.bWin95)
  246.     {
  247.         // set the text with SetWindowText, then free
  248.         BOOL bResult = ::SetWindowText(m_hWnd, lpszText);
  249.         LocalUnlock(hText);
  250.         LocalFree(hText);
  251.  
  252.         // make sure that SetWindowText was successful
  253.         if (!bResult || ::GetWindowTextLength(m_hWnd) < (int)nLen)
  254.             AfxThrowMemoryException();
  255.  
  256.         // remove old shadow buffer
  257.         delete[] m_pShadowBuffer;
  258.         m_pShadowBuffer = NULL;
  259.         m_nShadowSize = 0;
  260.  
  261.         ASSERT_VALID(this);
  262.         return;
  263.     }
  264. #endif
  265.     LocalUnlock(hText);
  266.     HLOCAL hOldText = GetEditCtrl().GetHandle();
  267.     ASSERT(hOldText != NULL);
  268.     LocalFree(hOldText);
  269.     GetEditCtrl().SetHandle((HLOCAL)(UINT)(DWORD)hText);
  270.     Invalidate();
  271.     ASSERT_VALID(this);
  272. }
  273.  
  274. void CEditView::WriteToArchive(CArchive& ar)
  275.     // Write just the text to an archive, no length prefix.
  276. {
  277.     ASSERT_VALID(this);
  278.     LPCTSTR lpszText = LockBuffer();
  279.     ASSERT(lpszText != NULL);
  280.     UINT nLen = GetBufferLength();
  281.     TRY
  282.     {
  283.         ar.Write(lpszText, nLen*sizeof(TCHAR));
  284.     }
  285.     CATCH_ALL(e)
  286.     {
  287.         UnlockBuffer();
  288.         THROW_LAST();
  289.     }
  290.     END_CATCH_ALL
  291.     UnlockBuffer();
  292.     ASSERT_VALID(this);
  293. }
  294.  
  295. void CEditView::SerializeRaw(CArchive& ar)
  296.     // Read/Write object as stand-alone file.
  297. {
  298.     ASSERT_VALID(this);
  299.     if (ar.IsStoring())
  300.     {
  301.         WriteToArchive(ar);
  302.     }
  303.     else
  304.     {
  305.         CFile* pFile = ar.GetFile();
  306.         ASSERT(pFile->GetPosition() == 0);
  307.         DWORD nFileSize = pFile->GetLength();
  308.         if (nFileSize/sizeof(TCHAR) > nMaxSize)
  309.         {
  310.             AfxMessageBox(AFX_IDP_FILE_TOO_LARGE);
  311.             AfxThrowUserException();
  312.         }
  313.         // ReadFromArchive takes the number of characters as argument
  314.         ReadFromArchive(ar, (UINT)nFileSize/sizeof(TCHAR));
  315.     }
  316.     ASSERT_VALID(this);
  317. }
  318.  
  319. /////////////////////////////////////////////////////////////////////////////
  320. // CEditView Printing Helpers
  321.  
  322. AFX_STATIC UINT AFXAPI _AfxEndOfLine(LPCTSTR lpszText, UINT nLen, UINT nIndex)
  323. {
  324.     ASSERT(AfxIsValidAddress(lpszText, nLen, FALSE));
  325.     LPCTSTR lpsz = lpszText + nIndex;
  326.     LPCTSTR lpszStop = lpszText + nLen;
  327.     while (lpsz < lpszStop && *lpsz != '\r')
  328.         ++lpsz;
  329.     return lpsz - lpszText;
  330. }
  331.  
  332. AFX_STATIC UINT AFXAPI _AfxNextLine(LPCTSTR lpszText, UINT nLen, UINT nIndex)
  333. {
  334.     ASSERT(AfxIsValidAddress(lpszText, nLen, FALSE));
  335.     LPCTSTR lpsz = lpszText + nIndex;
  336.     LPCTSTR lpszStop = lpszText + nLen;
  337.     while (lpsz < lpszStop && *lpsz == '\r')
  338.         ++lpsz;
  339.     if (lpsz < lpszStop && *lpsz == '\n')
  340.         ++lpsz;
  341.     return lpsz - lpszText;
  342. }
  343.  
  344. AFX_STATIC UINT AFXAPI
  345. _AfxClipLine(CDC* pDC, int aCharWidths[256], int cxLine, int nTabStop,
  346.     LPCTSTR lpszText, UINT nIndex, UINT nIndexEnd)
  347. {
  348.     ASSERT_VALID(pDC);
  349.     ASSERT(nIndex < nIndexEnd);
  350.     ASSERT(AfxIsValidAddress(lpszText, nIndexEnd, FALSE));
  351.  
  352.     TEXTMETRIC tm;
  353.     ::GetTextMetrics(pDC->m_hDC, &tm);
  354.  
  355.     // make an initial guess on the number of characters that will fit
  356.     int cx = 0;
  357.     LPCTSTR lpszStart = lpszText + nIndex;
  358.     LPCTSTR lpszStop = lpszText + nIndexEnd;
  359.     LPCTSTR lpsz = lpszStart;
  360.     while (lpsz < lpszStop)
  361.     {
  362.         if (*lpsz == '\t')
  363.             cx += nTabStop - (cx % nTabStop);
  364.         else
  365.         {
  366. #ifdef _UNICODE
  367.             if (*lpsz <= 0xFF)
  368.                 cx += aCharWidths[(BYTE)*lpsz];
  369.             else
  370.                 cx += tm.tmAveCharWidth;
  371. #else //_UNICODE
  372.             if (_afxDBCS && _istlead(*lpsz))
  373.             {
  374.                 ++lpsz;
  375.                 cx += tm.tmAveCharWidth;
  376.             }
  377.             else
  378.                 cx += aCharWidths[(BYTE)*lpsz];
  379. #endif //!_UNICODE
  380.         }
  381.         ++lpsz;
  382.         if (cx > cxLine)
  383.             break;
  384.     }
  385.  
  386.     // adjust for errors in the guess
  387.     cx = pDC->GetTabbedTextExtent(lpszStart, lpsz-lpszStart, 1, &nTabStop).cx;
  388.     if (cx > cxLine)
  389.     {
  390.         // remove characters until it fits
  391.         do
  392.         {
  393.             ASSERT(lpsz != lpszStart);
  394.             if (_afxDBCS)
  395.                 lpsz = _tcsdec(lpszStart, lpsz);
  396.             else
  397.                 --lpsz;
  398.             cx = pDC->GetTabbedTextExtent(lpszStart, lpsz-lpszStart, 1, &nTabStop).cx;
  399.         } while (cx > cxLine);
  400.     }
  401.     else if (cx < cxLine)
  402.     {
  403.         // add characters until it doesn't fit
  404.         while (lpsz < lpszStop)
  405.         {
  406.             lpsz = _tcsinc(lpsz);
  407.             ASSERT(lpsz <= lpszStop);
  408.             cx = pDC->GetTabbedTextExtent(lpszStart, lpsz-lpszStart, 1, &nTabStop).cx;
  409.             if (cx > cxLine)
  410.             {
  411.                 if (_afxDBCS)
  412.                     lpsz = _tcsdec(lpszStart, lpsz);
  413.                 else
  414.                     --lpsz;
  415.                 break;
  416.             }
  417.         }
  418.     }
  419.  
  420.     // return index of character just past the last that would fit
  421.     return lpsz - lpszText;
  422. }
  423.  
  424. /////////////////////////////////////////////////////////////////////////////
  425. // CEditView Printing support
  426.  
  427. BOOL CEditView::OnPreparePrinting(CPrintInfo* pInfo)
  428. {
  429.     return DoPreparePrinting(pInfo);
  430. }
  431.  
  432. void CEditView::OnBeginPrinting(CDC* pDC, CPrintInfo*)
  433. {
  434.     ASSERT_VALID(this);
  435.     ASSERT_VALID(pDC);
  436.     // initialize page start vector
  437.     ASSERT(m_aPageStart.GetSize() == 0);
  438.     m_aPageStart.Add(0);
  439.     ASSERT(m_aPageStart.GetSize() > 0);
  440.  
  441.     if (m_hPrinterFont == NULL)
  442.     {
  443.         // get current screen font object metrics
  444.         CFont* pFont = GetFont();
  445.         LOGFONT lf;
  446.         LOGFONT lfSys;
  447.         if (pFont == NULL)
  448.             return;
  449.         VERIFY(pFont->GetObject(sizeof(LOGFONT), &lf));
  450.         VERIFY(::GetObject(::GetStockObject(SYSTEM_FONT), sizeof(LOGFONT),
  451.             &lfSys));
  452.         if (lstrcmpi((LPCTSTR)lf.lfFaceName, (LPCTSTR)lfSys.lfFaceName) == 0)
  453.             return;
  454.  
  455.         // map to printer font metrics
  456.         HDC hDCFrom = ::GetDC(NULL);
  457.         lf.lfHeight = ::MulDiv(lf.lfHeight, pDC->GetDeviceCaps(LOGPIXELSY),
  458.             ::GetDeviceCaps(hDCFrom, LOGPIXELSY));
  459.         lf.lfWidth = ::MulDiv(lf.lfWidth, pDC->GetDeviceCaps(LOGPIXELSX),
  460.             ::GetDeviceCaps(hDCFrom, LOGPIXELSX));
  461.         ::ReleaseDC(NULL, hDCFrom);
  462.  
  463.         // create it, if it fails we just use the printer's default.
  464.         m_hMirrorFont = ::CreateFontIndirect(&lf);
  465.         m_hPrinterFont = m_hMirrorFont;
  466.     }
  467.     ASSERT_VALID(this);
  468. }
  469.  
  470. BOOL CEditView::PaginateTo(CDC* pDC, CPrintInfo* pInfo)
  471.     // attempts pagination to pInfo->m_nCurPage, TRUE == success
  472. {
  473.     ASSERT_VALID(this);
  474.     ASSERT_VALID(pDC);
  475.  
  476.     CRect rectSave = pInfo->m_rectDraw;
  477.     UINT nPageSave = pInfo->m_nCurPage;
  478.     ASSERT(nPageSave > 1);
  479.     ASSERT(nPageSave >= (UINT)m_aPageStart.GetSize());
  480.     VERIFY(pDC->SaveDC() != 0);
  481.     pDC->IntersectClipRect(0, 0, 0, 0);
  482.     pInfo->m_nCurPage = m_aPageStart.GetSize();
  483.     while (pInfo->m_nCurPage < nPageSave)
  484.     {
  485.         ASSERT(pInfo->m_nCurPage == (UINT)m_aPageStart.GetSize());
  486.         OnPrepareDC(pDC, pInfo);
  487.         ASSERT(pInfo->m_bContinuePrinting);
  488.         pInfo->m_rectDraw.SetRect(0, 0,
  489.             pDC->GetDeviceCaps(HORZRES), pDC->GetDeviceCaps(VERTRES));
  490.         pDC->DPtoLP(&pInfo->m_rectDraw);
  491.         OnPrint(pDC, pInfo);
  492.         if (pInfo->m_nCurPage == (UINT)m_aPageStart.GetSize())
  493.             break;
  494.         ++pInfo->m_nCurPage;
  495.     }
  496.     BOOL bResult = pInfo->m_nCurPage == nPageSave;
  497.     pDC->RestoreDC(-1);
  498.     pInfo->m_nCurPage = nPageSave;
  499.     pInfo->m_rectDraw = rectSave;
  500.     ASSERT_VALID(this);
  501.     return bResult;
  502. }
  503.  
  504. void CEditView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
  505. {
  506.     ASSERT_VALID(this);
  507.     ASSERT_VALID(pDC);
  508.     ASSERT(pInfo != NULL);  // overriding OnPaint -- never get this.
  509.  
  510.     if (pInfo->m_nCurPage > (UINT)m_aPageStart.GetSize() &&
  511.         !PaginateTo(pDC, pInfo))
  512.     {
  513.         // can't paginate to that page, thus cannot print it.
  514.         pInfo->m_bContinuePrinting = FALSE;
  515.     }
  516.     ASSERT_VALID(this);
  517. }
  518.  
  519. UINT CEditView::PrintInsideRect(CDC* pDC, RECT& rectLayout,
  520.     UINT nIndexStart, UINT nIndexStop)
  521.     // worker function for laying out text in a rectangle.
  522. {
  523.     ASSERT_VALID(this);
  524.     ASSERT_VALID(pDC);
  525.     BOOL bWordWrap = (GetStyle() & ES_AUTOHSCROLL) == 0;
  526.  
  527.     // get buffer and real starting and ending postions
  528.     UINT nLen = GetBufferLength();
  529.     if (nIndexStart >= nLen)
  530.         return nLen;
  531.     LPCTSTR lpszText = LockBuffer();
  532.     if (nIndexStop > nLen)
  533.         nIndexStop = nLen;
  534.     ASSERT(nIndexStart < nLen);
  535.  
  536.     // calculate text & tab metrics
  537.     TEXTMETRIC tm;
  538.     pDC->GetTextMetrics(&tm);
  539.     int cyChar = tm.tmHeight + tm.tmExternalLeading;
  540.     int nTabStop = m_nTabStops *
  541.         pDC->GetTabbedTextExtent(_T("\t"), 1, 0, NULL).cx / 8 / 4;
  542.     int aCharWidths[256];
  543.     pDC->GetCharWidth(0, 255, aCharWidths);
  544.  
  545.     int y = rectLayout.top;
  546.     UINT cx = rectLayout.right - rectLayout.left;
  547.     UINT nIndex = nIndexStart;
  548.  
  549.     VERIFY(pDC->SaveDC() != 0);
  550.     BOOL bLayoutOnly = pDC->IntersectClipRect(&rectLayout) == NULLREGION;
  551.  
  552.     do
  553.     {
  554.         UINT nIndexEnd = _AfxEndOfLine(lpszText, nIndexStop, nIndex);
  555.         if (nIndex == nIndexEnd)
  556.         {
  557.             y += cyChar;
  558.         }
  559.         else if (bWordWrap)
  560.         {
  561.             // word-wrap printing
  562.             do
  563.             {
  564.                 UINT nIndexWrap = _AfxClipLine(pDC, aCharWidths,
  565.                     cx, nTabStop, lpszText, nIndex, nIndexEnd);
  566.                 UINT nIndexWord = nIndexWrap;
  567.                 if (nIndexWord != nIndexEnd)
  568.                 {
  569.                     while (nIndexWord > nIndex &&
  570.                       !_istspace(lpszText[nIndexWord]))
  571.                     {
  572.                         nIndexWord--;
  573.                     }
  574.                     if (nIndexWord == nIndex)
  575.                         nIndexWord = nIndexWrap;
  576.                 }
  577.                 CRect rect(rectLayout.left, y, rectLayout.right, y+cyChar);
  578.                 if (!bLayoutOnly && pDC->RectVisible(rect))
  579.                 {
  580.                     pDC->TabbedTextOut(rect.left, y,
  581.                         (LPCTSTR)(lpszText+nIndex), nIndexWord-nIndex, 1,
  582.                         &nTabStop, rect.left);
  583.                 }
  584.                 y += cyChar;
  585.                 nIndex = nIndexWord;
  586.                 while (nIndex < nIndexEnd && _istspace(lpszText[nIndex]))
  587.                     nIndex++;
  588.             } while (nIndex < nIndexEnd && y+cyChar <= rectLayout.bottom);
  589.  
  590.             nIndexEnd = nIndex;
  591.         }
  592.         else
  593.         {
  594.             // non-word wrap printing (much easier and faster)
  595.             CRect rect(rectLayout.left, y, rectLayout.right, y+cyChar);
  596.             if (!bLayoutOnly && pDC->RectVisible(rect))
  597.             {
  598.                 UINT nIndexClip = _AfxClipLine(pDC, aCharWidths, cx, nTabStop,
  599.                     lpszText, nIndex, nIndexEnd);
  600.                 if (nIndexClip < nIndexEnd)
  601.                 {
  602.                     if (_istlead(*(lpszText+nIndexClip)))
  603.                         nIndexClip++;
  604.                     nIndexClip++;
  605.                 }
  606.                 pDC->TabbedTextOut(rect.left, y,
  607.                     (LPCTSTR)(lpszText+nIndex), nIndexClip-nIndex, 1,
  608.                     &nTabStop, rect.left);
  609.             }
  610.             y += cyChar;
  611.         }
  612.         nIndex = _AfxNextLine(lpszText, nIndexStop, nIndexEnd);
  613.     }
  614.     while (nIndex < nIndexStop && y+cyChar <= rectLayout.bottom);
  615.  
  616.     pDC->RestoreDC(-1);
  617.     UnlockBuffer();
  618.     ASSERT_VALID(this);
  619.  
  620.     rectLayout.bottom = y;
  621.     return nIndex;
  622. }
  623.  
  624. void CEditView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
  625. {
  626.     ASSERT_VALID(this);
  627.     ASSERT_VALID(pDC);
  628.     ASSERT(pInfo != NULL);
  629.     ASSERT(pInfo->m_bContinuePrinting);
  630.  
  631.     CFont* pOldFont = NULL;
  632.     if (m_hPrinterFont != NULL)
  633.         pOldFont = pDC->SelectObject(CFont::FromHandle(m_hPrinterFont));
  634.     pDC->SetBkMode(TRANSPARENT);
  635.  
  636.     UINT nPage = pInfo->m_nCurPage;
  637.     ASSERT(nPage <= (UINT)m_aPageStart.GetSize());
  638.     UINT nIndex = m_aPageStart[nPage-1];
  639.  
  640.     // print as much as possible in the current page.
  641.     nIndex = PrintInsideRect(pDC, pInfo->m_rectDraw, nIndex, GetBufferLength());
  642.  
  643.     if (pOldFont != NULL)
  644.         pDC->SelectObject(pOldFont);
  645.  
  646.     // update pagination information for page just printed
  647.     if (nPage == (UINT)m_aPageStart.GetSize())
  648.     {
  649.         if (nIndex < GetBufferLength())
  650.             m_aPageStart.Add(nIndex);
  651.     }
  652.     else
  653.     {
  654.         ASSERT(nPage+1 <= (UINT)m_aPageStart.GetSize());
  655.         ASSERT(nIndex == m_aPageStart[nPage+1-1]);
  656.     }
  657. }
  658.  
  659. void CEditView::OnEndPrinting(CDC*, CPrintInfo*)
  660. {
  661.     ASSERT_VALID(this);
  662.  
  663.     m_aPageStart.RemoveAll();
  664.     if (m_hMirrorFont != NULL && m_hPrinterFont == m_hMirrorFont)
  665.     {
  666.         AfxDeleteObject((HGDIOBJ*)&m_hMirrorFont);
  667.         m_hPrinterFont = NULL;
  668.     }
  669. }
  670.  
  671. /////////////////////////////////////////////////////////////////////////////
  672. // CEditView commands
  673.  
  674. void CEditView::OnUpdateNeedSel(CCmdUI* pCmdUI)
  675. {
  676.     ASSERT_VALID(this);
  677.     int nStartChar, nEndChar;
  678.     GetEditCtrl().GetSel(nStartChar, nEndChar);
  679.     pCmdUI->Enable(nStartChar != nEndChar);
  680.     ASSERT_VALID(this);
  681. }
  682.  
  683. void CEditView::OnUpdateNeedClip(CCmdUI* pCmdUI)
  684. {
  685.     ASSERT_VALID(this);
  686.     pCmdUI->Enable(::IsClipboardFormatAvailable(CF_TEXT));
  687.     ASSERT_VALID(this);
  688. }
  689.  
  690. void CEditView::OnUpdateNeedText(CCmdUI* pCmdUI)
  691. {
  692.     ASSERT_VALID(this);
  693.     pCmdUI->Enable(GetWindowTextLength() != 0);
  694.     ASSERT_VALID(this);
  695. }
  696.  
  697. void CEditView::OnUpdateNeedFind(CCmdUI* pCmdUI)
  698. {
  699.     ASSERT_VALID(this);
  700.     _AFX_EDIT_STATE* pEditState = _afxEditState;
  701.     pCmdUI->Enable(GetWindowTextLength() != 0 &&
  702.         !pEditState->strFind.IsEmpty());
  703.     ASSERT_VALID(this);
  704. }
  705.  
  706. void CEditView::OnUpdateEditUndo(CCmdUI* pCmdUI)
  707. {
  708.     ASSERT_VALID(this);
  709.     pCmdUI->Enable(GetEditCtrl().CanUndo());
  710.     ASSERT_VALID(this);
  711. }
  712.  
  713. BOOL CEditView::OnEditChange()
  714. {
  715.     ASSERT_VALID(this);
  716.     GetDocument()->SetModifiedFlag();
  717.     ASSERT_VALID(this);
  718.  
  719.     return FALSE;   // continue routing
  720. }
  721.  
  722. void CEditView::OnEditCut()
  723. {
  724.     ASSERT_VALID(this);
  725.     GetEditCtrl().Cut();
  726.     ASSERT_VALID(this);
  727. }
  728.  
  729. void CEditView::OnEditCopy()
  730. {
  731.     ASSERT_VALID(this);
  732.     GetEditCtrl().Copy();
  733.     ASSERT_VALID(this);
  734. }
  735.  
  736. void CEditView::OnEditPaste()
  737. {
  738.     ASSERT_VALID(this);
  739.     GetEditCtrl().Paste();
  740.     ASSERT_VALID(this);
  741. }
  742.  
  743. void CEditView::OnEditClear()
  744. {
  745.     ASSERT_VALID(this);
  746.     GetEditCtrl().Clear();
  747.     ASSERT_VALID(this);
  748. }
  749.  
  750. void CEditView::OnEditUndo()
  751. {
  752.     ASSERT_VALID(this);
  753.     GetEditCtrl().Undo();
  754.     ASSERT_VALID(this);
  755. }
  756.  
  757. void CEditView::OnEditSelectAll()
  758. {
  759.     ASSERT_VALID(this);
  760.     GetEditCtrl().SetSel(0, -1);
  761.     ASSERT_VALID(this);
  762. }
  763.  
  764. /////////////////////////////////////////////////////////////////////////////
  765. // CEditView Font Handling
  766.  
  767. LRESULT CEditView::OnSetFont(WPARAM, LPARAM)
  768. {
  769.     ASSERT_VALID(this);
  770.     Default();
  771.     GetEditCtrl().SetTabStops(m_nTabStops);
  772.     ASSERT_VALID(this);
  773.     return 0;
  774. }
  775.  
  776. void CEditView::SetPrinterFont(CFont* pFont)
  777. {
  778.     ASSERT_VALID(this);
  779.     m_hPrinterFont = (HFONT)pFont->GetSafeHandle();
  780.     ASSERT_VALID(this);
  781. }
  782.  
  783. CFont* CEditView::GetPrinterFont() const
  784. {
  785.     ASSERT_VALID(this);
  786.     return CFont::FromHandle(m_hPrinterFont);
  787. }
  788.  
  789. /////////////////////////////////////////////////////////////////////////////
  790. // CEditView attributes
  791.  
  792. LPCTSTR CEditView::LockBuffer() const
  793. {
  794.     ASSERT_VALID(this);
  795.     ASSERT(m_hWnd != NULL);
  796. #ifndef _UNICODE
  797.     if (afxData.bWin95)
  798.     {
  799.         // under Win32s, it is necessary to maintain a shadow buffer
  800.         //  it is only updated when the control contents have been changed.
  801.         if (m_pShadowBuffer == NULL || GetEditCtrl().GetModify())
  802.         {
  803.             ASSERT(m_pShadowBuffer != NULL || m_nShadowSize == 0);
  804.             UINT nSize = GetWindowTextLength()+1;
  805.             if (nSize > m_nShadowSize)
  806.             {
  807.                 // need more room for shadow buffer
  808.                 CEditView* pThis = (CEditView*)this;
  809.                 delete[] m_pShadowBuffer;
  810.                 pThis->m_pShadowBuffer = NULL;
  811.                 pThis->m_nShadowSize = 0;
  812.                 pThis->m_pShadowBuffer = new TCHAR[nSize];
  813.                 pThis->m_nShadowSize = nSize;
  814.             }
  815.  
  816.             // update the shadow buffer with GetWindowText
  817.             ASSERT(m_nShadowSize >= nSize);
  818.             ASSERT(m_pShadowBuffer != NULL);
  819.             GetWindowText(m_pShadowBuffer, nSize);
  820.  
  821.             // turn off edit control's modify bit
  822.             GetEditCtrl().SetModify(FALSE);
  823.         }
  824.         return m_pShadowBuffer;
  825.     }
  826. #endif
  827.     // else -- running under non-subset Win32 system
  828.     HLOCAL hLocal = GetEditCtrl().GetHandle();
  829.     ASSERT(hLocal != NULL);
  830.     LPCTSTR lpszText = (LPCTSTR)LocalLock(hLocal);
  831.     ASSERT(lpszText != NULL);
  832.     ASSERT_VALID(this);
  833.     return lpszText;
  834. }
  835.  
  836. void CEditView::UnlockBuffer() const
  837. {
  838.     ASSERT_VALID(this);
  839.     ASSERT(m_hWnd != NULL);
  840. #ifndef _UNICODE
  841.     if (afxData.bWin95)
  842.         return;
  843. #endif
  844.     HLOCAL hLocal = GetEditCtrl().GetHandle();
  845.     ASSERT(hLocal != NULL);
  846.     LocalUnlock(hLocal);
  847. }
  848.  
  849. // this function returns the length in characters
  850. UINT CEditView::GetBufferLength() const
  851. {
  852.     ASSERT_VALID(this);
  853.     ASSERT(m_hWnd != NULL);
  854.     LPCTSTR lpszText = LockBuffer();
  855.     UINT nLen = lstrlen(lpszText);
  856.     UnlockBuffer();
  857.     return nLen;
  858. }
  859.  
  860. void CEditView::GetSelectedText(CString& strResult) const
  861. {
  862.     ASSERT_VALID(this);
  863.     int nStartChar, nEndChar;
  864.     GetEditCtrl().GetSel(nStartChar, nEndChar);
  865.     ASSERT((UINT)nEndChar <= GetBufferLength());
  866.     LPCTSTR lpszText = ((CEditView*)this)->LockBuffer();
  867.     UINT nLen = _AfxEndOfLine(lpszText, nEndChar, nStartChar) - nStartChar;
  868.     memcpy(strResult.GetBuffer(nLen), lpszText + nStartChar,
  869.         nLen * sizeof(TCHAR));
  870.     strResult.ReleaseBuffer(nLen);
  871.     UnlockBuffer();
  872.     ASSERT_VALID(this);
  873. }
  874.  
  875. /////////////////////////////////////////////////////////////////////////////
  876. // CEditView Find & Replace
  877.  
  878. void CEditView::OnEditFind()
  879. {
  880.     ASSERT_VALID(this);
  881.     OnEditFindReplace(TRUE);
  882.     ASSERT_VALID(this);
  883. }
  884.  
  885. void CEditView::OnEditReplace()
  886. {
  887.     ASSERT_VALID(this);
  888.     OnEditFindReplace(FALSE);
  889.     ASSERT_VALID(this);
  890. }
  891.  
  892. void CEditView::OnEditRepeat()
  893. {
  894.     ASSERT_VALID(this);
  895.     _AFX_EDIT_STATE* pEditState = _afxEditState;
  896.     if (!FindText(pEditState->strFind,
  897.         pEditState->bNext,
  898.         pEditState->bCase))
  899.     {
  900.         OnTextNotFound(pEditState->strFind);
  901.     }
  902.     ASSERT_VALID(this);
  903. }
  904.  
  905. void CEditView::OnEditFindReplace(BOOL bFindOnly)
  906. {
  907.     ASSERT_VALID(this);
  908.     _AFX_EDIT_STATE* pEditState = _afxEditState;
  909.     if (pEditState->pFindReplaceDlg != NULL)
  910.     {
  911.         if (pEditState->bFindOnly == bFindOnly)
  912.         {
  913.             pEditState->pFindReplaceDlg->SetActiveWindow();
  914.             pEditState->pFindReplaceDlg->ShowWindow(SW_SHOW);
  915.             return;
  916.         }
  917.         ASSERT(pEditState->bFindOnly != bFindOnly);
  918.         pEditState->pFindReplaceDlg->SendMessage(WM_CLOSE);
  919.         ASSERT(pEditState->pFindReplaceDlg == NULL);
  920.         ASSERT_VALID(this);
  921.     }
  922.  
  923.     CString strFind;
  924.     GetSelectedText(strFind);
  925.     if (strFind.IsEmpty())
  926.         strFind = pEditState->strFind;
  927.     CString strReplace = pEditState->strReplace;
  928.     pEditState->pFindReplaceDlg = new CFindReplaceDialog;
  929.     ASSERT(pEditState->pFindReplaceDlg != NULL);
  930.     DWORD dwFlags = FR_HIDEWHOLEWORD;
  931.     if (pEditState->bNext)
  932.         dwFlags |= FR_DOWN;
  933.     if (pEditState->bCase)
  934.         dwFlags |= FR_MATCHCASE;
  935.     if (!pEditState->pFindReplaceDlg->Create(bFindOnly, strFind,
  936.         strReplace, dwFlags, this))
  937.     {
  938.         pEditState->pFindReplaceDlg = NULL;
  939.         ASSERT_VALID(this);
  940.         return;
  941.     }
  942.  
  943.     pEditState->pFindReplaceDlg->SetActiveWindow();
  944.     pEditState->pFindReplaceDlg->ShowWindow(SW_SHOW);
  945.     ASSERT(pEditState->pFindReplaceDlg != NULL);
  946.     pEditState->bFindOnly = bFindOnly;
  947.     ASSERT_VALID(this);
  948. }
  949.  
  950. void CEditView::OnFindNext(LPCTSTR lpszFind, BOOL bNext, BOOL bCase)
  951. {
  952.     ASSERT_VALID(this);
  953.     _AFX_EDIT_STATE* pEditState = _afxEditState;
  954.     pEditState->strFind = lpszFind;
  955.     pEditState->bCase = bCase;
  956.     pEditState->bNext = bNext;
  957.  
  958.     if (!FindText(pEditState->strFind, bNext, bCase))
  959.         OnTextNotFound(pEditState->strFind);
  960.     ASSERT_VALID(this);
  961. }
  962.  
  963. void CEditView::OnReplaceSel(LPCTSTR lpszFind, BOOL bNext, BOOL bCase,
  964.     LPCTSTR lpszReplace)
  965. {
  966.     ASSERT_VALID(this);
  967.     _AFX_EDIT_STATE* pEditState = _afxEditState;
  968.     pEditState->strFind = lpszFind;
  969.     pEditState->strReplace = lpszReplace;
  970.     pEditState->bCase = bCase;
  971.     pEditState->bNext = bNext;
  972.  
  973.     if (!InitializeReplace())
  974.         return;
  975.  
  976.     GetEditCtrl().ReplaceSel(pEditState->strReplace);
  977.     FindText(pEditState->strFind, bNext, bCase);
  978.     ASSERT_VALID(this);
  979. }
  980.  
  981. void CEditView::OnReplaceAll(LPCTSTR lpszFind, LPCTSTR lpszReplace, BOOL bCase)
  982. {
  983.     ASSERT_VALID(this);
  984.     _AFX_EDIT_STATE* pEditState = _afxEditState;
  985.     pEditState->strFind = lpszFind;
  986.     pEditState->strReplace = lpszReplace;
  987.     pEditState->bCase = bCase;
  988.     pEditState->bNext = TRUE;
  989.  
  990.     if (!InitializeReplace() &&
  991.         !SameAsSelected(pEditState->strFind, pEditState->bCase))
  992.     {
  993.         // initial find was not successful
  994.         return;
  995.     }
  996.  
  997.     do
  998.     {
  999.         GetEditCtrl().ReplaceSel(pEditState->strReplace);
  1000.     } while (FindText(pEditState->strFind, 1, bCase));
  1001.  
  1002.     ASSERT_VALID(this);
  1003. }
  1004.  
  1005. BOOL CEditView::InitializeReplace()
  1006.     // helper to do find first if no selection
  1007. {
  1008.     ASSERT_VALID(this);
  1009.  
  1010.     _AFX_EDIT_STATE* pEditState = _afxEditState;
  1011.  
  1012.     // do find next if no selection
  1013.     int nStartChar, nEndChar;
  1014.     GetEditCtrl().GetSel(nStartChar, nEndChar);
  1015.     if (nStartChar == nEndChar)
  1016.     {
  1017.         if (!FindText(pEditState->strFind, pEditState->bNext,
  1018.             pEditState->bCase))
  1019.         {
  1020.             // text not found
  1021.             OnTextNotFound(pEditState->strFind);
  1022.         }
  1023.         return FALSE;
  1024.     }
  1025.  
  1026.     if (!SameAsSelected(pEditState->strFind, pEditState->bCase))
  1027.     {
  1028.         if (!FindText(pEditState->strFind, pEditState->bNext,
  1029.             pEditState->bCase))
  1030.         {
  1031.             // text not found
  1032.             OnTextNotFound(pEditState->strFind);
  1033.         }
  1034.         return FALSE;
  1035.     }
  1036.  
  1037.     ASSERT_VALID(this);
  1038.     return TRUE;
  1039. }
  1040.  
  1041. LRESULT CEditView::OnFindReplaceCmd(WPARAM, LPARAM lParam)
  1042. {
  1043.     ASSERT_VALID(this);
  1044.  
  1045.     _AFX_EDIT_STATE* pEditState = _afxEditState;
  1046.     CFindReplaceDialog* pDialog = CFindReplaceDialog::GetNotifier(lParam);
  1047.     ASSERT(pDialog != NULL);
  1048.     ASSERT(pDialog == pEditState->pFindReplaceDlg);
  1049.     if (pDialog->IsTerminating())
  1050.     {
  1051.         pEditState->pFindReplaceDlg = NULL;
  1052.     }
  1053.     else if (pDialog->FindNext())
  1054.     {
  1055.         OnFindNext(pDialog->GetFindString(),
  1056.             pDialog->SearchDown(), pDialog->MatchCase());
  1057.     }
  1058.     else if (pDialog->ReplaceCurrent())
  1059.     {
  1060.         ASSERT(!pEditState->bFindOnly);
  1061.         OnReplaceSel(pDialog->GetFindString(),
  1062.             pDialog->SearchDown(), pDialog->MatchCase(),
  1063.             pDialog->GetReplaceString());
  1064.     }
  1065.     else if (pDialog->ReplaceAll())
  1066.     {
  1067.         ASSERT(!pEditState->bFindOnly);
  1068.         OnReplaceAll(pDialog->GetFindString(), pDialog->GetReplaceString(),
  1069.             pDialog->MatchCase());
  1070.     }
  1071.     ASSERT_VALID(this);
  1072.     return 0;
  1073. }
  1074.  
  1075. typedef int (WINAPI* AFX_COMPARE_PROC)(LPCTSTR str1, LPCTSTR str2);
  1076.  
  1077. BOOL CEditView::SameAsSelected(LPCTSTR lpszCompare, BOOL bCase)
  1078. {
  1079.     // check length first
  1080.     size_t nLen = lstrlen(lpszCompare);
  1081.     int nStartChar, nEndChar;
  1082.     GetEditCtrl().GetSel(nStartChar, nEndChar);
  1083.     if (nLen != (size_t)(nEndChar - nStartChar))
  1084.         return FALSE;
  1085.  
  1086.     // length is the same, check contents
  1087.     CString strSelect;
  1088.     GetSelectedText(strSelect);
  1089.     return (bCase && lstrcmp(lpszCompare, strSelect) == 0) ||
  1090.         (!bCase && lstrcmpi(lpszCompare, strSelect) == 0);
  1091. }
  1092.  
  1093. BOOL CEditView::FindText(LPCTSTR lpszFind, BOOL bNext, BOOL bCase)
  1094. {
  1095.     ASSERT_VALID(this);
  1096.     ASSERT(lpszFind != NULL);
  1097.     ASSERT(*lpszFind != '\0');
  1098.  
  1099.     UINT nLen = GetBufferLength();
  1100.     int nStartChar, nEndChar;
  1101.     GetEditCtrl().GetSel(nStartChar, nEndChar);
  1102.     UINT nStart = nStartChar;
  1103.     int iDir = bNext ? +1 : -1;
  1104.  
  1105.     // can't find a match before the first character
  1106.     if (nStart == 0 && iDir < 0)
  1107.         return FALSE;
  1108.  
  1109.     CWaitCursor wait;
  1110.     LPCTSTR lpszText = LockBuffer();
  1111.  
  1112.     if (iDir < 0)
  1113.     {
  1114.         // always go back one for search backwards
  1115.         nStart -= (lpszText+nStart) -
  1116.             _tcsdec(lpszText, lpszText+nStart);
  1117.     }
  1118.     else if (nStartChar != nEndChar && SameAsSelected(lpszFind, bCase))
  1119.     {
  1120.         // easy to go backward/forward with SBCS
  1121.         if (_istlead(lpszText[nStart]))
  1122.             nStart++;
  1123.         nStart += iDir;
  1124.     }
  1125.  
  1126.     // handle search with nStart past end of buffer
  1127.     size_t nLenFind = lstrlen(lpszFind);
  1128.     if (nStart+nLenFind-1 >= nLen)
  1129.     {
  1130.         if (iDir < 0 && nLen >= nLenFind)
  1131.         {
  1132.             if (_afxDBCS)
  1133.             {
  1134.                 // walk back to previous character n times
  1135.                 nStart = nLen;
  1136.                 int n = nLenFind;
  1137.                 while (n--)
  1138.                 {
  1139.                     nStart -= (lpszText+nStart) -
  1140.                         _tcsdec(lpszText, lpszText+nStart);
  1141.                 }
  1142.             }
  1143.             else
  1144.             {
  1145.                 // single-byte character set is easy and fast
  1146.                 nStart = nLen - nLenFind;
  1147.             }
  1148.             ASSERT(nStart+nLenFind-1 <= nLen);
  1149.         }
  1150.         else
  1151.         {
  1152.             UnlockBuffer();
  1153.             return FALSE;
  1154.         }
  1155.     }
  1156.  
  1157.     // start the search at nStart
  1158.     LPCTSTR lpsz = lpszText + nStart;
  1159.     AFX_COMPARE_PROC pfnCompare = bCase ? lstrcmp : lstrcmpi;
  1160.  
  1161.     if (_afxDBCS)
  1162.     {
  1163.         // double-byte string search
  1164.         LPCTSTR lpszStop;
  1165.         if (iDir > 0)
  1166.         {
  1167.             // start at current and find _first_ occurrance
  1168.             lpszStop = lpszText + nLen - nLenFind + 1;
  1169.         }
  1170.         else
  1171.         {
  1172.             // start at top and find _last_ occurrance
  1173.             lpszStop = lpsz;
  1174.             lpsz = lpszText;
  1175.         }
  1176.  
  1177.         LPCTSTR lpszFound = NULL;
  1178.         while (lpsz <= lpszStop)
  1179.         {
  1180.             if (!bCase || (*lpsz == *lpszFind &&
  1181.                 (!_istlead(*lpsz) || lpsz[1] == lpszFind[1])))
  1182.             {
  1183.                 LPTSTR lpch = (LPTSTR)(lpsz + nLenFind);
  1184.                 TCHAR chSave = *lpch;
  1185.                 *lpch = '\0';
  1186.                 int nResult = (*pfnCompare)(lpsz, lpszFind);
  1187.                 *lpch = chSave;
  1188.                 if (nResult == 0)
  1189.                 {
  1190.                     lpszFound = lpsz;
  1191.                     if (iDir > 0)
  1192.                         break;
  1193.                 }
  1194.             }
  1195.             lpsz = _tcsinc(lpsz);
  1196.         }
  1197.         UnlockBuffer();
  1198.  
  1199.         if (lpszFound != NULL)
  1200.         {
  1201.             int n = (int)(lpszFound - lpszText);
  1202.             GetEditCtrl().SetSel(n, n+nLenFind);
  1203.             return TRUE;
  1204.         }
  1205.     }
  1206.     else
  1207.     {
  1208.         // single-byte string search
  1209.         UINT nCompare;
  1210.         if (iDir < 0)
  1211.             nCompare = (UINT)(lpsz - lpszText) + 1;
  1212.         else
  1213.             nCompare = nLen - (UINT)(lpsz - lpszText) - nLenFind + 1;
  1214.  
  1215.         while (nCompare > 0)
  1216.         {
  1217.             ASSERT(lpsz >= lpszText);
  1218.             ASSERT(lpsz+nLenFind-1 <= lpszText+nLen-1);
  1219.  
  1220.             LPSTR lpch = (LPSTR)(lpsz + nLenFind);
  1221.             char chSave = *lpch;
  1222.             *lpch = '\0';
  1223.             int nResult = (*pfnCompare)(lpsz, lpszFind);
  1224.             *lpch = chSave;
  1225.             if (nResult == 0)
  1226.             {
  1227.                 UnlockBuffer();
  1228.                 int n = (int)(lpsz - lpszText);
  1229.                 GetEditCtrl().SetSel(n, n+nLenFind);
  1230.                 ASSERT_VALID(this);
  1231.                 return TRUE;
  1232.             }
  1233.  
  1234.             // restore character at end of search
  1235.             *lpch = chSave;
  1236.  
  1237.             // move on to next substring
  1238.             nCompare--;
  1239.             lpsz += iDir;
  1240.         }
  1241.         UnlockBuffer();
  1242.     }
  1243.  
  1244.     ASSERT_VALID(this);
  1245.     return FALSE;
  1246. }
  1247.  
  1248. void CEditView::OnTextNotFound(LPCTSTR)
  1249. {
  1250.     ASSERT_VALID(this);
  1251.     MessageBeep(0);
  1252. }
  1253.  
  1254. /////////////////////////////////////////////////////////////////////////////
  1255. // CEditView Tab Stops
  1256.  
  1257. void CEditView::SetTabStops(int nTabStops)
  1258. {
  1259.     ASSERT_VALID(this);
  1260.     m_nTabStops = nTabStops;
  1261.     GetEditCtrl().SetTabStops(m_nTabStops);
  1262.     Invalidate();
  1263.     ASSERT_VALID(this);
  1264. }
  1265.  
  1266. /////////////////////////////////////////////////////////////////////////////
  1267. // CEditView diagnostics
  1268.  
  1269. #ifdef _DEBUG
  1270. void CEditView::AssertValid() const
  1271. {
  1272.     CCtrlView::AssertValid();
  1273.     ASSERT_VALID(&m_aPageStart);
  1274.     if (m_hPrinterFont != NULL)
  1275.         ASSERT_VALID(CFont::FromHandle(m_hPrinterFont));
  1276.     if (m_hMirrorFont != NULL)
  1277.         ASSERT_VALID(CFont::FromHandle(m_hMirrorFont));
  1278.     _AFX_EDIT_STATE* pEditState = _afxEditState;
  1279.     if (pEditState->pFindReplaceDlg != NULL)
  1280.         ASSERT_VALID(pEditState->pFindReplaceDlg);
  1281. }
  1282.  
  1283. void CEditView::Dump(CDumpContext& dc) const
  1284. {
  1285.     CCtrlView::Dump(dc);
  1286.  
  1287.     dc << "m_nTabStops = " << m_nTabStops;
  1288.     if (m_hPrinterFont != NULL)
  1289.         dc << "\nm_hPrinterFont " << (UINT)m_hPrinterFont;
  1290.     if (m_hMirrorFont != NULL)
  1291.         dc << "\nm_hMirrorFont " << (UINT)m_hMirrorFont;
  1292.     dc << "\nm_aPageStart: " << &m_aPageStart;
  1293.     dc << "\nstatic member data:";
  1294.     _AFX_EDIT_STATE* pEditState = _afxEditState;
  1295.     if (pEditState->pFindReplaceDlg != NULL)
  1296.     {
  1297.         dc << "\npFindReplaceDlg = "
  1298.             << (void*)pEditState->pFindReplaceDlg;
  1299.         dc << "\nbFindOnly = " << pEditState->bFindOnly;
  1300.     }
  1301.     dc << "\nstrFind = " << pEditState->strFind;
  1302.     dc << "\nstrReplace = " << pEditState->strReplace;
  1303.     dc << "\nbCase = " << pEditState->bCase;
  1304.     dc << "\nbNext = " << pEditState->bNext;
  1305.  
  1306.     dc << "\n";
  1307. }
  1308. #endif //_DEBUG
  1309.  
  1310. #ifdef AFX_INIT_SEG
  1311. #pragma code_seg(AFX_INIT_SEG)
  1312. #endif
  1313.  
  1314. IMPLEMENT_DYNCREATE(CEditView, CCtrlView)
  1315.  
  1316. #pragma warning(disable: 4074)
  1317. #pragma init_seg(lib)
  1318.  
  1319. PROCESS_LOCAL(_AFX_EDIT_STATE, _afxEditState)
  1320.  
  1321. /////////////////////////////////////////////////////////////////////////////
  1322.