1 // Windows Template Library - WTL version 8.0
2 // Copyright (C) Microsoft Corporation. All rights reserved.
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.
15 #error ATL requires C++ compilation (use a .cpp suffix)
19 #error atlfind.h is not supported on Windows CE
22 #ifndef __ATLCTRLS_H__
23 #error atlfind.h requires atlctrls.h to be included first
27 #error atlfind.h requires atldlgs.h to be included first
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)
35 ///////////////////////////////////////////////////////////////////////////////
36 // Classes in this file:
38 // CEditFindReplaceImplBase<T, TFindReplaceDialog>
39 // CEditFindReplaceImpl<T, TFindReplaceDialog>
40 // CRichEditFindReplaceImpl<T, TFindReplaceDialog>
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
55 typedef CEditFindReplaceImplBase
<T
, TFindReplaceDialog
> thisClass
;
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
;
65 enum TranslationTextItem
67 eText_OnReplaceAllMessage
= 0,
68 eText_OnReplaceAllTitle
= 1,
69 eText_OnTextNotFoundMessage
= 2,
70 eText_OnTextNotFoundTitle
= 3
75 CEditFindReplaceImplBase() :
76 m_pFindReplaceDialog(NULL
),
82 m_nInitialSearchPos(0),
88 BEGIN_MSG_MAP(thisClass
)
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
)
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
);
109 LRESULT
OnFindReplaceCmd(UINT
/*uMsg*/, WPARAM
/*wParam*/, LPARAM lParam
, BOOL
& /*bHandled*/)
111 T
* pT
= static_cast<T
*>(this);
113 TFindReplaceDialog
* pDialog
= TFindReplaceDialog::GetNotifier(lParam
);
117 ::MessageBeep(MB_ICONERROR
);
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
;
153 LRESULT
OnEditFind(WORD
/*wNotifyCode*/, WORD
/*wID*/, HWND
/*hWndCtl*/, BOOL
& /*bHandled*/)
155 T
* pT
= static_cast<T
*>(this);
156 pT
->FindReplace(TRUE
);
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
);
177 if(!pT
->FindTextSimple(m_sFindNext
, m_bMatchCase
, m_bWholeWord
, m_bFindDown
))
178 pT
->TextNotFound(m_sFindNext
);
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
);
195 // Don't allow replace when the edit control is read only
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
);
218 HWND hWndFindReplace
= findReplaceDialog
->Create(bFindOnly
,
219 lpszFindWhat
, lpszReplaceWith
, dwFlags
, hWndParent
);
220 if(hWndFindReplace
== NULL
)
222 delete findReplaceDialog
;
223 findReplaceDialog
= NULL
;
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
);
247 ::GetWindowRect(hWndDialog
, &rect
);
248 if(rect
.PtInRect(point
))
250 if(point
.y
> rect
.Height())
252 rect
.OffsetRect(0, point
.y
- rect
.bottom
- 20);
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
272 dwFlags
|= FR_MATCHCASE
;
274 dwFlags
|= FR_WHOLEWORD
;
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
);
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
))
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
;
348 case eText_OnReplaceAllMessage
:
349 text
= _T("Replaced %d occurances of \"%s\" with \"%s\"");
351 case eText_OnReplaceAllTitle
:
352 text
= _T("Replace All");
354 case eText_OnTextNotFoundMessage
:
355 text
= _T("Unable to find the text \"%s\"");
357 case eText_OnTextNotFoundTitle
:
358 text
= _T("Text not found");
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
);
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
);
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
;
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
);
420 pT
->OnReplaceAllCoreBegin();
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
);
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
);
486 pT
->MessageBox(formattedMessage
,
487 pT
->GetTranslationText(eText_OnTextNotFoundTitle
),
488 MB_OK
| MB_ICONINFORMATION
| MB_APPLMODAL
);
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.
509 // class CMyEdit : public CWindowImpl<CMyEdit, CEdit>,
510 // public CEditFindReplaceImpl<CMyEdit>
513 // BEGIN_MSG_MAP(CMyEdit)
514 // // your handlers...
515 // CHAIN_MSG_MAP_ALT(CEditFindReplaceImpl<CMyEdit>, 1)
520 template <class T
, class TFindReplaceDialog
= CFindReplaceDialog
>
521 class CEditFindReplaceImpl
: public CEditFindReplaceImplBase
<T
, TFindReplaceDialog
>
524 typedef CEditFindReplaceImpl
<T
, TFindReplaceDialog
> thisClass
;
525 typedef CEditFindReplaceImplBase
<T
, TFindReplaceDialog
> baseClass
;
528 LPTSTR m_pShadowBuffer
; // Special shadow buffer only used in some cases.
530 int m_bShadowBufferNeeded
; // TRUE, FALSE, < 0 => Need to check
534 CEditFindReplaceImpl() :
535 m_pShadowBuffer(NULL
),
537 m_bShadowBufferNeeded(-1)
541 virtual ~CEditFindReplaceImpl()
543 if(m_pShadowBuffer
!= NULL
)
545 delete [] m_pShadowBuffer
;
546 m_pShadowBuffer
= NULL
;
551 BEGIN_MSG_MAP(thisClass
)
553 CHAIN_MSG_MAP_ALT(baseClass
, 1)
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)
580 LPCTSTR lpszText
= pT
->LockBuffer();
585 ::GetCPInfo(::GetOEMCP(), &info
);
586 isDBCS
= (info
.MaxCharSize
> 1);
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
598 if(::IsDBCSLeadByte(lpszText
[nStart
]))
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
)
612 // walk back to previous character n times
617 nStart
-= int((lpszText
+ nStart
) - ::CharPrev(lpszText
, lpszText
+ nStart
));
622 // single-byte character set is easy and fast
623 nStart
= nLen
- nLenFind
;
625 ATLASSERT(nStart
+ nLenFind
- 1 <= nLen
);
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
;
641 // double-byte string search
642 LPCTSTR lpszStop
= NULL
;
645 // start at current and find _first_ occurrance
646 lpszStop
= lpszText
+ nLen
- nLenFind
+ 1;
650 // start at top and find _last_ occurrance
655 LPCTSTR lpszFound
= NULL
;
656 while(lpsz
<= lpszStop
)
659 if(!bMatchCase
|| (*lpsz
== *lpszFind
&& (!::IsDBCSLeadByte(*lpsz
) || lpsz
[1] == lpszFind
[1])))
661 if(!bMatchCase
|| (*lpsz
== *lpszFind
&& lpsz
[1] == lpszFind
[1]))
664 LPTSTR lpch
= (LPTSTR
)(lpsz
+ nLenFind
);
665 TCHAR chSave
= *lpch
;
667 int nResult
= (*pfnCompare
)(lpsz
, lpszFind
);
676 lpsz
= ::CharNext(lpsz
);
680 if(lpszFound
!= NULL
)
682 int n
= (int)(lpszFound
- lpszText
);
683 pT
->SetSel(n
, n
+ nLenFind
);
689 // single-byte string search
692 nCompare
= (UINT
)(lpsz
- lpszText
) + 1;
694 nCompare
= nLen
- (UINT
)(lpsz
- lpszText
) - nLenFind
+ 1;
698 ATLASSERT(lpsz
>= lpszText
);
699 ATLASSERT(lpsz
+ nLenFind
- 1 <= lpszText
+ nLen
- 1);
701 LPSTR lpch
= (LPSTR
)(lpsz
+ nLenFind
);
704 int nResult
= (*pfnCompare
)(lpsz
, lpszFind
);
709 int n
= (int)(lpsz
- lpszText
);
710 pT
->SetSel(n
, n
+ nLenFind
);
714 // restore character at end of search
717 // move on to next substring
727 LPCTSTR
LockBuffer() const
729 const T
* pT
= static_cast<const T
*>(this);
731 ATLASSERT(pT
->m_hWnd
!= NULL
);
733 BOOL useShadowBuffer
= pT
->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
);
768 void UnlockBuffer() const
770 const T
* pT
= static_cast<const T
*>(this);
772 ATLASSERT(pT
->m_hWnd
!= NULL
);
774 BOOL useShadowBuffer
= pT
->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
);
789 LPCTSTR lpszText
= pT
->LockBuffer();
791 nLen
= ::lstrlen(lpszText
);
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'))
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
);
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
);
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
;
844 // Windows NT, 2000, XP, etc.
845 pThisNoConst
->m_bShadowBufferNeeded
= FALSE
;
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
862 DWORD dwMajorVersion
;
863 DWORD dwMinorVersion
;
866 } DLLVERSIONINFO_private
;
868 HMODULE hModule
= ::LoadLibrary("comctl32.dll");
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
);
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.
908 // class CMyRichEdit : public CWindowImpl<CMyRichEdit, CRichEditCtrl>,
909 // public CRichEditFindReplaceImpl<CMyRichEdit>
912 // BEGIN_MSG_MAP(CMyRichEdit)
913 // // your handlers...
914 // CHAIN_MSG_MAP_ALT(CRichEditFindReplaceImpl<CMyRichEdit>, 1)
919 template <class T
, class TFindReplaceDialog
= CFindReplaceDialog
>
920 class CRichEditFindReplaceImpl
: public CEditFindReplaceImplBase
<T
, TFindReplaceDialog
>
923 typedef CRichEditFindReplaceImpl
<T
, TFindReplaceDialog
> thisClass
;
924 typedef CEditFindReplaceImplBase
<T
, TFindReplaceDialog
> baseClass
;
927 BEGIN_MSG_MAP(thisClass
)
929 CHAIN_MSG_MAP_ALT(baseClass
, 1)
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 };
944 m_nInitialSearchPos
= ft
.chrg
.cpMin
;
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)
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
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
;
977 if(m_nInitialSearchPos
>= 0)
978 ft
.chrg
.cpMax
= pT
->GetTextLength();
981 ATLASSERT(ft
.chrg
.cpMax
>= ft
.chrg
.cpMin
);
985 if(m_nInitialSearchPos
>= 0)
989 ATLASSERT(ft
.chrg
.cpMax
<= ft
.chrg
.cpMin
);
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
1005 ft
.chrg
.cpMax
= m_nInitialSearchPos
;
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
;
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
);
1033 #endif // __ATLFIND_H__