// Windows Template Library - WTL version 8.1 // Copyright (C) Microsoft Corporation. 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 // Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php) // which can be found in the file CPL.TXT at the root of this distribution. // By using this software in any fashion, you are agreeing to be bound by // the terms of this license. You must not remove this notice, or // any other, from this software. #ifndef __ATLFIND_H__ #define __ATLFIND_H__ #pragma once #ifdef _WIN32_WCE #error atlfind.h is not supported on Windows CE #endif #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 #if !((defined(__ATLMISC_H__) && defined(_WTL_USE_CSTRING)) || defined(__ATLSTR_H__)) #error atlfind.h requires CString (either from ATL's atlstr.h or WTL's atlmisc.h with _WTL_USE_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; // Data members TFindReplaceDialog* m_pFindReplaceDialog; _CSTRING_NS::CString m_sFindNext, m_sReplaceWith; BOOL m_bFindOnly, m_bFirstSearch, m_bMatchCase, m_bWholeWord, m_bFindDown; LONG m_nInitialSearchPos; HCURSOR m_hOldCursor; // Enumerations enum TranslationTextItem { eText_OnReplaceAllMessage = 0, eText_OnReplaceAllTitle = 1, eText_OnTextNotFoundMessage = 2, eText_OnTextNotFoundTitle = 3 }; public: // 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(WM_DESTROY, OnDestroy) 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 OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { if(m_pFindReplaceDialog != NULL) { m_pFindReplaceDialog->SendMessage(WM_CLOSE); ATLASSERT(m_pFindReplaceDialog == NULL); } bHandled = FALSE; return 0; } 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 = 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); ::ClientToScreen(pT->GetParent(), &point); CRect rect; ::GetWindowRect(hWndDialog, &rect); if(rect.PtInRect(point)) { if(point.y > rect.Height()) { rect.OffsetRect(0, point.y - rect.bottom - 20); } else { int nVertExt = GetSystemMetrics(SM_CYSCREEN); if(point.y + rect.Height() < nVertExt) rect.OffsetRect(0, 40 + point.y - rect.top); } ::MoveWindow(hWndDialog, rect.left, rect.top, rect.Width(), rect.Height(), TRUE); } } DWORD GetFindReplaceDialogFlags(void) 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); _CSTRING_NS::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; _CSTRING_NS::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 _CSTRING_NS::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); } _CSTRING_NS::CString GetTranslationText(enum TranslationTextItem eItem) const { _CSTRING_NS::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); _CSTRING_NS::CString message = pT->GetTranslationText(eText_OnReplaceAllMessage); if(message.GetLength() > 0) { _CSTRING_NS::CString formattedMessage; formattedMessage.Format(message, replaceCount, m_sFindNext, 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); _CSTRING_NS::CString message = pT->GetTranslationText(eText_OnTextNotFoundMessage); if(message.GetLength() > 0) { _CSTRING_NS::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; // Data members LPTSTR m_pShadowBuffer; // Special shadow buffer only used in some cases. UINT m_nShadowSize; int m_bShadowBufferNeeded; // TRUE, FALSE, < 0 => Need to check public: // Constructors CEditFindReplaceImpl() : m_pShadowBuffer(NULL), m_nShadowSize(0), m_bShadowBufferNeeded(-1) { } virtual ~CEditFindReplaceImpl() { if(m_pShadowBuffer != NULL) { delete [] m_pShadowBuffer; m_pShadowBuffer = NULL; } } // 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 = { 0 }; ::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; 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); BOOL useShadowBuffer = pT->UseShadowBuffer(); if(useShadowBuffer) { if(m_pShadowBuffer == NULL || pT->GetModify()) { ATLASSERT(m_pShadowBuffer != NULL || m_nShadowSize == 0); UINT nSize = pT->GetWindowTextLength() + 1; if(nSize > m_nShadowSize) { // need more room for shadow buffer T* pThisNoConst = const_cast(pT); delete[] m_pShadowBuffer; pThisNoConst->m_pShadowBuffer = NULL; pThisNoConst->m_nShadowSize = 0; pThisNoConst->m_pShadowBuffer = new TCHAR[nSize]; pThisNoConst->m_nShadowSize = nSize; } // update the shadow buffer with GetWindowText ATLASSERT(m_nShadowSize >= nSize); ATLASSERT(m_pShadowBuffer != NULL); pT->GetWindowText(m_pShadowBuffer, nSize); } return m_pShadowBuffer; } 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); BOOL useShadowBuffer = pT->UseShadowBuffer(); if(!useShadowBuffer) { 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(_CSTRING_NS::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; SecureHelper::memcpy_x(strText.GetBuffer(nLen), nLen * sizeof(TCHAR), lpszText + nStartChar, nLen * sizeof(TCHAR)); strText.ReleaseBuffer(nLen); pT->UnlockBuffer(); return nLen; } BOOL UseShadowBuffer(void) const { const T* pT = static_cast(this); if(pT->m_bShadowBufferNeeded < 0) { T* pThisNoConst = const_cast(pT); OSVERSIONINFO ovi = { 0 }; ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); ::GetVersionEx(&ovi); bool bWin9x = (ovi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); if(bWin9x) { // Windows 95, 98, ME // Under Win9x, it is necessary to maintain a shadow buffer. // It is only updated when the control contents have been changed. pThisNoConst->m_bShadowBufferNeeded = TRUE; } else { // Windows NT, 2000, XP, etc. pThisNoConst->m_bShadowBufferNeeded = FALSE; #ifndef _UNICODE // On Windows XP (or later), if common controls version 6 is in use // (such as via theming), then EM_GETHANDLE will always return a UNICODE string. // If theming is enabled and Common Controls version 6 is in use, // you're really not suppose to superclass or subclass common controls // with an ANSI windows procedure (so its best to only theme if you use UNICODE). // Using a shadow buffer uses GetWindowText instead, so it solves // this problem for us (although it makes it a little less efficient). if((ovi.dwMajorVersion == 5 && ovi.dwMinorVersion >= 1) || (ovi.dwMajorVersion > 5)) { // We use DLLVERSIONINFO_private so we don't have to depend on shlwapi.h typedef struct _DLLVERSIONINFO_private { DWORD cbSize; DWORD dwMajorVersion; DWORD dwMinorVersion; DWORD dwBuildNumber; DWORD dwPlatformID; } DLLVERSIONINFO_private; HMODULE hModule = ::LoadLibrary("comctl32.dll"); if(hModule != NULL) { typedef HRESULT (CALLBACK *LPFN_DllGetVersion)(DLLVERSIONINFO_private *); LPFN_DllGetVersion fnDllGetVersion = (LPFN_DllGetVersion)::GetProcAddress(hModule, "DllGetVersion"); if(fnDllGetVersion != NULL) { DLLVERSIONINFO_private version = { 0 }; version.cbSize = sizeof(DLLVERSIONINFO_private); if(SUCCEEDED(fnDllGetVersion(&version))) { if(version.dwMajorVersion >= 6) { pThisNoConst->m_bShadowBufferNeeded = TRUE; ATLTRACE2(atlTraceUI, 0, _T("Warning: You have compiled for MBCS/ANSI but are using common controls version 6 or later (likely through a manifest file).\r\n")); ATLTRACE2(atlTraceUI, 0, _T("If you use common controls version 6 or later, you should only do so for UNICODE builds.\r\n")); } } } ::FreeLibrary(hModule); hModule = NULL; } } #endif // !_UNICODE } } return (pT->m_bShadowBufferNeeded != FALSE); } }; /////////////////////////////////////////////////////////////////////////////// // 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 = { 0 }; pT->GetSel(ft.chrg); if(m_bFirstSearch) { if(bFindDown) m_nInitialSearchPos = ft.chrg.cpMin; else m_nInitialSearchPos = ft.chrg.cpMax; m_bFirstSearch = FALSE; } #if (_RICHEDIT_VER >= 0x0200) ft.lpstrText = (LPTSTR)lpszFind; #else // !(_RICHEDIT_VER >= 0x0200) USES_CONVERSION; ft.lpstrText = T2A((LPTSTR)lpszFind); #endif // !(_RICHEDIT_VER >= 0x0200) 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() + m_nInitialSearchPos; if(bFindDown) { if(m_nInitialSearchPos >= 0) ft.chrg.cpMax = pT->GetTextLength(); dwFlags |= FR_DOWN; ATLASSERT(ft.chrg.cpMax >= ft.chrg.cpMin); } else { if(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(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 = m_nInitialSearchPos; } else { ft.chrg.cpMin = pT->GetTextLength(); ft.chrg.cpMax = m_nInitialSearchPos; } m_nInitialSearchPos = 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__