Add ICU message format support
[chromium-blink-merge.git] / third_party / wtl / include / atlfind.h
blob35a630e79470545665f16ebc7ab634908b8dd474
1 // Windows Template Library - WTL version 8.0
2 // Copyright (C) Microsoft Corporation. All rights reserved.
3 //
4 // This file is a part of the Windows Template Library.
5 // The use and distribution terms for this software are covered by the
6 // Microsoft Permissive License (Ms-PL) which can be found in the file
7 // Ms-PL.txt at the root of this distribution.
9 #ifndef __ATLFIND_H__
10 #define __ATLFIND_H__
12 #pragma once
14 #ifndef __cplusplus
15 #error ATL requires C++ compilation (use a .cpp suffix)
16 #endif
18 #ifdef _WIN32_WCE
19 #error atlfind.h is not supported on Windows CE
20 #endif
22 #ifndef __ATLCTRLS_H__
23 #error atlfind.h requires atlctrls.h to be included first
24 #endif
26 #ifndef __ATLDLGS_H__
27 #error atlfind.h requires atldlgs.h to be included first
28 #endif
30 #if !((defined(__ATLMISC_H__) && defined(_WTL_USE_CSTRING)) || defined(__ATLSTR_H__))
31 #error atlfind.h requires CString (either from ATL's atlstr.h or WTL's atlmisc.h with _WTL_USE_CSTRING)
32 #endif
35 ///////////////////////////////////////////////////////////////////////////////
36 // Classes in this file:
38 // CEditFindReplaceImplBase<T, TFindReplaceDialog>
39 // CEditFindReplaceImpl<T, TFindReplaceDialog>
40 // CRichEditFindReplaceImpl<T, TFindReplaceDialog>
43 namespace WTL
46 ///////////////////////////////////////////////////////////////////////////////
47 // CEditFindReplaceImplBase - Base class for mixin classes that
48 // help implement Find/Replace for CEdit or CRichEditCtrl based window classes.
50 template <class T, class TFindReplaceDialog = CFindReplaceDialog>
51 class CEditFindReplaceImplBase
53 protected:
54 // Typedefs
55 typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> thisClass;
57 // Data members
58 TFindReplaceDialog* m_pFindReplaceDialog;
59 _CSTRING_NS::CString m_sFindNext, m_sReplaceWith;
60 BOOL m_bFindOnly, m_bFirstSearch, m_bMatchCase, m_bWholeWord, m_bFindDown;
61 LONG m_nInitialSearchPos;
62 HCURSOR m_hOldCursor;
64 // Enumerations
65 enum TranslationTextItem
67 eText_OnReplaceAllMessage = 0,
68 eText_OnReplaceAllTitle = 1,
69 eText_OnTextNotFoundMessage = 2,
70 eText_OnTextNotFoundTitle = 3
73 public:
74 // Constructors
75 CEditFindReplaceImplBase() :
76 m_pFindReplaceDialog(NULL),
77 m_bFindOnly(TRUE),
78 m_bFirstSearch(TRUE),
79 m_bMatchCase(FALSE),
80 m_bWholeWord(FALSE),
81 m_bFindDown(TRUE),
82 m_nInitialSearchPos(0),
83 m_hOldCursor(NULL)
87 // Message Handlers
88 BEGIN_MSG_MAP(thisClass)
89 ALT_MSG_MAP(1)
90 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
91 MESSAGE_HANDLER(TFindReplaceDialog::GetFindReplaceMsg(), OnFindReplaceCmd)
92 COMMAND_ID_HANDLER(ID_EDIT_FIND, OnEditFind)
93 COMMAND_ID_HANDLER(ID_EDIT_REPEAT, OnEditRepeat)
94 COMMAND_ID_HANDLER(ID_EDIT_REPLACE, OnEditReplace)
95 END_MSG_MAP()
97 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
99 if(m_pFindReplaceDialog != NULL)
101 m_pFindReplaceDialog->SendMessage(WM_CLOSE);
102 ATLASSERT(m_pFindReplaceDialog == NULL);
105 bHandled = FALSE;
106 return 0;
109 LRESULT OnFindReplaceCmd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
111 T* pT = static_cast<T*>(this);
113 TFindReplaceDialog* pDialog = TFindReplaceDialog::GetNotifier(lParam);
114 if(pDialog == NULL)
116 ATLASSERT(FALSE);
117 ::MessageBeep(MB_ICONERROR);
118 return 1;
120 ATLASSERT(pDialog == m_pFindReplaceDialog);
122 LPFINDREPLACE findReplace = (LPFINDREPLACE)lParam;
123 if((m_pFindReplaceDialog != NULL) && (findReplace != NULL))
125 if(pDialog->FindNext())
127 pT->OnFindNext(pDialog->GetFindString(), pDialog->SearchDown(),
128 pDialog->MatchCase(), pDialog->MatchWholeWord());
130 else if(pDialog->ReplaceCurrent())
132 pT->OnReplaceSel(pDialog->GetFindString(),
133 pDialog->SearchDown(), pDialog->MatchCase(), pDialog->MatchWholeWord(),
134 pDialog->GetReplaceString());
136 else if(pDialog->ReplaceAll())
138 pT->OnReplaceAll(pDialog->GetFindString(), pDialog->GetReplaceString(),
139 pDialog->MatchCase(), pDialog->MatchWholeWord());
141 else if(pDialog->IsTerminating())
143 // Dialog is going away (but hasn't gone away yet)
144 // OnFinalMessage will "delete this"
145 pT->OnTerminatingFindReplaceDialog(m_pFindReplaceDialog);
146 m_pFindReplaceDialog = NULL;
150 return 0;
153 LRESULT OnEditFind(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
155 T* pT = static_cast<T*>(this);
156 pT->FindReplace(TRUE);
158 return 0;
161 LRESULT OnEditRepeat(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
163 T* pT = static_cast<T*>(this);
165 // If the user is holding down SHIFT when hitting F3, we'll
166 // search in reverse. Otherwise, we'll search forward.
167 // (be sure to have an accelerator mapped to ID_EDIT_REPEAT
168 // for both F3 and Shift+F3)
169 m_bFindDown = !((::GetKeyState(VK_SHIFT) & 0x8000) == 0x8000);
171 if(m_sFindNext.IsEmpty())
173 pT->FindReplace(TRUE);
175 else
177 if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown))
178 pT->TextNotFound(m_sFindNext);
181 return 0;
184 LRESULT OnEditReplace(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& bHandled)
186 T* pT = static_cast<T*>(this);
188 DWORD style = pT->GetStyle();
189 if((style & ES_READONLY) != ES_READONLY)
191 pT->FindReplace(FALSE);
193 else
195 // Don't allow replace when the edit control is read only
196 bHandled = FALSE;
199 return 0;
202 // Operations (overrideable)
203 TFindReplaceDialog* CreateFindReplaceDialog(BOOL bFindOnly, // TRUE for Find, FALSE for FindReplace
204 LPCTSTR lpszFindWhat,
205 LPCTSTR lpszReplaceWith = NULL,
206 DWORD dwFlags = FR_DOWN,
207 HWND hWndParent = NULL)
209 // You can override all of this in a derived class
211 TFindReplaceDialog* findReplaceDialog = new TFindReplaceDialog();
212 if(findReplaceDialog == NULL)
214 ::MessageBeep(MB_ICONHAND);
216 else
218 HWND hWndFindReplace = findReplaceDialog->Create(bFindOnly,
219 lpszFindWhat, lpszReplaceWith, dwFlags, hWndParent);
220 if(hWndFindReplace == NULL)
222 delete findReplaceDialog;
223 findReplaceDialog = NULL;
225 else
227 findReplaceDialog->SetActiveWindow();
228 findReplaceDialog->ShowWindow(SW_SHOW);
232 return findReplaceDialog;
235 void AdjustDialogPosition(HWND hWndDialog)
237 ATLASSERT((hWndDialog != NULL) && ::IsWindow(hWndDialog));
239 T* pT = static_cast<T*>(this);
240 LONG nStartChar = 0, nEndChar = 0;
241 // Send EM_GETSEL so we can use both Edit and RichEdit
242 // (CEdit::GetSel uses int&, and CRichEditCtrlT::GetSel uses LONG&)
243 ::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARAM)&nEndChar);
244 POINT point = pT->PosFromChar(nStartChar);
245 ::ClientToScreen(pT->GetParent(), &point);
246 CRect rect;
247 ::GetWindowRect(hWndDialog, &rect);
248 if(rect.PtInRect(point))
250 if(point.y > rect.Height())
252 rect.OffsetRect(0, point.y - rect.bottom - 20);
254 else
256 int nVertExt = GetSystemMetrics(SM_CYSCREEN);
257 if(point.y + rect.Height() < nVertExt)
258 rect.OffsetRect(0, 40 + point.y - rect.top);
261 ::MoveWindow(hWndDialog, rect.left, rect.top, rect.Width(), rect.Height(), TRUE);
265 DWORD GetFindReplaceDialogFlags(void) const
267 DWORD dwFlags = 0;
269 if(m_bFindDown)
270 dwFlags |= FR_DOWN;
271 if(m_bMatchCase)
272 dwFlags |= FR_MATCHCASE;
273 if(m_bWholeWord)
274 dwFlags |= FR_WHOLEWORD;
276 return dwFlags;
279 void FindReplace(BOOL bFindOnly)
281 T* pT = static_cast<T*>(this);
282 m_bFirstSearch = TRUE;
283 if(m_pFindReplaceDialog != NULL)
285 if(m_bFindOnly == bFindOnly)
287 m_pFindReplaceDialog->SetActiveWindow();
288 m_pFindReplaceDialog->ShowWindow(SW_SHOW);
289 return;
291 else
293 m_pFindReplaceDialog->SendMessage(WM_CLOSE);
294 ATLASSERT(m_pFindReplaceDialog == NULL);
298 ATLASSERT(m_pFindReplaceDialog == NULL);
300 _CSTRING_NS::CString findNext;
301 pT->GetSelText(findNext);
302 // if selection is empty or spans multiple lines use old find text
303 if(findNext.IsEmpty() || (findNext.FindOneOf(_T("\n\r")) != -1))
304 findNext = m_sFindNext;
305 _CSTRING_NS::CString replaceWith = m_sReplaceWith;
306 DWORD dwFlags = pT->GetFindReplaceDialogFlags();
308 m_pFindReplaceDialog = pT->CreateFindReplaceDialog(bFindOnly,
309 findNext, replaceWith, dwFlags, pT->operator HWND());
310 ATLASSERT(m_pFindReplaceDialog != NULL);
311 if(m_pFindReplaceDialog != NULL)
312 m_bFindOnly = bFindOnly;
315 BOOL SameAsSelected(LPCTSTR lpszCompare, BOOL bMatchCase, BOOL /*bWholeWord*/)
317 T* pT = static_cast<T*>(this);
319 // check length first
320 size_t nLen = lstrlen(lpszCompare);
321 LONG nStartChar = 0, nEndChar = 0;
322 // Send EM_GETSEL so we can use both Edit and RichEdit
323 // (CEdit::GetSel uses int&, and CRichEditCtrlT::GetSel uses LONG&)
324 ::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARAM)&nEndChar);
325 if(nLen != (size_t)(nEndChar - nStartChar))
326 return FALSE;
328 // length is the same, check contents
329 _CSTRING_NS::CString selectedText;
330 pT->GetSelText(selectedText);
332 return (bMatchCase && selectedText.Compare(lpszCompare) == 0) ||
333 (!bMatchCase && selectedText.CompareNoCase(lpszCompare) == 0);
336 void TextNotFound(LPCTSTR lpszFind)
338 T* pT = static_cast<T*>(this);
339 m_bFirstSearch = TRUE;
340 pT->OnTextNotFound(lpszFind);
343 _CSTRING_NS::CString GetTranslationText(enum TranslationTextItem eItem) const
345 _CSTRING_NS::CString text;
346 switch(eItem)
348 case eText_OnReplaceAllMessage:
349 text = _T("Replaced %d occurances of \"%s\" with \"%s\"");
350 break;
351 case eText_OnReplaceAllTitle:
352 text = _T("Replace All");
353 break;
354 case eText_OnTextNotFoundMessage:
355 text = _T("Unable to find the text \"%s\"");
356 break;
357 case eText_OnTextNotFoundTitle:
358 text = _T("Text not found");
359 break;
362 return text;
365 // Overrideable Handlers
366 void OnFindNext(LPCTSTR lpszFind, BOOL bFindDown, BOOL bMatchCase, BOOL bWholeWord)
368 T* pT = static_cast<T*>(this);
370 m_sFindNext = lpszFind;
371 m_bMatchCase = bMatchCase;
372 m_bWholeWord = bWholeWord;
373 m_bFindDown = bFindDown;
375 if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown))
376 pT->TextNotFound(m_sFindNext);
377 else
378 pT->AdjustDialogPosition(m_pFindReplaceDialog->operator HWND());
381 void OnReplaceSel(LPCTSTR lpszFind, BOOL bFindDown, BOOL bMatchCase, BOOL bWholeWord, LPCTSTR lpszReplace)
383 T* pT = static_cast<T*>(this);
385 m_sFindNext = lpszFind;
386 m_sReplaceWith = lpszReplace;
387 m_bMatchCase = bMatchCase;
388 m_bWholeWord = bWholeWord;
389 m_bFindDown = bFindDown;
391 if(pT->SameAsSelected(m_sFindNext, m_bMatchCase, m_bWholeWord))
392 pT->ReplaceSel(m_sReplaceWith);
394 if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown))
395 pT->TextNotFound(m_sFindNext);
396 else
397 pT->AdjustDialogPosition(m_pFindReplaceDialog->operator HWND());
400 void OnReplaceAll(LPCTSTR lpszFind, LPCTSTR lpszReplace, BOOL bMatchCase, BOOL bWholeWord)
402 T* pT = static_cast<T*>(this);
404 m_sFindNext = lpszFind;
405 m_sReplaceWith = lpszReplace;
406 m_bMatchCase = bMatchCase;
407 m_bWholeWord = bWholeWord;
408 m_bFindDown = TRUE;
410 // no selection or different than what looking for
411 if(!pT->SameAsSelected(m_sFindNext, m_bMatchCase, m_bWholeWord))
413 if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown))
415 pT->TextNotFound(m_sFindNext);
416 return;
420 pT->OnReplaceAllCoreBegin();
422 int replaceCount=0;
425 ++replaceCount;
426 pT->ReplaceSel(m_sReplaceWith);
427 } while(pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown));
429 pT->OnReplaceAllCoreEnd(replaceCount);
432 void OnReplaceAllCoreBegin()
434 T* pT = static_cast<T*>(this);
436 m_hOldCursor = ::SetCursor(::LoadCursor(NULL, IDC_WAIT));
438 pT->HideSelection(TRUE, FALSE);
442 void OnReplaceAllCoreEnd(int replaceCount)
444 T* pT = static_cast<T*>(this);
445 pT->HideSelection(FALSE, FALSE);
447 ::SetCursor(m_hOldCursor);
449 _CSTRING_NS::CString message = pT->GetTranslationText(eText_OnReplaceAllMessage);
450 if(message.GetLength() > 0)
452 _CSTRING_NS::CString formattedMessage;
453 formattedMessage.Format(message,
454 replaceCount, m_sFindNext, m_sReplaceWith);
455 if(m_pFindReplaceDialog != NULL)
457 m_pFindReplaceDialog->MessageBox(formattedMessage,
458 pT->GetTranslationText(eText_OnReplaceAllTitle),
459 MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);
461 else
463 pT->MessageBox(formattedMessage,
464 pT->GetTranslationText(eText_OnReplaceAllTitle),
465 MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);
470 void OnTextNotFound(LPCTSTR lpszFind)
472 T* pT = static_cast<T*>(this);
473 _CSTRING_NS::CString message = pT->GetTranslationText(eText_OnTextNotFoundMessage);
474 if(message.GetLength() > 0)
476 _CSTRING_NS::CString formattedMessage;
477 formattedMessage.Format(message, lpszFind);
478 if(m_pFindReplaceDialog != NULL)
480 m_pFindReplaceDialog->MessageBox(formattedMessage,
481 pT->GetTranslationText(eText_OnTextNotFoundTitle),
482 MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);
484 else
486 pT->MessageBox(formattedMessage,
487 pT->GetTranslationText(eText_OnTextNotFoundTitle),
488 MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);
491 else
493 ::MessageBeep(MB_ICONHAND);
497 void OnTerminatingFindReplaceDialog(TFindReplaceDialog*& /*findReplaceDialog*/)
503 ///////////////////////////////////////////////////////////////////////////////
504 // CEditFindReplaceImpl - Mixin class for implementing Find/Replace for CEdit
505 // based window classes.
507 // Chain to CEditFindReplaceImpl message map. Your class must also derive from CEdit.
508 // Example:
509 // class CMyEdit : public CWindowImpl<CMyEdit, CEdit>,
510 // public CEditFindReplaceImpl<CMyEdit>
511 // {
512 // public:
513 // BEGIN_MSG_MAP(CMyEdit)
514 // // your handlers...
515 // CHAIN_MSG_MAP_ALT(CEditFindReplaceImpl<CMyEdit>, 1)
516 // END_MSG_MAP()
517 // // other stuff...
518 // };
520 template <class T, class TFindReplaceDialog = CFindReplaceDialog>
521 class CEditFindReplaceImpl : public CEditFindReplaceImplBase<T, TFindReplaceDialog>
523 protected:
524 typedef CEditFindReplaceImpl<T, TFindReplaceDialog> thisClass;
525 typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> baseClass;
527 // Data members
528 LPTSTR m_pShadowBuffer; // Special shadow buffer only used in some cases.
529 UINT m_nShadowSize;
530 int m_bShadowBufferNeeded; // TRUE, FALSE, < 0 => Need to check
532 public:
533 // Constructors
534 CEditFindReplaceImpl() :
535 m_pShadowBuffer(NULL),
536 m_nShadowSize(0),
537 m_bShadowBufferNeeded(-1)
541 virtual ~CEditFindReplaceImpl()
543 if(m_pShadowBuffer != NULL)
545 delete [] m_pShadowBuffer;
546 m_pShadowBuffer = NULL;
550 // Message Handlers
551 BEGIN_MSG_MAP(thisClass)
552 ALT_MSG_MAP(1)
553 CHAIN_MSG_MAP_ALT(baseClass, 1)
554 END_MSG_MAP()
556 // Operations
557 // Supported only for RichEdit, so this does nothing for Edit
558 void HideSelection(BOOL /*bHide*/ = TRUE, BOOL /*bChangeStyle*/ = FALSE)
562 // Operations (overrideable)
563 BOOL FindTextSimple(LPCTSTR lpszFind, BOOL bMatchCase, BOOL bWholeWord, BOOL bFindDown = TRUE)
565 T* pT = static_cast<T*>(this);
567 ATLASSERT(lpszFind != NULL);
568 ATLASSERT(*lpszFind != _T('\0'));
570 UINT nLen = pT->GetBufferLength();
571 int nStartChar = 0, nEndChar = 0;
572 pT->GetSel(nStartChar, nEndChar);
573 UINT nStart = nStartChar;
574 int iDir = bFindDown ? +1 : -1;
576 // can't find a match before the first character
577 if(nStart == 0 && iDir < 0)
578 return FALSE;
580 LPCTSTR lpszText = pT->LockBuffer();
582 bool isDBCS = false;
583 #ifdef _MBCS
584 CPINFO info = { 0 };
585 ::GetCPInfo(::GetOEMCP(), &info);
586 isDBCS = (info.MaxCharSize > 1);
587 #endif
589 if(iDir < 0)
591 // always go back one for search backwards
592 nStart -= int((lpszText + nStart) - ::CharPrev(lpszText, lpszText + nStart));
594 else if(nStartChar != nEndChar && pT->SameAsSelected(lpszFind, bMatchCase, bWholeWord))
596 // easy to go backward/forward with SBCS
597 #ifndef _UNICODE
598 if(::IsDBCSLeadByte(lpszText[nStart]))
599 nStart++;
600 #endif
601 nStart += iDir;
604 // handle search with nStart past end of buffer
605 UINT nLenFind = ::lstrlen(lpszFind);
606 if(nStart + nLenFind - 1 >= nLen)
608 if(iDir < 0 && nLen >= nLenFind)
610 if(isDBCS)
612 // walk back to previous character n times
613 nStart = nLen;
614 int n = nLenFind;
615 while(n--)
617 nStart -= int((lpszText + nStart) - ::CharPrev(lpszText, lpszText + nStart));
620 else
622 // single-byte character set is easy and fast
623 nStart = nLen - nLenFind;
625 ATLASSERT(nStart + nLenFind - 1 <= nLen);
627 else
629 pT->UnlockBuffer();
630 return FALSE;
634 // start the search at nStart
635 LPCTSTR lpsz = lpszText + nStart;
636 typedef int (WINAPI* CompareProc)(LPCTSTR str1, LPCTSTR str2);
637 CompareProc pfnCompare = bMatchCase ? lstrcmp : lstrcmpi;
639 if(isDBCS)
641 // double-byte string search
642 LPCTSTR lpszStop = NULL;
643 if(iDir > 0)
645 // start at current and find _first_ occurrance
646 lpszStop = lpszText + nLen - nLenFind + 1;
648 else
650 // start at top and find _last_ occurrance
651 lpszStop = lpsz;
652 lpsz = lpszText;
655 LPCTSTR lpszFound = NULL;
656 while(lpsz <= lpszStop)
658 #ifndef _UNICODE
659 if(!bMatchCase || (*lpsz == *lpszFind && (!::IsDBCSLeadByte(*lpsz) || lpsz[1] == lpszFind[1])))
660 #else
661 if(!bMatchCase || (*lpsz == *lpszFind && lpsz[1] == lpszFind[1]))
662 #endif
664 LPTSTR lpch = (LPTSTR)(lpsz + nLenFind);
665 TCHAR chSave = *lpch;
666 *lpch = _T('\0');
667 int nResult = (*pfnCompare)(lpsz, lpszFind);
668 *lpch = chSave;
669 if(nResult == 0)
671 lpszFound = lpsz;
672 if(iDir > 0)
673 break;
676 lpsz = ::CharNext(lpsz);
678 pT->UnlockBuffer();
680 if(lpszFound != NULL)
682 int n = (int)(lpszFound - lpszText);
683 pT->SetSel(n, n + nLenFind);
684 return TRUE;
687 else
689 // single-byte string search
690 UINT nCompare;
691 if(iDir < 0)
692 nCompare = (UINT)(lpsz - lpszText) + 1;
693 else
694 nCompare = nLen - (UINT)(lpsz - lpszText) - nLenFind + 1;
696 while(nCompare > 0)
698 ATLASSERT(lpsz >= lpszText);
699 ATLASSERT(lpsz + nLenFind - 1 <= lpszText + nLen - 1);
701 LPSTR lpch = (LPSTR)(lpsz + nLenFind);
702 char chSave = *lpch;
703 *lpch = '\0';
704 int nResult = (*pfnCompare)(lpsz, lpszFind);
705 *lpch = chSave;
706 if(nResult == 0)
708 pT->UnlockBuffer();
709 int n = (int)(lpsz - lpszText);
710 pT->SetSel(n, n + nLenFind);
711 return TRUE;
714 // restore character at end of search
715 *lpch = chSave;
717 // move on to next substring
718 nCompare--;
719 lpsz += iDir;
721 pT->UnlockBuffer();
724 return FALSE;
727 LPCTSTR LockBuffer() const
729 const T* pT = static_cast<const T*>(this);
731 ATLASSERT(pT->m_hWnd != NULL);
733 BOOL useShadowBuffer = pT->UseShadowBuffer();
734 if(useShadowBuffer)
736 if(m_pShadowBuffer == NULL || pT->GetModify())
738 ATLASSERT(m_pShadowBuffer != NULL || m_nShadowSize == 0);
739 UINT nSize = pT->GetWindowTextLength() + 1;
740 if(nSize > m_nShadowSize)
742 // need more room for shadow buffer
743 T* pThisNoConst = const_cast<T*>(pT);
744 delete[] m_pShadowBuffer;
745 pThisNoConst->m_pShadowBuffer = NULL;
746 pThisNoConst->m_nShadowSize = 0;
747 pThisNoConst->m_pShadowBuffer = new TCHAR[nSize];
748 pThisNoConst->m_nShadowSize = nSize;
751 // update the shadow buffer with GetWindowText
752 ATLASSERT(m_nShadowSize >= nSize);
753 ATLASSERT(m_pShadowBuffer != NULL);
754 pT->GetWindowText(m_pShadowBuffer, nSize);
757 return m_pShadowBuffer;
760 HLOCAL hLocal = pT->GetHandle();
761 ATLASSERT(hLocal != NULL);
762 LPCTSTR lpszText = (LPCTSTR)::LocalLock(hLocal);
763 ATLASSERT(lpszText != NULL);
765 return lpszText;
768 void UnlockBuffer() const
770 const T* pT = static_cast<const T*>(this);
772 ATLASSERT(pT->m_hWnd != NULL);
774 BOOL useShadowBuffer = pT->UseShadowBuffer();
775 if(!useShadowBuffer)
777 HLOCAL hLocal = pT->GetHandle();
778 ATLASSERT(hLocal != NULL);
779 ::LocalUnlock(hLocal);
783 UINT GetBufferLength() const
785 const T* pT = static_cast<const T*>(this);
787 ATLASSERT(pT->m_hWnd != NULL);
788 UINT nLen = 0;
789 LPCTSTR lpszText = pT->LockBuffer();
790 if(lpszText != NULL)
791 nLen = ::lstrlen(lpszText);
792 pT->UnlockBuffer();
794 return nLen;
797 LONG EndOfLine(LPCTSTR lpszText, UINT nLen, UINT nIndex) const
799 LPCTSTR lpsz = lpszText + nIndex;
800 LPCTSTR lpszStop = lpszText + nLen;
801 while(lpsz < lpszStop && *lpsz != _T('\r'))
802 ++lpsz;
803 return LONG(lpsz - lpszText);
806 LONG GetSelText(_CSTRING_NS::CString& strText) const
808 const T* pT = static_cast<const T*>(this);
810 int nStartChar = 0, nEndChar = 0;
811 pT->GetSel(nStartChar, nEndChar);
812 ATLASSERT((UINT)nEndChar <= pT->GetBufferLength());
813 LPCTSTR lpszText = pT->LockBuffer();
814 LONG nLen = pT->EndOfLine(lpszText, nEndChar, nStartChar) - nStartChar;
815 SecureHelper::memcpy_x(strText.GetBuffer(nLen), nLen * sizeof(TCHAR), lpszText + nStartChar, nLen * sizeof(TCHAR));
816 strText.ReleaseBuffer(nLen);
817 pT->UnlockBuffer();
819 return nLen;
822 BOOL UseShadowBuffer(void) const
824 const T* pT = static_cast<const T*>(this);
826 if(pT->m_bShadowBufferNeeded < 0)
828 T* pThisNoConst = const_cast<T*>(pT);
830 OSVERSIONINFO ovi = { 0 };
831 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
832 ::GetVersionEx(&ovi);
834 bool bWin9x = (ovi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
835 if(bWin9x)
837 // Windows 95, 98, ME
838 // Under Win9x, it is necessary to maintain a shadow buffer.
839 // It is only updated when the control contents have been changed.
840 pThisNoConst->m_bShadowBufferNeeded = TRUE;
842 else
844 // Windows NT, 2000, XP, etc.
845 pThisNoConst->m_bShadowBufferNeeded = FALSE;
847 #ifndef _UNICODE
848 // On Windows XP (or later), if common controls version 6 is in use
849 // (such as via theming), then EM_GETHANDLE will always return a UNICODE string.
850 // If theming is enabled and Common Controls version 6 is in use,
851 // you're really not suppose to superclass or subclass common controls
852 // with an ANSI windows procedure (so its best to only theme if you use UNICODE).
853 // Using a shadow buffer uses GetWindowText instead, so it solves
854 // this problem for us (although it makes it a little less efficient).
856 if((ovi.dwMajorVersion == 5 && ovi.dwMinorVersion >= 1) || (ovi.dwMajorVersion > 5))
858 // We use DLLVERSIONINFO_private so we don't have to depend on shlwapi.h
859 typedef struct _DLLVERSIONINFO_private
861 DWORD cbSize;
862 DWORD dwMajorVersion;
863 DWORD dwMinorVersion;
864 DWORD dwBuildNumber;
865 DWORD dwPlatformID;
866 } DLLVERSIONINFO_private;
868 HMODULE hModule = ::LoadLibrary("comctl32.dll");
869 if(hModule != NULL)
871 typedef HRESULT (CALLBACK *LPFN_DllGetVersion)(DLLVERSIONINFO_private *);
872 LPFN_DllGetVersion fnDllGetVersion = (LPFN_DllGetVersion)::GetProcAddress(hModule, "DllGetVersion");
873 if(fnDllGetVersion != NULL)
875 DLLVERSIONINFO_private version = { 0 };
876 version.cbSize = sizeof(DLLVERSIONINFO_private);
877 if(SUCCEEDED(fnDllGetVersion(&version)))
879 if(version.dwMajorVersion >= 6)
881 pThisNoConst->m_bShadowBufferNeeded = TRUE;
883 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"));
884 ATLTRACE2(atlTraceUI, 0, _T("If you use common controls version 6 or later, you should only do so for UNICODE builds.\r\n"));
889 ::FreeLibrary(hModule);
890 hModule = NULL;
893 #endif // !_UNICODE
897 return (pT->m_bShadowBufferNeeded == TRUE);
902 ///////////////////////////////////////////////////////////////////////////////
903 // CRichEditFindReplaceImpl - Mixin class for implementing Find/Replace for CRichEditCtrl
904 // based window classes.
906 // Chain to CRichEditFindReplaceImpl message map. Your class must also derive from CRichEditCtrl.
907 // Example:
908 // class CMyRichEdit : public CWindowImpl<CMyRichEdit, CRichEditCtrl>,
909 // public CRichEditFindReplaceImpl<CMyRichEdit>
910 // {
911 // public:
912 // BEGIN_MSG_MAP(CMyRichEdit)
913 // // your handlers...
914 // CHAIN_MSG_MAP_ALT(CRichEditFindReplaceImpl<CMyRichEdit>, 1)
915 // END_MSG_MAP()
916 // // other stuff...
917 // };
919 template <class T, class TFindReplaceDialog = CFindReplaceDialog>
920 class CRichEditFindReplaceImpl : public CEditFindReplaceImplBase<T, TFindReplaceDialog>
922 protected:
923 typedef CRichEditFindReplaceImpl<T, TFindReplaceDialog> thisClass;
924 typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> baseClass;
926 public:
927 BEGIN_MSG_MAP(thisClass)
928 ALT_MSG_MAP(1)
929 CHAIN_MSG_MAP_ALT(baseClass, 1)
930 END_MSG_MAP()
932 // Operations (overrideable)
933 BOOL FindTextSimple(LPCTSTR lpszFind, BOOL bMatchCase, BOOL bWholeWord, BOOL bFindDown = TRUE)
935 T* pT = static_cast<T*>(this);
937 ATLASSERT(lpszFind != NULL);
938 FINDTEXTEX ft = { 0 };
940 pT->GetSel(ft.chrg);
941 if(m_bFirstSearch)
943 if(bFindDown)
944 m_nInitialSearchPos = ft.chrg.cpMin;
945 else
946 m_nInitialSearchPos = ft.chrg.cpMax;
947 m_bFirstSearch = FALSE;
950 #if (_RICHEDIT_VER >= 0x0200)
951 ft.lpstrText = (LPTSTR)lpszFind;
952 #else // !(_RICHEDIT_VER >= 0x0200)
953 USES_CONVERSION;
954 ft.lpstrText = T2A((LPTSTR)lpszFind);
955 #endif // !(_RICHEDIT_VER >= 0x0200)
957 if(ft.chrg.cpMin != ft.chrg.cpMax) // i.e. there is a selection
959 if(bFindDown)
961 ft.chrg.cpMin++;
963 else
965 // won't wraparound backwards
966 ft.chrg.cpMin = __max(ft.chrg.cpMin, 0);
970 DWORD dwFlags = bMatchCase ? FR_MATCHCASE : 0;
971 dwFlags |= bWholeWord ? FR_WHOLEWORD : 0;
973 ft.chrg.cpMax = pT->GetTextLength() + m_nInitialSearchPos;
975 if(bFindDown)
977 if(m_nInitialSearchPos >= 0)
978 ft.chrg.cpMax = pT->GetTextLength();
980 dwFlags |= FR_DOWN;
981 ATLASSERT(ft.chrg.cpMax >= ft.chrg.cpMin);
983 else
985 if(m_nInitialSearchPos >= 0)
986 ft.chrg.cpMax = 0;
988 dwFlags &= ~FR_DOWN;
989 ATLASSERT(ft.chrg.cpMax <= ft.chrg.cpMin);
992 BOOL bRet = FALSE;
994 if(pT->FindAndSelect(dwFlags, ft) != -1)
996 bRet = TRUE; // we found the text
998 else if(m_nInitialSearchPos > 0)
1000 // if the original starting point was not the beginning
1001 // of the buffer and we haven't already been here
1002 if(bFindDown)
1004 ft.chrg.cpMin = 0;
1005 ft.chrg.cpMax = m_nInitialSearchPos;
1007 else
1009 ft.chrg.cpMin = pT->GetTextLength();
1010 ft.chrg.cpMax = m_nInitialSearchPos;
1012 m_nInitialSearchPos = m_nInitialSearchPos - pT->GetTextLength();
1014 bRet = (pT->FindAndSelect(dwFlags, ft) != -1) ? TRUE : FALSE;
1017 return bRet;
1020 long FindAndSelect(DWORD dwFlags, FINDTEXTEX& ft)
1022 T* pT = static_cast<T*>(this);
1023 LONG index = pT->FindText(dwFlags, ft);
1024 if(index != -1) // i.e. we found something
1025 pT->SetSel(ft.chrgText);
1027 return index;
1031 }; // namespace WTL
1033 #endif // __ATLFIND_H__