home *** CD-ROM | disk | FTP | other *** search
/ Supercompiler 1997 / SUPERCOMPILER97.iso / MS_VC.50 / VC / MFC / SRC / VIEWEDIT.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1996-10-30  |  33.8 KB  |  1,371 lines

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