OLD | NEW |
| (Empty) |
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. | |
8 | |
9 #ifndef __ATLFIND_H__ | |
10 #define __ATLFIND_H__ | |
11 | |
12 #pragma once | |
13 | |
14 #ifndef __cplusplus | |
15 #error ATL requires C++ compilation (use a .cpp suffix) | |
16 #endif | |
17 | |
18 #ifdef _WIN32_WCE | |
19 #error atlfind.h is not supported on Windows CE | |
20 #endif | |
21 | |
22 #ifndef __ATLCTRLS_H__ | |
23 #error atlfind.h requires atlctrls.h to be included first | |
24 #endif | |
25 | |
26 #ifndef __ATLDLGS_H__ | |
27 #error atlfind.h requires atldlgs.h to be included first | |
28 #endif | |
29 | |
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 a
tlmisc.h with _WTL_USE_CSTRING) | |
32 #endif | |
33 | |
34 | |
35 /////////////////////////////////////////////////////////////////////////////// | |
36 // Classes in this file: | |
37 // | |
38 // CEditFindReplaceImplBase<T, TFindReplaceDialog> | |
39 // CEditFindReplaceImpl<T, TFindReplaceDialog> | |
40 // CRichEditFindReplaceImpl<T, TFindReplaceDialog> | |
41 | |
42 | |
43 namespace WTL | |
44 { | |
45 | |
46 /////////////////////////////////////////////////////////////////////////////// | |
47 // CEditFindReplaceImplBase - Base class for mixin classes that | |
48 // help implement Find/Replace for CEdit or CRichEditCtrl based window classes. | |
49 | |
50 template <class T, class TFindReplaceDialog = CFindReplaceDialog> | |
51 class CEditFindReplaceImplBase | |
52 { | |
53 protected: | |
54 // Typedefs | |
55 typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> thisClass; | |
56 | |
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_bFindDow
n; | |
61 LONG m_nInitialSearchPos; | |
62 HCURSOR m_hOldCursor; | |
63 | |
64 // Enumerations | |
65 enum TranslationTextItem | |
66 { | |
67 eText_OnReplaceAllMessage = 0, | |
68 eText_OnReplaceAllTitle = 1, | |
69 eText_OnTextNotFoundMessage = 2, | |
70 eText_OnTextNotFoundTitle = 3 | |
71 }; | |
72 | |
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) | |
84 { | |
85 } | |
86 | |
87 // Message Handlers | |
88 BEGIN_MSG_MAP(thisClass) | |
89 ALT_MSG_MAP(1) | |
90 MESSAGE_HANDLER(WM_DESTROY, OnDestroy) | |
91 MESSAGE_HANDLER(TFindReplaceDialog::GetFindReplaceMsg(), OnFindR
eplaceCmd) | |
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() | |
96 | |
97 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, B
OOL& bHandled) | |
98 { | |
99 if(m_pFindReplaceDialog != NULL) | |
100 { | |
101 m_pFindReplaceDialog->SendMessage(WM_CLOSE); | |
102 ATLASSERT(m_pFindReplaceDialog == NULL); | |
103 } | |
104 | |
105 bHandled = FALSE; | |
106 return 0; | |
107 } | |
108 | |
109 LRESULT OnFindReplaceCmd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam
, BOOL& /*bHandled*/) | |
110 { | |
111 T* pT = static_cast<T*>(this); | |
112 | |
113 TFindReplaceDialog* pDialog = TFindReplaceDialog::GetNotifier(lP
aram); | |
114 if(pDialog == NULL) | |
115 { | |
116 ATLASSERT(FALSE); | |
117 ::MessageBeep(MB_ICONERROR); | |
118 return 1; | |
119 } | |
120 ATLASSERT(pDialog == m_pFindReplaceDialog); | |
121 | |
122 LPFINDREPLACE findReplace = (LPFINDREPLACE)lParam; | |
123 if((m_pFindReplaceDialog != NULL) && (findReplace != NULL)) | |
124 { | |
125 if(pDialog->FindNext()) | |
126 { | |
127 pT->OnFindNext(pDialog->GetFindString(), pDialog
->SearchDown(), | |
128 pDialog->MatchCase(), pDialog->MatchWhol
eWord()); | |
129 } | |
130 else if(pDialog->ReplaceCurrent()) | |
131 { | |
132 pT->OnReplaceSel(pDialog->GetFindString(), | |
133 pDialog->SearchDown(), pDialog->MatchCas
e(), pDialog->MatchWholeWord(), | |
134 pDialog->GetReplaceString()); | |
135 } | |
136 else if(pDialog->ReplaceAll()) | |
137 { | |
138 pT->OnReplaceAll(pDialog->GetFindString(), pDial
og->GetReplaceString(), | |
139 pDialog->MatchCase(), pDialog->MatchWhol
eWord()); | |
140 } | |
141 else if(pDialog->IsTerminating()) | |
142 { | |
143 // Dialog is going away (but hasn't gone away ye
t) | |
144 // OnFinalMessage will "delete this" | |
145 pT->OnTerminatingFindReplaceDialog(m_pFindReplac
eDialog); | |
146 m_pFindReplaceDialog = NULL; | |
147 } | |
148 } | |
149 | |
150 return 0; | |
151 } | |
152 | |
153 LRESULT OnEditFind(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/,
BOOL& /*bHandled*/) | |
154 { | |
155 T* pT = static_cast<T*>(this); | |
156 pT->FindReplace(TRUE); | |
157 | |
158 return 0; | |
159 } | |
160 | |
161 LRESULT OnEditRepeat(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*
/, BOOL& /*bHandled*/) | |
162 { | |
163 T* pT = static_cast<T*>(this); | |
164 | |
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); | |
170 | |
171 if(m_sFindNext.IsEmpty()) | |
172 { | |
173 pT->FindReplace(TRUE); | |
174 } | |
175 else | |
176 { | |
177 if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWho
leWord, m_bFindDown)) | |
178 pT->TextNotFound(m_sFindNext); | |
179 } | |
180 | |
181 return 0; | |
182 } | |
183 | |
184 LRESULT OnEditReplace(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl
*/, BOOL& bHandled) | |
185 { | |
186 T* pT = static_cast<T*>(this); | |
187 | |
188 DWORD style = pT->GetStyle(); | |
189 if((style & ES_READONLY) != ES_READONLY) | |
190 { | |
191 pT->FindReplace(FALSE); | |
192 } | |
193 else | |
194 { | |
195 // Don't allow replace when the edit control is read onl
y | |
196 bHandled = FALSE; | |
197 } | |
198 | |
199 return 0; | |
200 } | |
201 | |
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) | |
208 { | |
209 // You can override all of this in a derived class | |
210 | |
211 TFindReplaceDialog* findReplaceDialog = new TFindReplaceDialog()
; | |
212 if(findReplaceDialog == NULL) | |
213 { | |
214 ::MessageBeep(MB_ICONHAND); | |
215 } | |
216 else | |
217 { | |
218 HWND hWndFindReplace = findReplaceDialog->Create(bFindOn
ly, | |
219 lpszFindWhat, lpszReplaceWith, dwFlags, hWndPare
nt); | |
220 if(hWndFindReplace == NULL) | |
221 { | |
222 delete findReplaceDialog; | |
223 findReplaceDialog = NULL; | |
224 } | |
225 else | |
226 { | |
227 findReplaceDialog->SetActiveWindow(); | |
228 findReplaceDialog->ShowWindow(SW_SHOW); | |
229 } | |
230 } | |
231 | |
232 return findReplaceDialog; | |
233 } | |
234 | |
235 void AdjustDialogPosition(HWND hWndDialog) | |
236 { | |
237 ATLASSERT((hWndDialog != NULL) && ::IsWindow(hWndDialog)); | |
238 | |
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 LON
G&) | |
243 ::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARA
M)&nEndChar); | |
244 POINT point = pT->PosFromChar(nStartChar); | |
245 ::ClientToScreen(pT->GetParent(), &point); | |
246 CRect rect; | |
247 ::GetWindowRect(hWndDialog, &rect); | |
248 if(rect.PtInRect(point)) | |
249 { | |
250 if(point.y > rect.Height()) | |
251 { | |
252 rect.OffsetRect(0, point.y - rect.bottom - 20); | |
253 } | |
254 else | |
255 { | |
256 int nVertExt = GetSystemMetrics(SM_CYSCREEN); | |
257 if(point.y + rect.Height() < nVertExt) | |
258 rect.OffsetRect(0, 40 + point.y - rect.t
op); | |
259 } | |
260 | |
261 ::MoveWindow(hWndDialog, rect.left, rect.top, rect.Width
(), rect.Height(), TRUE); | |
262 } | |
263 } | |
264 | |
265 DWORD GetFindReplaceDialogFlags(void) const | |
266 { | |
267 DWORD dwFlags = 0; | |
268 | |
269 if(m_bFindDown) | |
270 dwFlags |= FR_DOWN; | |
271 if(m_bMatchCase) | |
272 dwFlags |= FR_MATCHCASE; | |
273 if(m_bWholeWord) | |
274 dwFlags |= FR_WHOLEWORD; | |
275 | |
276 return dwFlags; | |
277 } | |
278 | |
279 void FindReplace(BOOL bFindOnly) | |
280 { | |
281 T* pT = static_cast<T*>(this); | |
282 m_bFirstSearch = TRUE; | |
283 if(m_pFindReplaceDialog != NULL) | |
284 { | |
285 if(m_bFindOnly == bFindOnly) | |
286 { | |
287 m_pFindReplaceDialog->SetActiveWindow(); | |
288 m_pFindReplaceDialog->ShowWindow(SW_SHOW); | |
289 return; | |
290 } | |
291 else | |
292 { | |
293 m_pFindReplaceDialog->SendMessage(WM_CLOSE); | |
294 ATLASSERT(m_pFindReplaceDialog == NULL); | |
295 } | |
296 } | |
297 | |
298 ATLASSERT(m_pFindReplaceDialog == NULL); | |
299 | |
300 _CSTRING_NS::CString findNext; | |
301 pT->GetSelText(findNext); | |
302 // if selection is empty or spans multiple lines use old find te
xt | |
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(); | |
307 | |
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; | |
313 } | |
314 | |
315 BOOL SameAsSelected(LPCTSTR lpszCompare, BOOL bMatchCase, BOOL /*bWholeW
ord*/) | |
316 { | |
317 T* pT = static_cast<T*>(this); | |
318 | |
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 LON
G&) | |
324 ::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARA
M)&nEndChar); | |
325 if(nLen != (size_t)(nEndChar - nStartChar)) | |
326 return FALSE; | |
327 | |
328 // length is the same, check contents | |
329 _CSTRING_NS::CString selectedText; | |
330 pT->GetSelText(selectedText); | |
331 | |
332 return (bMatchCase && selectedText.Compare(lpszCompare) == 0) || | |
333 (!bMatchCase && selectedText.CompareNoCase(lpszCompare)
== 0); | |
334 } | |
335 | |
336 void TextNotFound(LPCTSTR lpszFind) | |
337 { | |
338 T* pT = static_cast<T*>(this); | |
339 m_bFirstSearch = TRUE; | |
340 pT->OnTextNotFound(lpszFind); | |
341 } | |
342 | |
343 _CSTRING_NS::CString GetTranslationText(enum TranslationTextItem eItem)
const | |
344 { | |
345 _CSTRING_NS::CString text; | |
346 switch(eItem) | |
347 { | |
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; | |
360 } | |
361 | |
362 return text; | |
363 } | |
364 | |
365 // Overrideable Handlers | |
366 void OnFindNext(LPCTSTR lpszFind, BOOL bFindDown, BOOL bMatchCase, BOOL
bWholeWord) | |
367 { | |
368 T* pT = static_cast<T*>(this); | |
369 | |
370 m_sFindNext = lpszFind; | |
371 m_bMatchCase = bMatchCase; | |
372 m_bWholeWord = bWholeWord; | |
373 m_bFindDown = bFindDown; | |
374 | |
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()); | |
379 } | |
380 | |
381 void OnReplaceSel(LPCTSTR lpszFind, BOOL bFindDown, BOOL bMatchCase, BOO
L bWholeWord, LPCTSTR lpszReplace) | |
382 { | |
383 T* pT = static_cast<T*>(this); | |
384 | |
385 m_sFindNext = lpszFind; | |
386 m_sReplaceWith = lpszReplace; | |
387 m_bMatchCase = bMatchCase; | |
388 m_bWholeWord = bWholeWord; | |
389 m_bFindDown = bFindDown; | |
390 | |
391 if(pT->SameAsSelected(m_sFindNext, m_bMatchCase, m_bWholeWord)) | |
392 pT->ReplaceSel(m_sReplaceWith); | |
393 | |
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()); | |
398 } | |
399 | |
400 void OnReplaceAll(LPCTSTR lpszFind, LPCTSTR lpszReplace, BOOL bMatchCase
, BOOL bWholeWord) | |
401 { | |
402 T* pT = static_cast<T*>(this); | |
403 | |
404 m_sFindNext = lpszFind; | |
405 m_sReplaceWith = lpszReplace; | |
406 m_bMatchCase = bMatchCase; | |
407 m_bWholeWord = bWholeWord; | |
408 m_bFindDown = TRUE; | |
409 | |
410 // no selection or different than what looking for | |
411 if(!pT->SameAsSelected(m_sFindNext, m_bMatchCase, m_bWholeWord)) | |
412 { | |
413 if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWho
leWord, m_bFindDown)) | |
414 { | |
415 pT->TextNotFound(m_sFindNext); | |
416 return; | |
417 } | |
418 } | |
419 | |
420 pT->OnReplaceAllCoreBegin(); | |
421 | |
422 int replaceCount=0; | |
423 do | |
424 { | |
425 ++replaceCount; | |
426 pT->ReplaceSel(m_sReplaceWith); | |
427 } while(pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWo
rd, m_bFindDown)); | |
428 | |
429 pT->OnReplaceAllCoreEnd(replaceCount); | |
430 } | |
431 | |
432 void OnReplaceAllCoreBegin() | |
433 { | |
434 T* pT = static_cast<T*>(this); | |
435 | |
436 m_hOldCursor = ::SetCursor(::LoadCursor(NULL, IDC_WAIT)); | |
437 | |
438 pT->HideSelection(TRUE, FALSE); | |
439 | |
440 } | |
441 | |
442 void OnReplaceAllCoreEnd(int replaceCount) | |
443 { | |
444 T* pT = static_cast<T*>(this); | |
445 pT->HideSelection(FALSE, FALSE); | |
446 | |
447 ::SetCursor(m_hOldCursor); | |
448 | |
449 _CSTRING_NS::CString message = pT->GetTranslationText(eText_OnRe
placeAllMessage); | |
450 if(message.GetLength() > 0) | |
451 { | |
452 _CSTRING_NS::CString formattedMessage; | |
453 formattedMessage.Format(message, | |
454 replaceCount, m_sFindNext, m_sReplaceWith); | |
455 if(m_pFindReplaceDialog != NULL) | |
456 { | |
457 m_pFindReplaceDialog->MessageBox(formattedMessag
e, | |
458 pT->GetTranslationText(eText_OnReplaceAl
lTitle), | |
459 MB_OK | MB_ICONINFORMATION | MB_APPLMODA
L); | |
460 } | |
461 else | |
462 { | |
463 pT->MessageBox(formattedMessage, | |
464 pT->GetTranslationText(eText_OnReplaceAl
lTitle), | |
465 MB_OK | MB_ICONINFORMATION | MB_APPLMODA
L); | |
466 } | |
467 } | |
468 } | |
469 | |
470 void OnTextNotFound(LPCTSTR lpszFind) | |
471 { | |
472 T* pT = static_cast<T*>(this); | |
473 _CSTRING_NS::CString message = pT->GetTranslationText(eText_OnTe
xtNotFoundMessage); | |
474 if(message.GetLength() > 0) | |
475 { | |
476 _CSTRING_NS::CString formattedMessage; | |
477 formattedMessage.Format(message, lpszFind); | |
478 if(m_pFindReplaceDialog != NULL) | |
479 { | |
480 m_pFindReplaceDialog->MessageBox(formattedMessag
e, | |
481 pT->GetTranslationText(eText_OnTextNotFo
undTitle), | |
482 MB_OK | MB_ICONINFORMATION | MB_APPLMODA
L); | |
483 } | |
484 else | |
485 { | |
486 pT->MessageBox(formattedMessage, | |
487 pT->GetTranslationText(eText_OnTextNotFo
undTitle), | |
488 MB_OK | MB_ICONINFORMATION | MB_APPLMODA
L); | |
489 } | |
490 } | |
491 else | |
492 { | |
493 ::MessageBeep(MB_ICONHAND); | |
494 } | |
495 } | |
496 | |
497 void OnTerminatingFindReplaceDialog(TFindReplaceDialog*& /*findReplaceDi
alog*/) | |
498 { | |
499 } | |
500 }; | |
501 | |
502 | |
503 /////////////////////////////////////////////////////////////////////////////// | |
504 // CEditFindReplaceImpl - Mixin class for implementing Find/Replace for CEdit | |
505 // based window classes. | |
506 | |
507 // Chain to CEditFindReplaceImpl message map. Your class must also derive from C
Edit. | |
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 // }; | |
519 | |
520 template <class T, class TFindReplaceDialog = CFindReplaceDialog> | |
521 class CEditFindReplaceImpl : public CEditFindReplaceImplBase<T, TFindReplaceDial
og> | |
522 { | |
523 protected: | |
524 typedef CEditFindReplaceImpl<T, TFindReplaceDialog> thisClass; | |
525 typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> baseClass; | |
526 | |
527 // Data members | |
528 LPTSTR m_pShadowBuffer; // Special shadow buffer only used in some c
ases. | |
529 UINT m_nShadowSize; | |
530 int m_bShadowBufferNeeded; // TRUE, FALSE, < 0 => Need to check | |
531 | |
532 public: | |
533 // Constructors | |
534 CEditFindReplaceImpl() : | |
535 m_pShadowBuffer(NULL), | |
536 m_nShadowSize(0), | |
537 m_bShadowBufferNeeded(-1) | |
538 { | |
539 } | |
540 | |
541 virtual ~CEditFindReplaceImpl() | |
542 { | |
543 if(m_pShadowBuffer != NULL) | |
544 { | |
545 delete [] m_pShadowBuffer; | |
546 m_pShadowBuffer = NULL; | |
547 } | |
548 } | |
549 | |
550 // Message Handlers | |
551 BEGIN_MSG_MAP(thisClass) | |
552 ALT_MSG_MAP(1) | |
553 CHAIN_MSG_MAP_ALT(baseClass, 1) | |
554 END_MSG_MAP() | |
555 | |
556 // Operations | |
557 // Supported only for RichEdit, so this does nothing for Edit | |
558 void HideSelection(BOOL /*bHide*/ = TRUE, BOOL /*bChangeStyle*/ = FALSE) | |
559 { | |
560 } | |
561 | |
562 // Operations (overrideable) | |
563 BOOL FindTextSimple(LPCTSTR lpszFind, BOOL bMatchCase, BOOL bWholeWord,
BOOL bFindDown = TRUE) | |
564 { | |
565 T* pT = static_cast<T*>(this); | |
566 | |
567 ATLASSERT(lpszFind != NULL); | |
568 ATLASSERT(*lpszFind != _T('\0')); | |
569 | |
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; | |
575 | |
576 // can't find a match before the first character | |
577 if(nStart == 0 && iDir < 0) | |
578 return FALSE; | |
579 | |
580 LPCTSTR lpszText = pT->LockBuffer(); | |
581 | |
582 bool isDBCS = false; | |
583 #ifdef _MBCS | |
584 CPINFO info = { 0 }; | |
585 ::GetCPInfo(::GetOEMCP(), &info); | |
586 isDBCS = (info.MaxCharSize > 1); | |
587 #endif | |
588 | |
589 if(iDir < 0) | |
590 { | |
591 // always go back one for search backwards | |
592 nStart -= int((lpszText + nStart) - ::CharPrev(lpszText,
lpszText + nStart)); | |
593 } | |
594 else if(nStartChar != nEndChar && pT->SameAsSelected(lpszFind, b
MatchCase, bWholeWord)) | |
595 { | |
596 // easy to go backward/forward with SBCS | |
597 #ifndef _UNICODE | |
598 if(::IsDBCSLeadByte(lpszText[nStart])) | |
599 nStart++; | |
600 #endif | |
601 nStart += iDir; | |
602 } | |
603 | |
604 // handle search with nStart past end of buffer | |
605 UINT nLenFind = ::lstrlen(lpszFind); | |
606 if(nStart + nLenFind - 1 >= nLen) | |
607 { | |
608 if(iDir < 0 && nLen >= nLenFind) | |
609 { | |
610 if(isDBCS) | |
611 { | |
612 // walk back to previous character n tim
es | |
613 nStart = nLen; | |
614 int n = nLenFind; | |
615 while(n--) | |
616 { | |
617 nStart -= int((lpszText + nStart
) - ::CharPrev(lpszText, lpszText + nStart)); | |
618 } | |
619 } | |
620 else | |
621 { | |
622 // single-byte character set is easy and
fast | |
623 nStart = nLen - nLenFind; | |
624 } | |
625 ATLASSERT(nStart + nLenFind - 1 <= nLen); | |
626 } | |
627 else | |
628 { | |
629 pT->UnlockBuffer(); | |
630 return FALSE; | |
631 } | |
632 } | |
633 | |
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; | |
638 | |
639 if(isDBCS) | |
640 { | |
641 // double-byte string search | |
642 LPCTSTR lpszStop = NULL; | |
643 if(iDir > 0) | |
644 { | |
645 // start at current and find _first_ occurrance | |
646 lpszStop = lpszText + nLen - nLenFind + 1; | |
647 } | |
648 else | |
649 { | |
650 // start at top and find _last_ occurrance | |
651 lpszStop = lpsz; | |
652 lpsz = lpszText; | |
653 } | |
654 | |
655 LPCTSTR lpszFound = NULL; | |
656 while(lpsz <= lpszStop) | |
657 { | |
658 #ifndef _UNICODE | |
659 if(!bMatchCase || (*lpsz == *lpszFind && (!::IsD
BCSLeadByte(*lpsz) || lpsz[1] == lpszFind[1]))) | |
660 #else | |
661 if(!bMatchCase || (*lpsz == *lpszFind && lpsz[1]
== lpszFind[1])) | |
662 #endif | |
663 { | |
664 LPTSTR lpch = (LPTSTR)(lpsz + nLenFind); | |
665 TCHAR chSave = *lpch; | |
666 *lpch = _T('\0'); | |
667 int nResult = (*pfnCompare)(lpsz, lpszFi
nd); | |
668 *lpch = chSave; | |
669 if(nResult == 0) | |
670 { | |
671 lpszFound = lpsz; | |
672 if(iDir > 0) | |
673 break; | |
674 } | |
675 } | |
676 lpsz = ::CharNext(lpsz); | |
677 } | |
678 pT->UnlockBuffer(); | |
679 | |
680 if(lpszFound != NULL) | |
681 { | |
682 int n = (int)(lpszFound - lpszText); | |
683 pT->SetSel(n, n + nLenFind); | |
684 return TRUE; | |
685 } | |
686 } | |
687 else | |
688 { | |
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) - nLen
Find + 1; | |
695 | |
696 while(nCompare > 0) | |
697 { | |
698 ATLASSERT(lpsz >= lpszText); | |
699 ATLASSERT(lpsz + nLenFind - 1 <= lpszText + nLen
- 1); | |
700 | |
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) | |
707 { | |
708 pT->UnlockBuffer(); | |
709 int n = (int)(lpsz - lpszText); | |
710 pT->SetSel(n, n + nLenFind); | |
711 return TRUE; | |
712 } | |
713 | |
714 // restore character at end of search | |
715 *lpch = chSave; | |
716 | |
717 // move on to next substring | |
718 nCompare--; | |
719 lpsz += iDir; | |
720 } | |
721 pT->UnlockBuffer(); | |
722 } | |
723 | |
724 return FALSE; | |
725 } | |
726 | |
727 LPCTSTR LockBuffer() const | |
728 { | |
729 const T* pT = static_cast<const T*>(this); | |
730 | |
731 ATLASSERT(pT->m_hWnd != NULL); | |
732 | |
733 BOOL useShadowBuffer = pT->UseShadowBuffer(); | |
734 if(useShadowBuffer) | |
735 { | |
736 if(m_pShadowBuffer == NULL || pT->GetModify()) | |
737 { | |
738 ATLASSERT(m_pShadowBuffer != NULL || m_nShadowSi
ze == 0); | |
739 UINT nSize = pT->GetWindowTextLength() + 1; | |
740 if(nSize > m_nShadowSize) | |
741 { | |
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 TCHA
R[nSize]; | |
748 pThisNoConst->m_nShadowSize = nSize; | |
749 } | |
750 | |
751 // update the shadow buffer with GetWindowText | |
752 ATLASSERT(m_nShadowSize >= nSize); | |
753 ATLASSERT(m_pShadowBuffer != NULL); | |
754 pT->GetWindowText(m_pShadowBuffer, nSize); | |
755 } | |
756 | |
757 return m_pShadowBuffer; | |
758 } | |
759 | |
760 HLOCAL hLocal = pT->GetHandle(); | |
761 ATLASSERT(hLocal != NULL); | |
762 LPCTSTR lpszText = (LPCTSTR)::LocalLock(hLocal); | |
763 ATLASSERT(lpszText != NULL); | |
764 | |
765 return lpszText; | |
766 } | |
767 | |
768 void UnlockBuffer() const | |
769 { | |
770 const T* pT = static_cast<const T*>(this); | |
771 | |
772 ATLASSERT(pT->m_hWnd != NULL); | |
773 | |
774 BOOL useShadowBuffer = pT->UseShadowBuffer(); | |
775 if(!useShadowBuffer) | |
776 { | |
777 HLOCAL hLocal = pT->GetHandle(); | |
778 ATLASSERT(hLocal != NULL); | |
779 ::LocalUnlock(hLocal); | |
780 } | |
781 } | |
782 | |
783 UINT GetBufferLength() const | |
784 { | |
785 const T* pT = static_cast<const T*>(this); | |
786 | |
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(); | |
793 | |
794 return nLen; | |
795 } | |
796 | |
797 LONG EndOfLine(LPCTSTR lpszText, UINT nLen, UINT nIndex) const | |
798 { | |
799 LPCTSTR lpsz = lpszText + nIndex; | |
800 LPCTSTR lpszStop = lpszText + nLen; | |
801 while(lpsz < lpszStop && *lpsz != _T('\r')) | |
802 ++lpsz; | |
803 return LONG(lpsz - lpszText); | |
804 } | |
805 | |
806 LONG GetSelText(_CSTRING_NS::CString& strText) const | |
807 { | |
808 const T* pT = static_cast<const T*>(this); | |
809 | |
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) - nSta
rtChar; | |
815 SecureHelper::memcpy_x(strText.GetBuffer(nLen), nLen * sizeof(TC
HAR), lpszText + nStartChar, nLen * sizeof(TCHAR)); | |
816 strText.ReleaseBuffer(nLen); | |
817 pT->UnlockBuffer(); | |
818 | |
819 return nLen; | |
820 } | |
821 | |
822 BOOL UseShadowBuffer(void) const | |
823 { | |
824 const T* pT = static_cast<const T*>(this); | |
825 | |
826 if(pT->m_bShadowBufferNeeded < 0) | |
827 { | |
828 T* pThisNoConst = const_cast<T*>(pT); | |
829 | |
830 OSVERSIONINFO ovi = { 0 }; | |
831 ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); | |
832 ::GetVersionEx(&ovi); | |
833 | |
834 bool bWin9x = (ovi.dwPlatformId == VER_PLATFORM_WIN32_WI
NDOWS); | |
835 if(bWin9x) | |
836 { | |
837 // Windows 95, 98, ME | |
838 // Under Win9x, it is necessary to maintain a sh
adow buffer. | |
839 // It is only updated when the control contents
have been changed. | |
840 pThisNoConst->m_bShadowBufferNeeded = TRUE; | |
841 } | |
842 else | |
843 { | |
844 // Windows NT, 2000, XP, etc. | |
845 pThisNoConst->m_bShadowBufferNeeded = FALSE; | |
846 | |
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 ver
sion 6 is in use, | |
851 // you're really not suppose to superclass or su
bclass common controls | |
852 // with an ANSI windows procedure (so its best t
o only theme if you use UNICODE). | |
853 // Using a shadow buffer uses GetWindowText inst
ead, so it solves | |
854 // this problem for us (although it makes it a l
ittle less efficient). | |
855 | |
856 if((ovi.dwMajorVersion == 5 && ovi.dwMinorVersio
n >= 1) || (ovi.dwMajorVersion > 5)) | |
857 { | |
858 // We use DLLVERSIONINFO_private so we d
on't have to depend on shlwapi.h | |
859 typedef struct _DLLVERSIONINFO_private | |
860 { | |
861 DWORD cbSize; | |
862 DWORD dwMajorVersion; | |
863 DWORD dwMinorVersion; | |
864 DWORD dwBuildNumber; | |
865 DWORD dwPlatformID; | |
866 } DLLVERSIONINFO_private; | |
867 | |
868 HMODULE hModule = ::LoadLibrary("comctl3
2.dll"); | |
869 if(hModule != NULL) | |
870 { | |
871 typedef HRESULT (CALLBACK *LPFN_
DllGetVersion)(DLLVERSIONINFO_private *); | |
872 LPFN_DllGetVersion fnDllGetVersi
on = (LPFN_DllGetVersion)::GetProcAddress(hModule, "DllGetVersion"); | |
873 if(fnDllGetVersion != NULL) | |
874 { | |
875 DLLVERSIONINFO_private v
ersion = { 0 }; | |
876 version.cbSize = sizeof(
DLLVERSIONINFO_private); | |
877 if(SUCCEEDED(fnDllGetVer
sion(&version))) | |
878 { | |
879 if(version.dwMaj
orVersion >= 6) | |
880 { | |
881 pThisNoC
onst->m_bShadowBufferNeeded = TRUE; | |
882 | |
883 ATLTRACE
2(atlTraceUI, 0, _T("Warning: You have compiled for MBCS/ANSI but are using comm
on controls version 6 or later (likely through a manifest file).\r\n")); | |
884 ATLTRACE
2(atlTraceUI, 0, _T("If you use common controls version 6 or later, you should o
nly do so for UNICODE builds.\r\n")); | |
885 } | |
886 } | |
887 } | |
888 | |
889 ::FreeLibrary(hModule); | |
890 hModule = NULL; | |
891 } | |
892 } | |
893 #endif // !_UNICODE | |
894 } | |
895 } | |
896 | |
897 return (pT->m_bShadowBufferNeeded == TRUE); | |
898 } | |
899 }; | |
900 | |
901 | |
902 /////////////////////////////////////////////////////////////////////////////// | |
903 // CRichEditFindReplaceImpl - Mixin class for implementing Find/Replace for CRic
hEditCtrl | |
904 // based window classes. | |
905 | |
906 // Chain to CRichEditFindReplaceImpl message map. Your class must also derive fr
om 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 // }; | |
918 | |
919 template <class T, class TFindReplaceDialog = CFindReplaceDialog> | |
920 class CRichEditFindReplaceImpl : public CEditFindReplaceImplBase<T, TFindReplace
Dialog> | |
921 { | |
922 protected: | |
923 typedef CRichEditFindReplaceImpl<T, TFindReplaceDialog> thisClass; | |
924 typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> baseClass; | |
925 | |
926 public: | |
927 BEGIN_MSG_MAP(thisClass) | |
928 ALT_MSG_MAP(1) | |
929 CHAIN_MSG_MAP_ALT(baseClass, 1) | |
930 END_MSG_MAP() | |
931 | |
932 // Operations (overrideable) | |
933 BOOL FindTextSimple(LPCTSTR lpszFind, BOOL bMatchCase, BOOL bWholeWord,
BOOL bFindDown = TRUE) | |
934 { | |
935 T* pT = static_cast<T*>(this); | |
936 | |
937 ATLASSERT(lpszFind != NULL); | |
938 FINDTEXTEX ft = { 0 }; | |
939 | |
940 pT->GetSel(ft.chrg); | |
941 if(m_bFirstSearch) | |
942 { | |
943 if(bFindDown) | |
944 m_nInitialSearchPos = ft.chrg.cpMin; | |
945 else | |
946 m_nInitialSearchPos = ft.chrg.cpMax; | |
947 m_bFirstSearch = FALSE; | |
948 } | |
949 | |
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) | |
956 | |
957 if(ft.chrg.cpMin != ft.chrg.cpMax) // i.e. there is a selection | |
958 { | |
959 if(bFindDown) | |
960 { | |
961 ft.chrg.cpMin++; | |
962 } | |
963 else | |
964 { | |
965 // won't wraparound backwards | |
966 ft.chrg.cpMin = __max(ft.chrg.cpMin, 0); | |
967 } | |
968 } | |
969 | |
970 DWORD dwFlags = bMatchCase ? FR_MATCHCASE : 0; | |
971 dwFlags |= bWholeWord ? FR_WHOLEWORD : 0; | |
972 | |
973 ft.chrg.cpMax = pT->GetTextLength() + m_nInitialSearchPos; | |
974 | |
975 if(bFindDown) | |
976 { | |
977 if(m_nInitialSearchPos >= 0) | |
978 ft.chrg.cpMax = pT->GetTextLength(); | |
979 | |
980 dwFlags |= FR_DOWN; | |
981 ATLASSERT(ft.chrg.cpMax >= ft.chrg.cpMin); | |
982 } | |
983 else | |
984 { | |
985 if(m_nInitialSearchPos >= 0) | |
986 ft.chrg.cpMax = 0; | |
987 | |
988 dwFlags &= ~FR_DOWN; | |
989 ATLASSERT(ft.chrg.cpMax <= ft.chrg.cpMin); | |
990 } | |
991 | |
992 BOOL bRet = FALSE; | |
993 | |
994 if(pT->FindAndSelect(dwFlags, ft) != -1) | |
995 { | |
996 bRet = TRUE; // we found the text | |
997 } | |
998 else if(m_nInitialSearchPos > 0) | |
999 { | |
1000 // if the original starting point was not the beginning | |
1001 // of the buffer and we haven't already been here | |
1002 if(bFindDown) | |
1003 { | |
1004 ft.chrg.cpMin = 0; | |
1005 ft.chrg.cpMax = m_nInitialSearchPos; | |
1006 } | |
1007 else | |
1008 { | |
1009 ft.chrg.cpMin = pT->GetTextLength(); | |
1010 ft.chrg.cpMax = m_nInitialSearchPos; | |
1011 } | |
1012 m_nInitialSearchPos = m_nInitialSearchPos - pT->GetTextL
ength(); | |
1013 | |
1014 bRet = (pT->FindAndSelect(dwFlags, ft) != -1) ? TRUE : F
ALSE; | |
1015 } | |
1016 | |
1017 return bRet; | |
1018 } | |
1019 | |
1020 long FindAndSelect(DWORD dwFlags, FINDTEXTEX& ft) | |
1021 { | |
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); | |
1026 | |
1027 return index; | |
1028 } | |
1029 }; | |
1030 | |
1031 }; // namespace WTL | |
1032 | |
1033 #endif // __ATLFIND_H__ | |
OLD | NEW |