Index: third_party/wtl/include/atlfind.h |
diff --git a/third_party/wtl/include/atlfind.h b/third_party/wtl/include/atlfind.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..35a630e79470545665f16ebc7ab634908b8dd474 |
--- /dev/null |
+++ b/third_party/wtl/include/atlfind.h |
@@ -0,0 +1,1033 @@ |
+// Windows Template Library - WTL version 8.0 |
+// 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 |
+// Microsoft Permissive License (Ms-PL) which can be found in the file |
+// Ms-PL.txt at the root of this distribution. |
+ |
+#ifndef __ATLFIND_H__ |
+#define __ATLFIND_H__ |
+ |
+#pragma once |
+ |
+#ifndef __cplusplus |
+ #error ATL requires C++ compilation (use a .cpp suffix) |
+#endif |
+ |
+#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<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; |
+ |
+// 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<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); |
+ ::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<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); |
+ |
+ _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<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 |
+ _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<T*>(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<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); |
+ |
+ _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<T*>(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<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; |
+ |
+// 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<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 = { 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<const T*>(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<T*>(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<const T*>(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<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(_CSTRING_NS::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; |
+ 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<const T*>(this); |
+ |
+ if(pT->m_bShadowBufferNeeded < 0) |
+ { |
+ T* pThisNoConst = const_cast<T*>(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 == TRUE); |
+ } |
+}; |
+ |
+ |
+/////////////////////////////////////////////////////////////////////////////// |
+// 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 = { 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<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__ |