1 // Windows Template Library - WTL version 8.0
\r
2 // Copyright (C) Microsoft Corporation. All rights reserved.
\r
4 // This file is a part of the Windows Template Library.
\r
5 // The use and distribution terms for this software are covered by the
\r
6 // Common Public License 1.0 (http://opensource.org/osi3.0/licenses/cpl1.0.php)
\r
7 // which can be found in the file CPL.TXT at the root of this distribution.
\r
8 // By using this software in any fashion, you are agreeing to be bound by
\r
9 // the terms of this license. You must not remove this notice, or
\r
10 // any other, from this software.
\r
12 #ifndef __ATLFIND_H__
\r
13 #define __ATLFIND_H__
\r
18 #error ATL requires C++ compilation (use a .cpp suffix)
\r
22 #error atlfind.h is not supported on Windows CE
\r
25 #ifndef __ATLCTRLS_H__
\r
26 #error atlfind.h requires atlctrls.h to be included first
\r
29 #ifndef __ATLDLGS_H__
\r
30 #error atlfind.h requires atldlgs.h to be included first
\r
33 #if !((defined(__ATLMISC_H__) && defined(_WTL_USE_CSTRING)) || defined(__ATLSTR_H__))
\r
34 #error atlfind.h requires CString (either from ATL's atlstr.h or WTL's atlmisc.h with _WTL_USE_CSTRING)
\r
38 ///////////////////////////////////////////////////////////////////////////////
\r
39 // Classes in this file:
\r
41 // CEditFindReplaceImplBase<T, TFindReplaceDialog>
\r
42 // CEditFindReplaceImpl<T, TFindReplaceDialog>
\r
43 // CRichEditFindReplaceImpl<T, TFindReplaceDialog>
\r
49 ///////////////////////////////////////////////////////////////////////////////
\r
50 // CEditFindReplaceImplBase - Base class for mixin classes that
\r
51 // help implement Find/Replace for CEdit or CRichEditCtrl based window classes.
\r
53 template <class T, class TFindReplaceDialog = CFindReplaceDialog>
\r
54 class CEditFindReplaceImplBase
\r
58 typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> thisClass;
\r
61 TFindReplaceDialog* m_pFindReplaceDialog;
\r
62 _CSTRING_NS::CString m_sFindNext, m_sReplaceWith;
\r
63 BOOL m_bFindOnly, m_bFirstSearch, m_bMatchCase, m_bWholeWord, m_bFindDown;
\r
64 LONG m_nInitialSearchPos;
\r
65 HCURSOR m_hOldCursor;
\r
68 enum TranslationTextItem
\r
70 eText_OnReplaceAllMessage = 0,
\r
71 eText_OnReplaceAllTitle = 1,
\r
72 eText_OnTextNotFoundMessage = 2,
\r
73 eText_OnTextNotFoundTitle = 3
\r
78 CEditFindReplaceImplBase() :
\r
79 m_pFindReplaceDialog(NULL),
\r
81 m_bFirstSearch(TRUE),
\r
82 m_bMatchCase(FALSE),
\r
83 m_bWholeWord(FALSE),
\r
85 m_nInitialSearchPos(0),
\r
91 BEGIN_MSG_MAP(thisClass)
\r
93 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
\r
94 MESSAGE_HANDLER(TFindReplaceDialog::GetFindReplaceMsg(), OnFindReplaceCmd)
\r
95 COMMAND_ID_HANDLER(ID_EDIT_FIND, OnEditFind)
\r
96 COMMAND_ID_HANDLER(ID_EDIT_REPEAT, OnEditRepeat)
\r
97 COMMAND_ID_HANDLER(ID_EDIT_REPLACE, OnEditReplace)
\r
100 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
\r
102 if(m_pFindReplaceDialog != NULL)
\r
104 m_pFindReplaceDialog->SendMessage(WM_CLOSE);
\r
105 ATLASSERT(m_pFindReplaceDialog == NULL);
\r
112 LRESULT OnFindReplaceCmd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
\r
114 T* pT = static_cast<T*>(this);
\r
116 TFindReplaceDialog* pDialog = TFindReplaceDialog::GetNotifier(lParam);
\r
117 if(pDialog == NULL)
\r
120 ::MessageBeep(MB_ICONERROR);
\r
123 ATLASSERT(pDialog == m_pFindReplaceDialog);
\r
125 LPFINDREPLACE findReplace = (LPFINDREPLACE)lParam;
\r
126 if((m_pFindReplaceDialog != NULL) && (findReplace != NULL))
\r
128 if(pDialog->FindNext())
\r
130 pT->OnFindNext(pDialog->GetFindString(), pDialog->SearchDown(),
\r
131 pDialog->MatchCase(), pDialog->MatchWholeWord());
\r
133 else if(pDialog->ReplaceCurrent())
\r
135 pT->OnReplaceSel(pDialog->GetFindString(),
\r
136 pDialog->SearchDown(), pDialog->MatchCase(), pDialog->MatchWholeWord(),
\r
137 pDialog->GetReplaceString());
\r
139 else if(pDialog->ReplaceAll())
\r
141 pT->OnReplaceAll(pDialog->GetFindString(), pDialog->GetReplaceString(),
\r
142 pDialog->MatchCase(), pDialog->MatchWholeWord());
\r
144 else if(pDialog->IsTerminating())
\r
146 // Dialog is going away (but hasn't gone away yet)
\r
147 // OnFinalMessage will "delete this"
\r
148 pT->OnTerminatingFindReplaceDialog(m_pFindReplaceDialog);
\r
149 m_pFindReplaceDialog = NULL;
\r
156 LRESULT OnEditFind(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
\r
158 T* pT = static_cast<T*>(this);
\r
159 pT->FindReplace(TRUE);
\r
164 LRESULT OnEditRepeat(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
\r
166 T* pT = static_cast<T*>(this);
\r
168 // If the user is holding down SHIFT when hitting F3, we'll
\r
169 // search in reverse. Otherwise, we'll search forward.
\r
170 // (be sure to have an accelerator mapped to ID_EDIT_REPEAT
\r
171 // for both F3 and Shift+F3)
\r
172 m_bFindDown = !((::GetKeyState(VK_SHIFT) & 0x8000) == 0x8000);
\r
174 if(m_sFindNext.IsEmpty())
\r
176 pT->FindReplace(TRUE);
\r
180 if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown))
\r
181 pT->TextNotFound(m_sFindNext);
\r
187 LRESULT OnEditReplace(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& bHandled)
\r
189 T* pT = static_cast<T*>(this);
\r
191 DWORD style = pT->GetStyle();
\r
192 if((style & ES_READONLY) != ES_READONLY)
\r
194 pT->FindReplace(FALSE);
\r
198 // Don't allow replace when the edit control is read only
\r
205 // Operations (overrideable)
\r
206 TFindReplaceDialog* CreateFindReplaceDialog(BOOL bFindOnly, // TRUE for Find, FALSE for FindReplace
\r
207 LPCTSTR lpszFindWhat,
\r
208 LPCTSTR lpszReplaceWith = NULL,
\r
209 DWORD dwFlags = FR_DOWN,
\r
210 HWND hWndParent = NULL)
\r
212 // You can override all of this in a derived class
\r
214 TFindReplaceDialog* findReplaceDialog = new TFindReplaceDialog();
\r
215 if(findReplaceDialog == NULL)
\r
217 ::MessageBeep(MB_ICONHAND);
\r
221 HWND hWndFindReplace = findReplaceDialog->Create(bFindOnly,
\r
222 lpszFindWhat, lpszReplaceWith, dwFlags, hWndParent);
\r
223 if(hWndFindReplace == NULL)
\r
225 delete findReplaceDialog;
\r
226 findReplaceDialog = NULL;
\r
230 findReplaceDialog->SetActiveWindow();
\r
231 findReplaceDialog->ShowWindow(SW_SHOW);
\r
235 return findReplaceDialog;
\r
238 void AdjustDialogPosition(HWND hWndDialog)
\r
240 ATLASSERT((hWndDialog != NULL) && ::IsWindow(hWndDialog));
\r
242 T* pT = static_cast<T*>(this);
\r
243 LONG nStartChar = 0, nEndChar = 0;
\r
244 // Send EM_GETSEL so we can use both Edit and RichEdit
\r
245 // (CEdit::GetSel uses int&, and CRichEditCtrlT::GetSel uses LONG&)
\r
246 ::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARAM)&nEndChar);
\r
247 POINT point = pT->PosFromChar(nStartChar);
\r
248 ::ClientToScreen(pT->GetParent(), &point);
\r
250 ::GetWindowRect(hWndDialog, &rect);
\r
251 if(rect.PtInRect(point))
\r
253 if(point.y > rect.Height())
\r
255 rect.OffsetRect(0, point.y - rect.bottom - 20);
\r
259 int nVertExt = GetSystemMetrics(SM_CYSCREEN);
\r
260 if(point.y + rect.Height() < nVertExt)
\r
261 rect.OffsetRect(0, 40 + point.y - rect.top);
\r
264 ::MoveWindow(hWndDialog, rect.left, rect.top, rect.Width(), rect.Height(), TRUE);
\r
268 DWORD GetFindReplaceDialogFlags(void) const
\r
273 dwFlags |= FR_DOWN;
\r
275 dwFlags |= FR_MATCHCASE;
\r
277 dwFlags |= FR_WHOLEWORD;
\r
282 void FindReplace(BOOL bFindOnly)
\r
284 T* pT = static_cast<T*>(this);
\r
285 m_bFirstSearch = TRUE;
\r
286 if(m_pFindReplaceDialog != NULL)
\r
288 if(m_bFindOnly == bFindOnly)
\r
290 m_pFindReplaceDialog->SetActiveWindow();
\r
291 m_pFindReplaceDialog->ShowWindow(SW_SHOW);
\r
296 m_pFindReplaceDialog->SendMessage(WM_CLOSE);
\r
297 ATLASSERT(m_pFindReplaceDialog == NULL);
\r
301 ATLASSERT(m_pFindReplaceDialog == NULL);
\r
303 _CSTRING_NS::CString findNext;
\r
304 pT->GetSelText(findNext);
\r
305 // if selection is empty or spans multiple lines use old find text
\r
306 if(findNext.IsEmpty() || (findNext.FindOneOf(_T("\n\r")) != -1))
\r
307 findNext = m_sFindNext;
\r
308 _CSTRING_NS::CString replaceWith = m_sReplaceWith;
\r
309 DWORD dwFlags = pT->GetFindReplaceDialogFlags();
\r
311 m_pFindReplaceDialog = pT->CreateFindReplaceDialog(bFindOnly,
\r
312 findNext, replaceWith, dwFlags, pT->operator HWND());
\r
313 ATLASSERT(m_pFindReplaceDialog != NULL);
\r
314 if(m_pFindReplaceDialog != NULL)
\r
315 m_bFindOnly = bFindOnly;
\r
318 BOOL SameAsSelected(LPCTSTR lpszCompare, BOOL bMatchCase, BOOL /*bWholeWord*/)
\r
320 T* pT = static_cast<T*>(this);
\r
322 // check length first
\r
323 size_t nLen = lstrlen(lpszCompare);
\r
324 LONG nStartChar = 0, nEndChar = 0;
\r
325 // Send EM_GETSEL so we can use both Edit and RichEdit
\r
326 // (CEdit::GetSel uses int&, and CRichEditCtrlT::GetSel uses LONG&)
\r
327 ::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARAM)&nEndChar);
\r
328 if(nLen != (size_t)(nEndChar - nStartChar))
\r
331 // length is the same, check contents
\r
332 _CSTRING_NS::CString selectedText;
\r
333 pT->GetSelText(selectedText);
\r
335 return (bMatchCase && selectedText.Compare(lpszCompare) == 0) ||
\r
336 (!bMatchCase && selectedText.CompareNoCase(lpszCompare) == 0);
\r
339 void TextNotFound(LPCTSTR lpszFind)
\r
341 T* pT = static_cast<T*>(this);
\r
342 m_bFirstSearch = TRUE;
\r
343 pT->OnTextNotFound(lpszFind);
\r
346 _CSTRING_NS::CString GetTranslationText(enum TranslationTextItem eItem) const
\r
348 _CSTRING_NS::CString text;
\r
351 case eText_OnReplaceAllMessage:
\r
352 text = _T("Replaced %d occurances of \"%s\" with \"%s\"");
\r
354 case eText_OnReplaceAllTitle:
\r
355 text = _T("Replace All");
\r
357 case eText_OnTextNotFoundMessage:
\r
358 text = _T("Unable to find the text \"%s\"");
\r
360 case eText_OnTextNotFoundTitle:
\r
361 text = _T("Text not found");
\r
368 // Overrideable Handlers
\r
369 void OnFindNext(LPCTSTR lpszFind, BOOL bFindDown, BOOL bMatchCase, BOOL bWholeWord)
\r
371 T* pT = static_cast<T*>(this);
\r
373 m_sFindNext = lpszFind;
\r
374 m_bMatchCase = bMatchCase;
\r
375 m_bWholeWord = bWholeWord;
\r
376 m_bFindDown = bFindDown;
\r
378 if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown))
\r
379 pT->TextNotFound(m_sFindNext);
\r
381 pT->AdjustDialogPosition(m_pFindReplaceDialog->operator HWND());
\r
384 void OnReplaceSel(LPCTSTR lpszFind, BOOL bFindDown, BOOL bMatchCase, BOOL bWholeWord, LPCTSTR lpszReplace)
\r
386 T* pT = static_cast<T*>(this);
\r
388 m_sFindNext = lpszFind;
\r
389 m_sReplaceWith = lpszReplace;
\r
390 m_bMatchCase = bMatchCase;
\r
391 m_bWholeWord = bWholeWord;
\r
392 m_bFindDown = bFindDown;
\r
394 if(pT->SameAsSelected(m_sFindNext, m_bMatchCase, m_bWholeWord))
\r
395 pT->ReplaceSel(m_sReplaceWith);
\r
397 if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown))
\r
398 pT->TextNotFound(m_sFindNext);
\r
400 pT->AdjustDialogPosition(m_pFindReplaceDialog->operator HWND());
\r
403 void OnReplaceAll(LPCTSTR lpszFind, LPCTSTR lpszReplace, BOOL bMatchCase, BOOL bWholeWord)
\r
405 T* pT = static_cast<T*>(this);
\r
407 m_sFindNext = lpszFind;
\r
408 m_sReplaceWith = lpszReplace;
\r
409 m_bMatchCase = bMatchCase;
\r
410 m_bWholeWord = bWholeWord;
\r
411 m_bFindDown = TRUE;
\r
413 // no selection or different than what looking for
\r
414 if(!pT->SameAsSelected(m_sFindNext, m_bMatchCase, m_bWholeWord))
\r
416 if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown))
\r
418 pT->TextNotFound(m_sFindNext);
\r
423 pT->OnReplaceAllCoreBegin();
\r
425 int replaceCount=0;
\r
429 pT->ReplaceSel(m_sReplaceWith);
\r
430 } while(pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown));
\r
432 pT->OnReplaceAllCoreEnd(replaceCount);
\r
435 void OnReplaceAllCoreBegin()
\r
437 T* pT = static_cast<T*>(this);
\r
439 m_hOldCursor = ::SetCursor(::LoadCursor(NULL, IDC_WAIT));
\r
441 pT->HideSelection(TRUE, FALSE);
\r
445 void OnReplaceAllCoreEnd(int replaceCount)
\r
447 T* pT = static_cast<T*>(this);
\r
448 pT->HideSelection(FALSE, FALSE);
\r
450 ::SetCursor(m_hOldCursor);
\r
452 _CSTRING_NS::CString message = pT->GetTranslationText(eText_OnReplaceAllMessage);
\r
453 if(message.GetLength() > 0)
\r
455 _CSTRING_NS::CString formattedMessage;
\r
456 formattedMessage.Format(message,
\r
457 replaceCount, m_sFindNext, m_sReplaceWith);
\r
458 if(m_pFindReplaceDialog != NULL)
\r
460 m_pFindReplaceDialog->MessageBox(formattedMessage,
\r
461 pT->GetTranslationText(eText_OnReplaceAllTitle),
\r
462 MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);
\r
466 pT->MessageBox(formattedMessage,
\r
467 pT->GetTranslationText(eText_OnReplaceAllTitle),
\r
468 MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);
\r
473 void OnTextNotFound(LPCTSTR lpszFind)
\r
475 T* pT = static_cast<T*>(this);
\r
476 _CSTRING_NS::CString message = pT->GetTranslationText(eText_OnTextNotFoundMessage);
\r
477 if(message.GetLength() > 0)
\r
479 _CSTRING_NS::CString formattedMessage;
\r
480 formattedMessage.Format(message, lpszFind);
\r
481 if(m_pFindReplaceDialog != NULL)
\r
483 m_pFindReplaceDialog->MessageBox(formattedMessage,
\r
484 pT->GetTranslationText(eText_OnTextNotFoundTitle),
\r
485 MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);
\r
489 pT->MessageBox(formattedMessage,
\r
490 pT->GetTranslationText(eText_OnTextNotFoundTitle),
\r
491 MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);
\r
496 ::MessageBeep(MB_ICONHAND);
\r
500 void OnTerminatingFindReplaceDialog(TFindReplaceDialog*& /*findReplaceDialog*/)
\r
506 ///////////////////////////////////////////////////////////////////////////////
\r
507 // CEditFindReplaceImpl - Mixin class for implementing Find/Replace for CEdit
\r
508 // based window classes.
\r
510 // Chain to CEditFindReplaceImpl message map. Your class must also derive from CEdit.
\r
512 // class CMyEdit : public CWindowImpl<CMyEdit, CEdit>,
\r
513 // public CEditFindReplaceImpl<CMyEdit>
\r
516 // BEGIN_MSG_MAP(CMyEdit)
\r
517 // // your handlers...
\r
518 // CHAIN_MSG_MAP_ALT(CEditFindReplaceImpl<CMyEdit>, 1)
\r
520 // // other stuff...
\r
523 template <class T, class TFindReplaceDialog = CFindReplaceDialog>
\r
524 class CEditFindReplaceImpl : public CEditFindReplaceImplBase<T, TFindReplaceDialog>
\r
527 typedef CEditFindReplaceImpl<T, TFindReplaceDialog> thisClass;
\r
528 typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> baseClass;
\r
531 LPTSTR m_pShadowBuffer; // Special shadow buffer only used in some cases.
\r
532 UINT m_nShadowSize;
\r
533 int m_bShadowBufferNeeded; // TRUE, FALSE, < 0 => Need to check
\r
537 CEditFindReplaceImpl() :
\r
538 m_pShadowBuffer(NULL),
\r
540 m_bShadowBufferNeeded(-1)
\r
544 virtual ~CEditFindReplaceImpl()
\r
546 if(m_pShadowBuffer != NULL)
\r
548 delete [] m_pShadowBuffer;
\r
549 m_pShadowBuffer = NULL;
\r
553 // Message Handlers
\r
554 BEGIN_MSG_MAP(thisClass)
\r
556 CHAIN_MSG_MAP_ALT(baseClass, 1)
\r
560 // Supported only for RichEdit, so this does nothing for Edit
\r
561 void HideSelection(BOOL /*bHide*/ = TRUE, BOOL /*bChangeStyle*/ = FALSE)
\r
565 // Operations (overrideable)
\r
566 BOOL FindTextSimple(LPCTSTR lpszFind, BOOL bMatchCase, BOOL bWholeWord, BOOL bFindDown = TRUE)
\r
568 T* pT = static_cast<T*>(this);
\r
570 ATLASSERT(lpszFind != NULL);
\r
571 ATLASSERT(*lpszFind != _T('\0'));
\r
573 UINT nLen = pT->GetBufferLength();
\r
574 int nStartChar = 0, nEndChar = 0;
\r
575 pT->GetSel(nStartChar, nEndChar);
\r
576 UINT nStart = nStartChar;
\r
577 int iDir = bFindDown ? +1 : -1;
\r
579 // can't find a match before the first character
\r
580 if(nStart == 0 && iDir < 0)
\r
583 LPCTSTR lpszText = pT->LockBuffer();
\r
585 bool isDBCS = false;
\r
587 CPINFO info = { 0 };
\r
588 ::GetCPInfo(::GetOEMCP(), &info);
\r
589 isDBCS = (info.MaxCharSize > 1);
\r
594 // always go back one for search backwards
\r
595 nStart -= int((lpszText + nStart) - ::CharPrev(lpszText, lpszText + nStart));
\r
597 else if(nStartChar != nEndChar && pT->SameAsSelected(lpszFind, bMatchCase, bWholeWord))
\r
599 // easy to go backward/forward with SBCS
\r
601 if(::IsDBCSLeadByte(lpszText[nStart]))
\r
607 // handle search with nStart past end of buffer
\r
608 UINT nLenFind = ::lstrlen(lpszFind);
\r
609 if(nStart + nLenFind - 1 >= nLen)
\r
611 if(iDir < 0 && nLen >= nLenFind)
\r
615 // walk back to previous character n times
\r
620 nStart -= int((lpszText + nStart) - ::CharPrev(lpszText, lpszText + nStart));
\r
625 // single-byte character set is easy and fast
\r
626 nStart = nLen - nLenFind;
\r
628 ATLASSERT(nStart + nLenFind - 1 <= nLen);
\r
632 pT->UnlockBuffer();
\r
637 // start the search at nStart
\r
638 LPCTSTR lpsz = lpszText + nStart;
\r
639 typedef int (WINAPI* CompareProc)(LPCTSTR str1, LPCTSTR str2);
\r
640 CompareProc pfnCompare = bMatchCase ? lstrcmp : lstrcmpi;
\r
644 // double-byte string search
\r
645 LPCTSTR lpszStop = NULL;
\r
648 // start at current and find _first_ occurrance
\r
649 lpszStop = lpszText + nLen - nLenFind + 1;
\r
653 // start at top and find _last_ occurrance
\r
658 LPCTSTR lpszFound = NULL;
\r
659 while(lpsz <= lpszStop)
\r
662 if(!bMatchCase || (*lpsz == *lpszFind && (!::IsDBCSLeadByte(*lpsz) || lpsz[1] == lpszFind[1])))
\r
664 if(!bMatchCase || (*lpsz == *lpszFind && lpsz[1] == lpszFind[1]))
\r
667 LPTSTR lpch = (LPTSTR)(lpsz + nLenFind);
\r
668 TCHAR chSave = *lpch;
\r
670 int nResult = (*pfnCompare)(lpsz, lpszFind);
\r
679 lpsz = ::CharNext(lpsz);
\r
681 pT->UnlockBuffer();
\r
683 if(lpszFound != NULL)
\r
685 int n = (int)(lpszFound - lpszText);
\r
686 pT->SetSel(n, n + nLenFind);
\r
692 // single-byte string search
\r
695 nCompare = (UINT)(lpsz - lpszText) + 1;
\r
697 nCompare = nLen - (UINT)(lpsz - lpszText) - nLenFind + 1;
\r
699 while(nCompare > 0)
\r
701 ATLASSERT(lpsz >= lpszText);
\r
702 ATLASSERT(lpsz + nLenFind - 1 <= lpszText + nLen - 1);
\r
704 LPSTR lpch = (LPSTR)(lpsz + nLenFind);
\r
705 char chSave = *lpch;
\r
707 int nResult = (*pfnCompare)(lpsz, lpszFind);
\r
711 pT->UnlockBuffer();
\r
712 int n = (int)(lpsz - lpszText);
\r
713 pT->SetSel(n, n + nLenFind);
\r
717 // restore character at end of search
\r
720 // move on to next substring
\r
724 pT->UnlockBuffer();
\r
730 LPCTSTR LockBuffer() const
\r
732 const T* pT = static_cast<const T*>(this);
\r
734 ATLASSERT(pT->m_hWnd != NULL);
\r
736 BOOL useShadowBuffer = pT->UseShadowBuffer();
\r
737 if(useShadowBuffer)
\r
739 if(m_pShadowBuffer == NULL || pT->GetModify())
\r
741 ATLASSERT(m_pShadowBuffer != NULL || m_nShadowSize == 0);
\r
742 UINT nSize = pT->GetWindowTextLength() + 1;
\r
743 if(nSize > m_nShadowSize)
\r
745 // need more room for shadow buffer
\r
746 T* pThisNoConst = const_cast<T*>(pT);
\r
747 delete[] m_pShadowBuffer;
\r
748 pThisNoConst->m_pShadowBuffer = NULL;
\r
749 pThisNoConst->m_nShadowSize = 0;
\r
750 pThisNoConst->m_pShadowBuffer = new TCHAR[nSize];
\r
751 pThisNoConst->m_nShadowSize = nSize;
\r
754 // update the shadow buffer with GetWindowText
\r
755 ATLASSERT(m_nShadowSize >= nSize);
\r
756 ATLASSERT(m_pShadowBuffer != NULL);
\r
757 pT->GetWindowText(m_pShadowBuffer, nSize);
\r
760 return m_pShadowBuffer;
\r
763 HLOCAL hLocal = pT->GetHandle();
\r
764 ATLASSERT(hLocal != NULL);
\r
765 LPCTSTR lpszText = (LPCTSTR)::LocalLock(hLocal);
\r
766 ATLASSERT(lpszText != NULL);
\r
771 void UnlockBuffer() const
\r
773 const T* pT = static_cast<const T*>(this);
\r
775 ATLASSERT(pT->m_hWnd != NULL);
\r
777 BOOL useShadowBuffer = pT->UseShadowBuffer();
\r
778 if(!useShadowBuffer)
\r
780 HLOCAL hLocal = pT->GetHandle();
\r
781 ATLASSERT(hLocal != NULL);
\r
782 ::LocalUnlock(hLocal);
\r
786 UINT GetBufferLength() const
\r
788 const T* pT = static_cast<const T*>(this);
\r
790 ATLASSERT(pT->m_hWnd != NULL);
\r
792 LPCTSTR lpszText = pT->LockBuffer();
\r
793 if(lpszText != NULL)
\r
794 nLen = ::lstrlen(lpszText);
\r
795 pT->UnlockBuffer();
\r
800 LONG EndOfLine(LPCTSTR lpszText, UINT nLen, UINT nIndex) const
\r
802 LPCTSTR lpsz = lpszText + nIndex;
\r
803 LPCTSTR lpszStop = lpszText + nLen;
\r
804 while(lpsz < lpszStop && *lpsz != _T('\r'))
\r
806 return LONG(lpsz - lpszText);
\r
809 LONG GetSelText(_CSTRING_NS::CString& strText) const
\r
811 const T* pT = static_cast<const T*>(this);
\r
813 int nStartChar = 0, nEndChar = 0;
\r
814 pT->GetSel(nStartChar, nEndChar);
\r
815 ATLASSERT((UINT)nEndChar <= pT->GetBufferLength());
\r
816 LPCTSTR lpszText = pT->LockBuffer();
\r
817 LONG nLen = pT->EndOfLine(lpszText, nEndChar, nStartChar) - nStartChar;
\r
818 SecureHelper::memcpy_x(strText.GetBuffer(nLen), nLen * sizeof(TCHAR), lpszText + nStartChar, nLen * sizeof(TCHAR));
\r
819 strText.ReleaseBuffer(nLen);
\r
820 pT->UnlockBuffer();
\r
825 BOOL UseShadowBuffer(void) const
\r
827 const T* pT = static_cast<const T*>(this);
\r
829 if(pT->m_bShadowBufferNeeded < 0)
\r
831 T* pThisNoConst = const_cast<T*>(pT);
\r
833 OSVERSIONINFO ovi = { 0 };
\r
834 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
\r
835 ::GetVersionEx(&ovi);
\r
837 bool bWin9x = (ovi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
\r
840 // Windows 95, 98, ME
\r
841 // Under Win9x, it is necessary to maintain a shadow buffer.
\r
842 // It is only updated when the control contents have been changed.
\r
843 pThisNoConst->m_bShadowBufferNeeded = TRUE;
\r
847 // Windows NT, 2000, XP, etc.
\r
848 pThisNoConst->m_bShadowBufferNeeded = FALSE;
\r
851 // On Windows XP (or later), if common controls version 6 is in use
\r
852 // (such as via theming), then EM_GETHANDLE will always return a UNICODE string.
\r
853 // If theming is enabled and Common Controls version 6 is in use,
\r
854 // you're really not suppose to superclass or subclass common controls
\r
855 // with an ANSI windows procedure (so its best to only theme if you use UNICODE).
\r
856 // Using a shadow buffer uses GetWindowText instead, so it solves
\r
857 // this problem for us (although it makes it a little less efficient).
\r
859 if((ovi.dwMajorVersion == 5 && ovi.dwMinorVersion >= 1) || (ovi.dwMajorVersion > 5))
\r
861 // We use DLLVERSIONINFO_private so we don't have to depend on shlwapi.h
\r
862 typedef struct _DLLVERSIONINFO_private
\r
865 DWORD dwMajorVersion;
\r
866 DWORD dwMinorVersion;
\r
867 DWORD dwBuildNumber;
\r
868 DWORD dwPlatformID;
\r
869 } DLLVERSIONINFO_private;
\r
871 HMODULE hModule = ::LoadLibrary("comctl32.dll");
\r
872 if(hModule != NULL)
\r
874 typedef HRESULT (CALLBACK *LPFN_DllGetVersion)(DLLVERSIONINFO_private *);
\r
875 LPFN_DllGetVersion fnDllGetVersion = (LPFN_DllGetVersion)::GetProcAddress(hModule, "DllGetVersion");
\r
876 if(fnDllGetVersion != NULL)
\r
878 DLLVERSIONINFO_private version = { 0 };
\r
879 version.cbSize = sizeof(DLLVERSIONINFO_private);
\r
880 if(SUCCEEDED(fnDllGetVersion(&version)))
\r
882 if(version.dwMajorVersion >= 6)
\r
884 pThisNoConst->m_bShadowBufferNeeded = TRUE;
\r
886 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"));
\r
887 ATLTRACE2(atlTraceUI, 0, _T("If you use common controls version 6 or later, you should only do so for UNICODE builds.\r\n"));
\r
892 ::FreeLibrary(hModule);
\r
896 #endif // !_UNICODE
\r
900 return (pT->m_bShadowBufferNeeded == TRUE);
\r
905 ///////////////////////////////////////////////////////////////////////////////
\r
906 // CRichEditFindReplaceImpl - Mixin class for implementing Find/Replace for CRichEditCtrl
\r
907 // based window classes.
\r
909 // Chain to CRichEditFindReplaceImpl message map. Your class must also derive from CRichEditCtrl.
\r
911 // class CMyRichEdit : public CWindowImpl<CMyRichEdit, CRichEditCtrl>,
\r
912 // public CRichEditFindReplaceImpl<CMyRichEdit>
\r
915 // BEGIN_MSG_MAP(CMyRichEdit)
\r
916 // // your handlers...
\r
917 // CHAIN_MSG_MAP_ALT(CRichEditFindReplaceImpl<CMyRichEdit>, 1)
\r
919 // // other stuff...
\r
922 template <class T, class TFindReplaceDialog = CFindReplaceDialog>
\r
923 class CRichEditFindReplaceImpl : public CEditFindReplaceImplBase<T, TFindReplaceDialog>
\r
926 typedef CRichEditFindReplaceImpl<T, TFindReplaceDialog> thisClass;
\r
927 typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> baseClass;
\r
930 BEGIN_MSG_MAP(thisClass)
\r
932 CHAIN_MSG_MAP_ALT(baseClass, 1)
\r
935 // Operations (overrideable)
\r
936 BOOL FindTextSimple(LPCTSTR lpszFind, BOOL bMatchCase, BOOL bWholeWord, BOOL bFindDown = TRUE)
\r
938 T* pT = static_cast<T*>(this);
\r
940 ATLASSERT(lpszFind != NULL);
\r
941 FINDTEXTEX ft = { 0 };
\r
943 pT->GetSel(ft.chrg);
\r
947 m_nInitialSearchPos = ft.chrg.cpMin;
\r
949 m_nInitialSearchPos = ft.chrg.cpMax;
\r
950 m_bFirstSearch = FALSE;
\r
953 #if (_RICHEDIT_VER >= 0x0200)
\r
954 ft.lpstrText = (LPTSTR)lpszFind;
\r
955 #else // !(_RICHEDIT_VER >= 0x0200)
\r
957 ft.lpstrText = T2A((LPTSTR)lpszFind);
\r
958 #endif // !(_RICHEDIT_VER >= 0x0200)
\r
960 if(ft.chrg.cpMin != ft.chrg.cpMax) // i.e. there is a selection
\r
968 // won't wraparound backwards
\r
969 ft.chrg.cpMin = max(ft.chrg.cpMin, 0);
\r
973 DWORD dwFlags = bMatchCase ? FR_MATCHCASE : 0;
\r
974 dwFlags |= bWholeWord ? FR_WHOLEWORD : 0;
\r
976 ft.chrg.cpMax = pT->GetTextLength() + m_nInitialSearchPos;
\r
980 if(m_nInitialSearchPos >= 0)
\r
981 ft.chrg.cpMax = pT->GetTextLength();
\r
983 dwFlags |= FR_DOWN;
\r
984 ATLASSERT(ft.chrg.cpMax >= ft.chrg.cpMin);
\r
988 if(m_nInitialSearchPos >= 0)
\r
991 dwFlags &= ~FR_DOWN;
\r
992 ATLASSERT(ft.chrg.cpMax <= ft.chrg.cpMin);
\r
997 if(pT->FindAndSelect(dwFlags, ft) != -1)
\r
999 bRet = TRUE; // we found the text
\r
1001 else if(m_nInitialSearchPos > 0)
\r
1003 // if the original starting point was not the beginning
\r
1004 // of the buffer and we haven't already been here
\r
1007 ft.chrg.cpMin = 0;
\r
1008 ft.chrg.cpMax = m_nInitialSearchPos;
\r
1012 ft.chrg.cpMin = pT->GetTextLength();
\r
1013 ft.chrg.cpMax = m_nInitialSearchPos;
\r
1015 m_nInitialSearchPos = m_nInitialSearchPos - pT->GetTextLength();
\r
1017 bRet = (pT->FindAndSelect(dwFlags, ft) != -1) ? TRUE : FALSE;
\r
1023 long FindAndSelect(DWORD dwFlags, FINDTEXTEX& ft)
\r
1025 T* pT = static_cast<T*>(this);
\r
1026 LONG index = pT->FindText(dwFlags, ft);
\r
1027 if(index != -1) // i.e. we found something
\r
1028 pT->SetSel(ft.chrgText);
\r
1034 }; // namespace WTL
\r
1036 #endif // __ATLFIND_H__
\r