Mercurial > foo_out_sdl
diff foosdk/wtl/Include/atlfind.h @ 1:20d02a178406 default tip
*: check in everything else
yay
| author | Paper <paper@tflc.us> |
|---|---|
| date | Mon, 05 Jan 2026 02:15:46 -0500 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/foosdk/wtl/Include/atlfind.h Mon Jan 05 02:15:46 2026 -0500 @@ -0,0 +1,884 @@ +// Windows Template Library - WTL version 10.0 +// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved. +// +// This file is a part of the Windows Template Library. +// The use and distribution terms for this software are covered by the +// Microsoft Public License (http://opensource.org/licenses/MS-PL) +// which can be found in the file MS-PL.txt at the root folder. + +#ifndef __ATLFIND_H__ +#define __ATLFIND_H__ + +#pragma once + +#ifndef __ATLCTRLS_H__ + #error atlfind.h requires atlctrls.h to be included first +#endif + +#ifndef __ATLDLGS_H__ + #error atlfind.h requires atldlgs.h to be included first +#endif + +#ifndef __ATLSTR_H__ + #error atlfind.h requires CString +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// Classes in this file: +// +// CEditFindReplaceImplBase<T, TFindReplaceDialog> +// CEditFindReplaceImpl<T, TFindReplaceDialog> +// CRichEditFindReplaceImpl<T, TFindReplaceDialog> + + +namespace WTL +{ + +/////////////////////////////////////////////////////////////////////////////// +// CEditFindReplaceImplBase - Base class for mixin classes that +// help implement Find/Replace for CEdit or CRichEditCtrl based window classes. + +template <class T, class TFindReplaceDialog = CFindReplaceDialog> +class CEditFindReplaceImplBase +{ +protected: +// Typedefs + typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> thisClass; + +// Enumerations + enum TranslationTextItem + { + eText_OnReplaceAllMessage = 0, + eText_OnReplaceAllTitle = 1, + eText_OnTextNotFoundMessage = 2, + eText_OnTextNotFoundTitle = 3 + }; + +public: +// Data members + TFindReplaceDialog* m_pFindReplaceDialog; + ATL::CString m_sFindNext, m_sReplaceWith; + BOOL m_bFindOnly, m_bFirstSearch, m_bMatchCase, m_bWholeWord, m_bFindDown; + LONG m_nInitialSearchPos; + HCURSOR m_hOldCursor; + +// Constructors + CEditFindReplaceImplBase() : + m_pFindReplaceDialog(NULL), + m_bFindOnly(TRUE), + m_bFirstSearch(TRUE), + m_bMatchCase(FALSE), + m_bWholeWord(FALSE), + m_bFindDown(TRUE), + m_nInitialSearchPos(0), + m_hOldCursor(NULL) + { + } + +// Message Handlers + BEGIN_MSG_MAP(thisClass) + ALT_MSG_MAP(1) + MESSAGE_HANDLER(TFindReplaceDialog::GetFindReplaceMsg(), OnFindReplaceCmd) + COMMAND_ID_HANDLER(ID_EDIT_FIND, OnEditFind) + COMMAND_ID_HANDLER(ID_EDIT_REPEAT, OnEditRepeat) + COMMAND_ID_HANDLER(ID_EDIT_REPLACE, OnEditReplace) + END_MSG_MAP() + + LRESULT OnFindReplaceCmd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/) + { + T* pT = static_cast<T*>(this); + + TFindReplaceDialog* pDialog = TFindReplaceDialog::GetNotifier(lParam); + if(pDialog == NULL) + { + ATLASSERT(FALSE); + ::MessageBeep(MB_ICONERROR); + return 1; + } + ATLASSERT(pDialog == m_pFindReplaceDialog); + + LPFINDREPLACE findReplace = (LPFINDREPLACE)lParam; + if((m_pFindReplaceDialog != NULL) && (findReplace != NULL)) + { + if(pDialog->FindNext()) + { + pT->OnFindNext(pDialog->GetFindString(), pDialog->SearchDown(), + pDialog->MatchCase(), pDialog->MatchWholeWord()); + } + else if(pDialog->ReplaceCurrent()) + { + pT->OnReplaceSel(pDialog->GetFindString(), + pDialog->SearchDown(), pDialog->MatchCase(), pDialog->MatchWholeWord(), + pDialog->GetReplaceString()); + } + else if(pDialog->ReplaceAll()) + { + pT->OnReplaceAll(pDialog->GetFindString(), pDialog->GetReplaceString(), + pDialog->MatchCase(), pDialog->MatchWholeWord()); + } + else if(pDialog->IsTerminating()) + { + // Dialog is going away (but hasn't gone away yet) + // OnFinalMessage will "delete this" + pT->OnTerminatingFindReplaceDialog(m_pFindReplaceDialog); + m_pFindReplaceDialog = NULL; + } + } + + return 0; + } + + LRESULT OnEditFind(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) + { + T* pT = static_cast<T*>(this); + pT->FindReplace(TRUE); + + return 0; + } + + LRESULT OnEditRepeat(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) + { + T* pT = static_cast<T*>(this); + + // If the user is holding down SHIFT when hitting F3, we'll + // search in reverse. Otherwise, we'll search forward. + // (be sure to have an accelerator mapped to ID_EDIT_REPEAT + // for both F3 and Shift+F3) + m_bFindDown = !((::GetKeyState(VK_SHIFT) & 0x8000) == 0x8000); + + if(m_sFindNext.IsEmpty()) + { + pT->FindReplace(TRUE); + } + else + { + if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown)) + pT->TextNotFound(m_sFindNext); + } + + return 0; + } + + LRESULT OnEditReplace(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& bHandled) + { + T* pT = static_cast<T*>(this); + + DWORD style = pT->GetStyle(); + if((style & ES_READONLY) != ES_READONLY) + { + pT->FindReplace(FALSE); + } + else + { + // Don't allow replace when the edit control is read only + bHandled = FALSE; + } + + return 0; + } + +// Operations (overrideable) + TFindReplaceDialog* CreateFindReplaceDialog(BOOL bFindOnly, // TRUE for Find, FALSE for FindReplace + LPCTSTR lpszFindWhat, + LPCTSTR lpszReplaceWith = NULL, + DWORD dwFlags = FR_DOWN, + HWND hWndParent = NULL) + { + // You can override all of this in a derived class + + TFindReplaceDialog* findReplaceDialog = new TFindReplaceDialog(); + if(findReplaceDialog == NULL) + { + ::MessageBeep(MB_ICONHAND); + } + else + { + HWND hWndFindReplace = findReplaceDialog->Create(bFindOnly, + lpszFindWhat, lpszReplaceWith, dwFlags, hWndParent); + if(hWndFindReplace == NULL) + { + delete findReplaceDialog; + findReplaceDialog = NULL; + } + else + { + findReplaceDialog->SetActiveWindow(); + findReplaceDialog->ShowWindow(SW_SHOW); + } + } + + return findReplaceDialog; + } + + void AdjustDialogPosition(HWND hWndDialog) + { + ATLASSERT((hWndDialog != NULL) && ::IsWindow(hWndDialog)); + + T* pT = static_cast<T*>(this); + LONG nStartChar = 0, nEndChar = 0; + // Send EM_GETSEL so we can use both Edit and RichEdit + // (CEdit::GetSel uses int&, and CRichEditCtrlT::GetSel uses LONG&) + ::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARAM)&nEndChar); + POINT point = pT->PosFromChar(nStartChar); + pT->ClientToScreen(&point); + RECT rect = {}; + ::GetWindowRect(hWndDialog, &rect); + if(::PtInRect(&rect, point) != FALSE) + { + if(point.y > (rect.bottom - rect.top)) + { + ::OffsetRect(&rect, 0, point.y - rect.bottom - 20); + } + else + { + int nVertExt = GetSystemMetrics(SM_CYSCREEN); + if((point.y + (rect.bottom - rect.top)) < nVertExt) + ::OffsetRect(&rect, 0, 40 + point.y - rect.top); + } + + ::MoveWindow(hWndDialog, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); + } + } + + DWORD GetFindReplaceDialogFlags() const + { + DWORD dwFlags = 0; + if(m_bFindDown) + dwFlags |= FR_DOWN; + if(m_bMatchCase) + dwFlags |= FR_MATCHCASE; + if(m_bWholeWord) + dwFlags |= FR_WHOLEWORD; + + return dwFlags; + } + + void FindReplace(BOOL bFindOnly) + { + T* pT = static_cast<T*>(this); + m_bFirstSearch = TRUE; + if(m_pFindReplaceDialog != NULL) + { + if(m_bFindOnly == bFindOnly) + { + m_pFindReplaceDialog->SetActiveWindow(); + m_pFindReplaceDialog->ShowWindow(SW_SHOW); + return; + } + else + { + m_pFindReplaceDialog->SendMessage(WM_CLOSE); + ATLASSERT(m_pFindReplaceDialog == NULL); + } + } + + ATLASSERT(m_pFindReplaceDialog == NULL); + + ATL::CString findNext; + pT->GetSelText(findNext); + // if selection is empty or spans multiple lines use old find text + if(findNext.IsEmpty() || (findNext.FindOneOf(_T("\n\r")) != -1)) + findNext = m_sFindNext; + ATL::CString replaceWith = m_sReplaceWith; + DWORD dwFlags = pT->GetFindReplaceDialogFlags(); + + m_pFindReplaceDialog = pT->CreateFindReplaceDialog(bFindOnly, + findNext, replaceWith, dwFlags, pT->operator HWND()); + ATLASSERT(m_pFindReplaceDialog != NULL); + if(m_pFindReplaceDialog != NULL) + m_bFindOnly = bFindOnly; + } + + BOOL SameAsSelected(LPCTSTR lpszCompare, BOOL bMatchCase, BOOL /*bWholeWord*/) + { + T* pT = static_cast<T*>(this); + + // check length first + size_t nLen = lstrlen(lpszCompare); + LONG nStartChar = 0, nEndChar = 0; + // Send EM_GETSEL so we can use both Edit and RichEdit + // (CEdit::GetSel uses int&, and CRichEditCtrlT::GetSel uses LONG&) + ::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARAM)&nEndChar); + if(nLen != (size_t)(nEndChar - nStartChar)) + return FALSE; + + // length is the same, check contents + ATL::CString selectedText; + pT->GetSelText(selectedText); + + return (bMatchCase && (selectedText.Compare(lpszCompare) == 0)) || + (!bMatchCase && (selectedText.CompareNoCase(lpszCompare) == 0)); + } + + void TextNotFound(LPCTSTR lpszFind) + { + T* pT = static_cast<T*>(this); + m_bFirstSearch = TRUE; + pT->OnTextNotFound(lpszFind); + } + + ATL::CString GetTranslationText(enum TranslationTextItem eItem) const + { + ATL::CString text; + switch(eItem) + { + case eText_OnReplaceAllMessage: + text = _T("Replaced %d occurances of \"%s\" with \"%s\""); + break; + case eText_OnReplaceAllTitle: + text = _T("Replace All"); + break; + case eText_OnTextNotFoundMessage: + text = _T("Unable to find the text \"%s\""); + break; + case eText_OnTextNotFoundTitle: + text = _T("Text not found"); + break; + } + + return text; + } + +// Overrideable Handlers + void OnFindNext(LPCTSTR lpszFind, BOOL bFindDown, BOOL bMatchCase, BOOL bWholeWord) + { + T* pT = static_cast<T*>(this); + + m_sFindNext = lpszFind; + m_bMatchCase = bMatchCase; + m_bWholeWord = bWholeWord; + m_bFindDown = bFindDown; + + if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown)) + pT->TextNotFound(m_sFindNext); + else + pT->AdjustDialogPosition(m_pFindReplaceDialog->operator HWND()); + } + + void OnReplaceSel(LPCTSTR lpszFind, BOOL bFindDown, BOOL bMatchCase, BOOL bWholeWord, LPCTSTR lpszReplace) + { + T* pT = static_cast<T*>(this); + + m_sFindNext = lpszFind; + m_sReplaceWith = lpszReplace; + m_bMatchCase = bMatchCase; + m_bWholeWord = bWholeWord; + m_bFindDown = bFindDown; + + if(pT->SameAsSelected(m_sFindNext, m_bMatchCase, m_bWholeWord)) + pT->ReplaceSel(m_sReplaceWith); + + if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown)) + pT->TextNotFound(m_sFindNext); + else + pT->AdjustDialogPosition(m_pFindReplaceDialog->operator HWND()); + } + + void OnReplaceAll(LPCTSTR lpszFind, LPCTSTR lpszReplace, BOOL bMatchCase, BOOL bWholeWord) + { + T* pT = static_cast<T*>(this); + + m_sFindNext = lpszFind; + m_sReplaceWith = lpszReplace; + m_bMatchCase = bMatchCase; + m_bWholeWord = bWholeWord; + m_bFindDown = TRUE; + + // no selection or different than what looking for + if(!pT->SameAsSelected(m_sFindNext, m_bMatchCase, m_bWholeWord)) + { + if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown)) + { + pT->TextNotFound(m_sFindNext); + return; + } + } + + pT->OnReplaceAllCoreBegin(); + + int replaceCount=0; + do + { + ++replaceCount; + pT->ReplaceSel(m_sReplaceWith); + } while(pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown)); + + pT->OnReplaceAllCoreEnd(replaceCount); + } + + void OnReplaceAllCoreBegin() + { + T* pT = static_cast<T*>(this); + + m_hOldCursor = ::SetCursor(::LoadCursor(NULL, IDC_WAIT)); + + pT->HideSelection(TRUE, FALSE); + + } + + void OnReplaceAllCoreEnd(int replaceCount) + { + T* pT = static_cast<T*>(this); + pT->HideSelection(FALSE, FALSE); + + ::SetCursor(m_hOldCursor); + + ATL::CString message = pT->GetTranslationText(eText_OnReplaceAllMessage); + if(message.GetLength() > 0) + { + ATL::CString formattedMessage; + formattedMessage.Format(message, replaceCount, (LPCTSTR)m_sFindNext, (LPCTSTR)m_sReplaceWith); + if(m_pFindReplaceDialog != NULL) + { + m_pFindReplaceDialog->MessageBox(formattedMessage, + pT->GetTranslationText(eText_OnReplaceAllTitle), + MB_OK | MB_ICONINFORMATION | MB_APPLMODAL); + } + else + { + pT->MessageBox(formattedMessage, + pT->GetTranslationText(eText_OnReplaceAllTitle), + MB_OK | MB_ICONINFORMATION | MB_APPLMODAL); + } + } + } + + void OnTextNotFound(LPCTSTR lpszFind) + { + T* pT = static_cast<T*>(this); + ATL::CString message = pT->GetTranslationText(eText_OnTextNotFoundMessage); + if(message.GetLength() > 0) + { + ATL::CString formattedMessage; + formattedMessage.Format(message, lpszFind); + if(m_pFindReplaceDialog != NULL) + { + m_pFindReplaceDialog->MessageBox(formattedMessage, + pT->GetTranslationText(eText_OnTextNotFoundTitle), + MB_OK | MB_ICONINFORMATION | MB_APPLMODAL); + } + else + { + pT->MessageBox(formattedMessage, + pT->GetTranslationText(eText_OnTextNotFoundTitle), + MB_OK | MB_ICONINFORMATION | MB_APPLMODAL); + } + } + else + { + ::MessageBeep(MB_ICONHAND); + } + } + + void OnTerminatingFindReplaceDialog(TFindReplaceDialog*& /*findReplaceDialog*/) + { + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// CEditFindReplaceImpl - Mixin class for implementing Find/Replace for CEdit +// based window classes. + +// Chain to CEditFindReplaceImpl message map. Your class must also derive from CEdit. +// Example: +// class CMyEdit : public CWindowImpl<CMyEdit, CEdit>, +// public CEditFindReplaceImpl<CMyEdit> +// { +// public: +// BEGIN_MSG_MAP(CMyEdit) +// // your handlers... +// CHAIN_MSG_MAP_ALT(CEditFindReplaceImpl<CMyEdit>, 1) +// END_MSG_MAP() +// // other stuff... +// }; + +template <class T, class TFindReplaceDialog = CFindReplaceDialog> +class CEditFindReplaceImpl : public CEditFindReplaceImplBase<T, TFindReplaceDialog> +{ +protected: + typedef CEditFindReplaceImpl<T, TFindReplaceDialog> thisClass; + typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> baseClass; + +public: +// Message Handlers + BEGIN_MSG_MAP(thisClass) + ALT_MSG_MAP(1) + CHAIN_MSG_MAP_ALT(baseClass, 1) + END_MSG_MAP() + +// Operations + // Supported only for RichEdit, so this does nothing for Edit + void HideSelection(BOOL /*bHide*/ = TRUE, BOOL /*bChangeStyle*/ = FALSE) + { + } + +// Operations (overrideable) + BOOL FindTextSimple(LPCTSTR lpszFind, BOOL bMatchCase, BOOL bWholeWord, BOOL bFindDown = TRUE) + { + T* pT = static_cast<T*>(this); + + ATLASSERT(lpszFind != NULL); + ATLASSERT(*lpszFind != _T('\0')); + + UINT nLen = pT->GetBufferLength(); + int nStartChar = 0, nEndChar = 0; + pT->GetSel(nStartChar, nEndChar); + UINT nStart = nStartChar; + int iDir = bFindDown ? +1 : -1; + + // can't find a match before the first character + if((nStart == 0) && (iDir < 0)) + return FALSE; + + LPCTSTR lpszText = pT->LockBuffer(); + + bool isDBCS = false; +#ifdef _MBCS + CPINFO info = {}; + ::GetCPInfo(::GetOEMCP(), &info); + isDBCS = (info.MaxCharSize > 1); +#endif + + if(iDir < 0) + { + // always go back one for search backwards + nStart -= int((lpszText + nStart) - ::CharPrev(lpszText, lpszText + nStart)); + } + else if((nStartChar != nEndChar) && (pT->SameAsSelected(lpszFind, bMatchCase, bWholeWord))) + { + // easy to go backward/forward with SBCS +#ifndef _UNICODE + if(::IsDBCSLeadByte(lpszText[nStart])) + nStart++; +#endif + nStart += iDir; + } + + // handle search with nStart past end of buffer + UINT nLenFind = ::lstrlen(lpszFind); + if((nStart + nLenFind - 1) >= nLen) + { + if((iDir < 0) && (nLen >= nLenFind)) + { + if(isDBCS) + { + // walk back to previous character n times + nStart = nLen; + int n = nLenFind; + while(n--) + { + nStart -= int((lpszText + nStart) - ::CharPrev(lpszText, lpszText + nStart)); + } + } + else + { + // single-byte character set is easy and fast + nStart = nLen - nLenFind; + } + ATLASSERT((nStart + nLenFind - 1) <= nLen); + } + else + { + pT->UnlockBuffer(); + return FALSE; + } + } + + // start the search at nStart + LPCTSTR lpsz = lpszText + nStart; + typedef int (WINAPI* CompareProc)(LPCTSTR str1, LPCTSTR str2); + CompareProc pfnCompare = bMatchCase ? lstrcmp : lstrcmpi; + + if(isDBCS) + { + // double-byte string search + LPCTSTR lpszStop = NULL; + if(iDir > 0) + { + // start at current and find _first_ occurrance + lpszStop = lpszText + nLen - nLenFind + 1; + } + else + { + // start at top and find _last_ occurrance + lpszStop = lpsz; + lpsz = lpszText; + } + + LPCTSTR lpszFound = NULL; + while(lpsz <= lpszStop) + { +#ifndef _UNICODE + if(!bMatchCase || ((*lpsz == *lpszFind) && (!::IsDBCSLeadByte(*lpsz) || (lpsz[1] == lpszFind[1])))) +#else + if(!bMatchCase || ((*lpsz == *lpszFind) && (lpsz[1] == lpszFind[1]))) +#endif + { + LPTSTR lpch = (LPTSTR)(lpsz + nLenFind); + TCHAR chSave = *lpch; + *lpch = _T('\0'); + int nResult = (*pfnCompare)(lpsz, lpszFind); + *lpch = chSave; + if(nResult == 0) + { + lpszFound = lpsz; + if(iDir > 0) + break; + } + } + lpsz = ::CharNext(lpsz); + } + pT->UnlockBuffer(); + + if(lpszFound != NULL) + { + int n = (int)(lpszFound - lpszText); + pT->SetSel(n, n + nLenFind); + return TRUE; + } + } + else + { + // single-byte string search + UINT nCompare = 0; + if(iDir < 0) + nCompare = (UINT)(lpsz - lpszText) + 1; + else + nCompare = nLen - (UINT)(lpsz - lpszText) - nLenFind + 1; + + while(nCompare > 0) + { + ATLASSERT(lpsz >= lpszText); + ATLASSERT((lpsz + nLenFind - 1) <= (lpszText + nLen - 1)); + + LPSTR lpch = (LPSTR)(lpsz + nLenFind); + char chSave = *lpch; + *lpch = '\0'; + int nResult = (*pfnCompare)(lpsz, lpszFind); + *lpch = chSave; + if(nResult == 0) + { + pT->UnlockBuffer(); + int n = (int)(lpsz - lpszText); + pT->SetSel(n, n + nLenFind); + return TRUE; + } + + // restore character at end of search + *lpch = chSave; + + // move on to next substring + nCompare--; + lpsz += iDir; + } + pT->UnlockBuffer(); + } + + return FALSE; + } + + LPCTSTR LockBuffer() const + { + const T* pT = static_cast<const T*>(this); + ATLASSERT(pT->m_hWnd != NULL); + +#ifndef _UNICODE + // If common controls version 6 is in use (such as via theming), then EM_GETHANDLE + // will always return a UNICODE string. You're really not supposed to superclass + // or subclass common controls with an ANSI windows procedure. + DWORD dwMajor = 0, dwMinor = 0; + HRESULT hRet = ATL::AtlGetCommCtrlVersion(&dwMajor, &dwMinor); + if(SUCCEEDED(hRet) && (dwMajor >= 6)) + { + ATLTRACE2(atlTraceUI, 0, _T("AtlFind Warning: You have compiled for MBCS/ANSI but are using common controls version 6 or later which are always Unicode.\r\n")); + ATLASSERT(FALSE); + } +#endif // !_UNICODE + + HLOCAL hLocal = pT->GetHandle(); + ATLASSERT(hLocal != NULL); + LPCTSTR lpszText = (LPCTSTR)::LocalLock(hLocal); + ATLASSERT(lpszText != NULL); + + return lpszText; + } + + void UnlockBuffer() const + { + const T* pT = static_cast<const T*>(this); + ATLASSERT(pT->m_hWnd != NULL); + + HLOCAL hLocal = pT->GetHandle(); + ATLASSERT(hLocal != NULL); + ::LocalUnlock(hLocal); + } + + UINT GetBufferLength() const + { + const T* pT = static_cast<const T*>(this); + ATLASSERT(pT->m_hWnd != NULL); + + UINT nLen = 0; + LPCTSTR lpszText = pT->LockBuffer(); + if(lpszText != NULL) + nLen = ::lstrlen(lpszText); + pT->UnlockBuffer(); + + return nLen; + } + + LONG EndOfLine(LPCTSTR lpszText, UINT nLen, UINT nIndex) const + { + LPCTSTR lpsz = lpszText + nIndex; + LPCTSTR lpszStop = lpszText + nLen; + while((lpsz < lpszStop) && (*lpsz != _T('\r'))) + ++lpsz; + return LONG(lpsz - lpszText); + } + + LONG GetSelText(ATL::CString& strText) const + { + const T* pT = static_cast<const T*>(this); + + int nStartChar = 0, nEndChar = 0; + pT->GetSel(nStartChar, nEndChar); + ATLASSERT((UINT)nEndChar <= pT->GetBufferLength()); + LPCTSTR lpszText = pT->LockBuffer(); + LONG nLen = pT->EndOfLine(lpszText, nEndChar, nStartChar) - nStartChar; + ATL::Checked::memcpy_s(strText.GetBuffer(nLen), nLen * sizeof(TCHAR), lpszText + nStartChar, nLen * sizeof(TCHAR)); + strText.ReleaseBuffer(nLen); + pT->UnlockBuffer(); + + return nLen; + } +}; + + +/////////////////////////////////////////////////////////////////////////////// +// CRichEditFindReplaceImpl - Mixin class for implementing Find/Replace for CRichEditCtrl +// based window classes. + +// Chain to CRichEditFindReplaceImpl message map. Your class must also derive from CRichEditCtrl. +// Example: +// class CMyRichEdit : public CWindowImpl<CMyRichEdit, CRichEditCtrl>, +// public CRichEditFindReplaceImpl<CMyRichEdit> +// { +// public: +// BEGIN_MSG_MAP(CMyRichEdit) +// // your handlers... +// CHAIN_MSG_MAP_ALT(CRichEditFindReplaceImpl<CMyRichEdit>, 1) +// END_MSG_MAP() +// // other stuff... +// }; + +template <class T, class TFindReplaceDialog = CFindReplaceDialog> +class CRichEditFindReplaceImpl : public CEditFindReplaceImplBase<T, TFindReplaceDialog> +{ +protected: + typedef CRichEditFindReplaceImpl<T, TFindReplaceDialog> thisClass; + typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> baseClass; + +public: + BEGIN_MSG_MAP(thisClass) + ALT_MSG_MAP(1) + CHAIN_MSG_MAP_ALT(baseClass, 1) + END_MSG_MAP() + +// Operations (overrideable) + BOOL FindTextSimple(LPCTSTR lpszFind, BOOL bMatchCase, BOOL bWholeWord, BOOL bFindDown = TRUE) + { + T* pT = static_cast<T*>(this); + + ATLASSERT(lpszFind != NULL); + FINDTEXTEX ft = {}; + + pT->GetSel(ft.chrg); + if(this->m_bFirstSearch) + { + if(bFindDown) + this->m_nInitialSearchPos = ft.chrg.cpMin; + else + this->m_nInitialSearchPos = ft.chrg.cpMax; + this->m_bFirstSearch = FALSE; + } + + ft.lpstrText = (LPTSTR)lpszFind; + + if(ft.chrg.cpMin != ft.chrg.cpMax) // i.e. there is a selection + { + if(bFindDown) + { + ft.chrg.cpMin++; + } + else + { + // won't wraparound backwards + ft.chrg.cpMin = __max(ft.chrg.cpMin, 0); + } + } + + DWORD dwFlags = bMatchCase ? FR_MATCHCASE : 0; + dwFlags |= bWholeWord ? FR_WHOLEWORD : 0; + + ft.chrg.cpMax = pT->GetTextLength() + this->m_nInitialSearchPos; + + if(bFindDown) + { + if(this->m_nInitialSearchPos >= 0) + ft.chrg.cpMax = pT->GetTextLength(); + + dwFlags |= FR_DOWN; + ATLASSERT(ft.chrg.cpMax >= ft.chrg.cpMin); + } + else + { + if(this->m_nInitialSearchPos >= 0) + ft.chrg.cpMax = 0; + + dwFlags &= ~FR_DOWN; + ATLASSERT(ft.chrg.cpMax <= ft.chrg.cpMin); + } + + BOOL bRet = FALSE; + if(pT->FindAndSelect(dwFlags, ft) != -1) + { + bRet = TRUE; // we found the text + } + else if(this->m_nInitialSearchPos > 0) + { + // if the original starting point was not the beginning + // of the buffer and we haven't already been here + if(bFindDown) + { + ft.chrg.cpMin = 0; + ft.chrg.cpMax = this->m_nInitialSearchPos; + } + else + { + ft.chrg.cpMin = pT->GetTextLength(); + ft.chrg.cpMax = this->m_nInitialSearchPos; + } + this->m_nInitialSearchPos = this->m_nInitialSearchPos - pT->GetTextLength(); + + bRet = (pT->FindAndSelect(dwFlags, ft) != -1) ? TRUE : FALSE; + } + + return bRet; + } + + long FindAndSelect(DWORD dwFlags, FINDTEXTEX& ft) + { + T* pT = static_cast<T*>(this); + LONG index = pT->FindText(dwFlags, ft); + if(index != -1) // i.e. we found something + pT->SetSel(ft.chrgText); + + return index; + } +}; + +} // namespace WTL + +#endif // __ATLFIND_H__
