📄 atlfind.h
字号:
// 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...
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -