| 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__
|
|
|