summaryrefslogtreecommitdiff
path: root/plugins/SmartAutoReplier/wtl/atlfind.h
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/SmartAutoReplier/wtl/atlfind.h')
-rw-r--r--plugins/SmartAutoReplier/wtl/atlfind.h1032
1 files changed, 1032 insertions, 0 deletions
diff --git a/plugins/SmartAutoReplier/wtl/atlfind.h b/plugins/SmartAutoReplier/wtl/atlfind.h
new file mode 100644
index 0000000000..832aba204e
--- /dev/null
+++ b/plugins/SmartAutoReplier/wtl/atlfind.h
@@ -0,0 +1,1032 @@
+// 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<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 != 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<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__