// 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 // CEditFindReplaceImpl // CRichEditFindReplaceImpl namespace WTL { /////////////////////////////////////////////////////////////////////////////// // CEditFindReplaceImplBase - Base class for mixin classes that // help implement Find/Replace for CEdit or CRichEditCtrl based window classes. template class CEditFindReplaceImplBase { protected: // Typedefs typedef CEditFindReplaceImplBase 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(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(this); pT->FindReplace(TRUE); return 0; } LRESULT OnEditRepeat(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { T* pT = static_cast(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(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 = NULL; ATLTRY(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(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(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(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(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(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(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(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(this); m_hOldCursor = ::SetCursor(::LoadCursor(NULL, IDC_WAIT)); pT->HideSelection(TRUE, FALSE); } void OnReplaceAllCoreEnd(int replaceCount) { T* pT = static_cast(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(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, // public CEditFindReplaceImpl // { // public: // BEGIN_MSG_MAP(CMyEdit) // // your handlers... // CHAIN_MSG_MAP_ALT(CEditFindReplaceImpl, 1) // END_MSG_MAP() // // other stuff... // }; template class CEditFindReplaceImpl : public CEditFindReplaceImplBase { protected: typedef CEditFindReplaceImpl thisClass; typedef CEditFindReplaceImplBase 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(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(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(this); ATLASSERT(pT->m_hWnd != NULL); HLOCAL hLocal = pT->GetHandle(); ATLASSERT(hLocal != NULL); ::LocalUnlock(hLocal); } UINT GetBufferLength() const { const T* pT = static_cast(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(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, // public CRichEditFindReplaceImpl // { // public: // BEGIN_MSG_MAP(CMyRichEdit) // // your handlers... // CHAIN_MSG_MAP_ALT(CRichEditFindReplaceImpl, 1) // END_MSG_MAP() // // other stuff... // }; template class CRichEditFindReplaceImpl : public CEditFindReplaceImplBase { protected: typedef CRichEditFindReplaceImpl thisClass; typedef CEditFindReplaceImplBase 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(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(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__