]> git.sesse.net Git - casparcg/blob - WTL80/include/atlfind.h
2.0.2: INFO TEMPLATE works on both compressed and uncompressed templates.
[casparcg] / WTL80 / include / atlfind.h
1 // Windows Template Library - WTL version 8.0\r
2 // Copyright (C) Microsoft Corporation. All rights reserved.\r
3 //\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
11 \r
12 #ifndef __ATLFIND_H__\r
13 #define __ATLFIND_H__\r
14 \r
15 #pragma once\r
16 \r
17 #ifndef __cplusplus\r
18         #error ATL requires C++ compilation (use a .cpp suffix)\r
19 #endif\r
20 \r
21 #ifdef _WIN32_WCE\r
22         #error atlfind.h is not supported on Windows CE\r
23 #endif\r
24 \r
25 #ifndef __ATLCTRLS_H__\r
26         #error atlfind.h requires atlctrls.h to be included first\r
27 #endif\r
28 \r
29 #ifndef __ATLDLGS_H__\r
30         #error atlfind.h requires atldlgs.h to be included first\r
31 #endif\r
32 \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
35 #endif\r
36 \r
37 \r
38 ///////////////////////////////////////////////////////////////////////////////\r
39 // Classes in this file:\r
40 //\r
41 // CEditFindReplaceImplBase<T, TFindReplaceDialog>\r
42 // CEditFindReplaceImpl<T, TFindReplaceDialog>\r
43 // CRichEditFindReplaceImpl<T, TFindReplaceDialog>\r
44 \r
45 \r
46 namespace WTL\r
47 {\r
48 \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
52 \r
53 template <class T, class TFindReplaceDialog = CFindReplaceDialog>\r
54 class CEditFindReplaceImplBase\r
55 {\r
56 protected:\r
57 // Typedefs\r
58         typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> thisClass;\r
59 \r
60 // Data members\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
66 \r
67 // Enumerations\r
68         enum TranslationTextItem\r
69         {\r
70                 eText_OnReplaceAllMessage   = 0,\r
71                 eText_OnReplaceAllTitle     = 1,\r
72                 eText_OnTextNotFoundMessage = 2,\r
73                 eText_OnTextNotFoundTitle   = 3\r
74         };\r
75 \r
76 public:\r
77 // Constructors\r
78         CEditFindReplaceImplBase() :\r
79                 m_pFindReplaceDialog(NULL),\r
80                 m_bFindOnly(TRUE),\r
81                 m_bFirstSearch(TRUE),\r
82                 m_bMatchCase(FALSE),\r
83                 m_bWholeWord(FALSE),\r
84                 m_bFindDown(TRUE),\r
85                 m_nInitialSearchPos(0),\r
86                 m_hOldCursor(NULL)\r
87         {\r
88         }\r
89 \r
90 // Message Handlers\r
91         BEGIN_MSG_MAP(thisClass)\r
92         ALT_MSG_MAP(1)\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
98         END_MSG_MAP()\r
99 \r
100         LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)\r
101         {\r
102                 if(m_pFindReplaceDialog != NULL)\r
103                 {\r
104                         m_pFindReplaceDialog->SendMessage(WM_CLOSE);\r
105                         ATLASSERT(m_pFindReplaceDialog == NULL);\r
106                 }\r
107 \r
108                 bHandled = FALSE;\r
109                 return 0;\r
110         }\r
111 \r
112         LRESULT OnFindReplaceCmd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)\r
113         {\r
114                 T* pT = static_cast<T*>(this);\r
115 \r
116                 TFindReplaceDialog* pDialog = TFindReplaceDialog::GetNotifier(lParam);\r
117                 if(pDialog == NULL)\r
118                 {\r
119                         ATLASSERT(FALSE);\r
120                         ::MessageBeep(MB_ICONERROR);\r
121                         return 1;\r
122                 }\r
123                 ATLASSERT(pDialog == m_pFindReplaceDialog);\r
124 \r
125                 LPFINDREPLACE findReplace = (LPFINDREPLACE)lParam;\r
126                 if((m_pFindReplaceDialog != NULL) && (findReplace != NULL))\r
127                 {\r
128                         if(pDialog->FindNext())\r
129                         {\r
130                                 pT->OnFindNext(pDialog->GetFindString(), pDialog->SearchDown(),\r
131                                         pDialog->MatchCase(), pDialog->MatchWholeWord());\r
132                         }\r
133                         else if(pDialog->ReplaceCurrent())\r
134                         {\r
135                                 pT->OnReplaceSel(pDialog->GetFindString(),\r
136                                         pDialog->SearchDown(), pDialog->MatchCase(), pDialog->MatchWholeWord(),\r
137                                         pDialog->GetReplaceString());\r
138                         }\r
139                         else if(pDialog->ReplaceAll())\r
140                         {\r
141                                 pT->OnReplaceAll(pDialog->GetFindString(), pDialog->GetReplaceString(),\r
142                                         pDialog->MatchCase(), pDialog->MatchWholeWord());\r
143                         }\r
144                         else if(pDialog->IsTerminating())\r
145                         {\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
150                         }\r
151                 }\r
152 \r
153                 return 0;\r
154         }\r
155 \r
156         LRESULT OnEditFind(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r
157         {\r
158                 T* pT = static_cast<T*>(this);\r
159                 pT->FindReplace(TRUE);\r
160 \r
161                 return 0;\r
162         }\r
163 \r
164         LRESULT OnEditRepeat(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)\r
165         {\r
166                 T* pT = static_cast<T*>(this);\r
167 \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
173 \r
174                 if(m_sFindNext.IsEmpty())\r
175                 {\r
176                         pT->FindReplace(TRUE);\r
177                 }\r
178                 else\r
179                 {\r
180                         if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown))\r
181                                 pT->TextNotFound(m_sFindNext);\r
182                 }\r
183 \r
184                 return 0;\r
185         }\r
186 \r
187         LRESULT OnEditReplace(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& bHandled)\r
188         {\r
189                 T* pT = static_cast<T*>(this);\r
190 \r
191                 DWORD style = pT->GetStyle();\r
192                 if((style & ES_READONLY) != ES_READONLY)\r
193                 {\r
194                         pT->FindReplace(FALSE);\r
195                 }\r
196                 else\r
197                 {\r
198                         // Don't allow replace when the edit control is read only\r
199                         bHandled = FALSE;\r
200                 }\r
201 \r
202                 return 0;\r
203         }\r
204 \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
211         {\r
212                 // You can override all of this in a derived class\r
213 \r
214                 TFindReplaceDialog* findReplaceDialog = new TFindReplaceDialog();\r
215                 if(findReplaceDialog == NULL)\r
216                 {\r
217                         ::MessageBeep(MB_ICONHAND);\r
218                 }\r
219                 else\r
220                 {\r
221                         HWND hWndFindReplace = findReplaceDialog->Create(bFindOnly,\r
222                                 lpszFindWhat, lpszReplaceWith, dwFlags, hWndParent);\r
223                         if(hWndFindReplace == NULL)\r
224                         {\r
225                                 delete findReplaceDialog;\r
226                                 findReplaceDialog = NULL;\r
227                         }\r
228                         else\r
229                         {\r
230                                 findReplaceDialog->SetActiveWindow();\r
231                                 findReplaceDialog->ShowWindow(SW_SHOW);\r
232                         }\r
233                 }\r
234 \r
235                 return findReplaceDialog;\r
236         }\r
237 \r
238         void AdjustDialogPosition(HWND hWndDialog)\r
239         {\r
240                 ATLASSERT((hWndDialog != NULL) && ::IsWindow(hWndDialog));\r
241 \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
249                 CRect rect;\r
250                 ::GetWindowRect(hWndDialog, &rect);\r
251                 if(rect.PtInRect(point))\r
252                 {\r
253                         if(point.y > rect.Height())\r
254                         {\r
255                                 rect.OffsetRect(0, point.y - rect.bottom - 20);\r
256                         }\r
257                         else\r
258                         {\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
262                         }\r
263 \r
264                         ::MoveWindow(hWndDialog, rect.left, rect.top, rect.Width(), rect.Height(), TRUE);\r
265                 }\r
266         }\r
267 \r
268         DWORD GetFindReplaceDialogFlags(void) const\r
269         {\r
270                 DWORD dwFlags = 0;\r
271 \r
272                 if(m_bFindDown)\r
273                         dwFlags |= FR_DOWN;\r
274                 if(m_bMatchCase)\r
275                         dwFlags |= FR_MATCHCASE;\r
276                 if(m_bWholeWord)\r
277                         dwFlags |= FR_WHOLEWORD;\r
278 \r
279                 return dwFlags;\r
280         }\r
281 \r
282         void FindReplace(BOOL bFindOnly)\r
283         {\r
284                 T* pT = static_cast<T*>(this);\r
285                 m_bFirstSearch = TRUE;\r
286                 if(m_pFindReplaceDialog != NULL)\r
287                 {\r
288                         if(m_bFindOnly == bFindOnly)\r
289                         {\r
290                                 m_pFindReplaceDialog->SetActiveWindow();\r
291                                 m_pFindReplaceDialog->ShowWindow(SW_SHOW);\r
292                                 return;\r
293                         }\r
294                         else\r
295                         {\r
296                                 m_pFindReplaceDialog->SendMessage(WM_CLOSE);\r
297                                 ATLASSERT(m_pFindReplaceDialog == NULL);\r
298                         }\r
299                 }\r
300 \r
301                 ATLASSERT(m_pFindReplaceDialog == NULL);\r
302 \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
310 \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
316         }\r
317 \r
318         BOOL SameAsSelected(LPCTSTR lpszCompare, BOOL bMatchCase, BOOL /*bWholeWord*/)\r
319         {\r
320                 T* pT = static_cast<T*>(this);\r
321 \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
329                         return FALSE;\r
330 \r
331                 // length is the same, check contents\r
332                 _CSTRING_NS::CString selectedText;\r
333                 pT->GetSelText(selectedText);\r
334 \r
335                 return (bMatchCase && selectedText.Compare(lpszCompare) == 0) ||\r
336                         (!bMatchCase && selectedText.CompareNoCase(lpszCompare) == 0);\r
337         }\r
338 \r
339         void TextNotFound(LPCTSTR lpszFind)\r
340         {\r
341                 T* pT = static_cast<T*>(this);\r
342                 m_bFirstSearch = TRUE;\r
343                 pT->OnTextNotFound(lpszFind);\r
344         }\r
345 \r
346         _CSTRING_NS::CString GetTranslationText(enum TranslationTextItem eItem) const\r
347         {\r
348                 _CSTRING_NS::CString text;\r
349                 switch(eItem)\r
350                 {\r
351                 case eText_OnReplaceAllMessage:\r
352                         text = _T("Replaced %d occurances of \"%s\" with \"%s\"");\r
353                         break;\r
354                 case eText_OnReplaceAllTitle:\r
355                         text = _T("Replace All");\r
356                         break;\r
357                 case eText_OnTextNotFoundMessage:\r
358                         text = _T("Unable to find the text \"%s\"");\r
359                         break;\r
360                 case eText_OnTextNotFoundTitle:\r
361                         text = _T("Text not found");\r
362                         break;\r
363                 }\r
364 \r
365                 return text;\r
366         }\r
367 \r
368 // Overrideable Handlers\r
369         void OnFindNext(LPCTSTR lpszFind, BOOL bFindDown, BOOL bMatchCase, BOOL bWholeWord)\r
370         {\r
371                 T* pT = static_cast<T*>(this);\r
372 \r
373                 m_sFindNext = lpszFind;\r
374                 m_bMatchCase = bMatchCase;\r
375                 m_bWholeWord = bWholeWord;\r
376                 m_bFindDown = bFindDown;\r
377 \r
378                 if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown))\r
379                         pT->TextNotFound(m_sFindNext);\r
380                 else\r
381                         pT->AdjustDialogPosition(m_pFindReplaceDialog->operator HWND());\r
382         }\r
383 \r
384         void OnReplaceSel(LPCTSTR lpszFind, BOOL bFindDown, BOOL bMatchCase, BOOL bWholeWord, LPCTSTR lpszReplace)\r
385         {\r
386                 T* pT = static_cast<T*>(this);\r
387 \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
393 \r
394                 if(pT->SameAsSelected(m_sFindNext, m_bMatchCase, m_bWholeWord))\r
395                         pT->ReplaceSel(m_sReplaceWith);\r
396 \r
397                 if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown))\r
398                         pT->TextNotFound(m_sFindNext);\r
399                 else\r
400                         pT->AdjustDialogPosition(m_pFindReplaceDialog->operator HWND());\r
401         }\r
402 \r
403         void OnReplaceAll(LPCTSTR lpszFind, LPCTSTR lpszReplace, BOOL bMatchCase, BOOL bWholeWord)\r
404         {\r
405                 T* pT = static_cast<T*>(this);\r
406 \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
412 \r
413                 // no selection or different than what looking for\r
414                 if(!pT->SameAsSelected(m_sFindNext, m_bMatchCase, m_bWholeWord))\r
415                 {\r
416                         if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown))\r
417                         {\r
418                                 pT->TextNotFound(m_sFindNext);\r
419                                 return;\r
420                         }\r
421                 }\r
422 \r
423                 pT->OnReplaceAllCoreBegin();\r
424 \r
425                 int replaceCount=0;\r
426                 do\r
427                 {\r
428                         ++replaceCount;\r
429                         pT->ReplaceSel(m_sReplaceWith);\r
430                 } while(pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown));\r
431 \r
432                 pT->OnReplaceAllCoreEnd(replaceCount);\r
433         }\r
434 \r
435         void OnReplaceAllCoreBegin()\r
436         {\r
437                 T* pT = static_cast<T*>(this);\r
438 \r
439                 m_hOldCursor = ::SetCursor(::LoadCursor(NULL, IDC_WAIT));\r
440 \r
441                 pT->HideSelection(TRUE, FALSE);\r
442 \r
443         }\r
444 \r
445         void OnReplaceAllCoreEnd(int replaceCount)\r
446         {\r
447                 T* pT = static_cast<T*>(this);\r
448                 pT->HideSelection(FALSE, FALSE);\r
449 \r
450                 ::SetCursor(m_hOldCursor);\r
451 \r
452                 _CSTRING_NS::CString message = pT->GetTranslationText(eText_OnReplaceAllMessage);\r
453                 if(message.GetLength() > 0)\r
454                 {\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
459                         {\r
460                                 m_pFindReplaceDialog->MessageBox(formattedMessage,\r
461                                         pT->GetTranslationText(eText_OnReplaceAllTitle),\r
462                                         MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);\r
463                         }\r
464                         else\r
465                         {\r
466                                 pT->MessageBox(formattedMessage,\r
467                                         pT->GetTranslationText(eText_OnReplaceAllTitle),\r
468                                         MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);\r
469                         }\r
470                 }\r
471         }\r
472 \r
473         void OnTextNotFound(LPCTSTR lpszFind)\r
474         {\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
478                 {\r
479                         _CSTRING_NS::CString formattedMessage;\r
480                         formattedMessage.Format(message, lpszFind);\r
481                         if(m_pFindReplaceDialog != NULL)\r
482                         {\r
483                                 m_pFindReplaceDialog->MessageBox(formattedMessage,\r
484                                         pT->GetTranslationText(eText_OnTextNotFoundTitle),\r
485                                         MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);\r
486                         }\r
487                         else\r
488                         {\r
489                                 pT->MessageBox(formattedMessage,\r
490                                         pT->GetTranslationText(eText_OnTextNotFoundTitle),\r
491                                         MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);\r
492                         }\r
493                 }\r
494                 else\r
495                 {\r
496                         ::MessageBeep(MB_ICONHAND);\r
497                 }\r
498         }\r
499 \r
500         void OnTerminatingFindReplaceDialog(TFindReplaceDialog*& /*findReplaceDialog*/)\r
501         {\r
502         }\r
503 };\r
504 \r
505 \r
506 ///////////////////////////////////////////////////////////////////////////////\r
507 // CEditFindReplaceImpl - Mixin class for implementing Find/Replace for CEdit\r
508 // based window classes.\r
509 \r
510 // Chain to CEditFindReplaceImpl message map. Your class must also derive from CEdit.\r
511 // Example:\r
512 // class CMyEdit : public CWindowImpl<CMyEdit, CEdit>,\r
513 //                 public CEditFindReplaceImpl<CMyEdit>\r
514 // {\r
515 // public:\r
516 //      BEGIN_MSG_MAP(CMyEdit)\r
517 //              // your handlers...\r
518 //              CHAIN_MSG_MAP_ALT(CEditFindReplaceImpl<CMyEdit>, 1)\r
519 //      END_MSG_MAP()\r
520 //      // other stuff...\r
521 // };\r
522 \r
523 template <class T, class TFindReplaceDialog = CFindReplaceDialog>\r
524 class CEditFindReplaceImpl : public CEditFindReplaceImplBase<T, TFindReplaceDialog>\r
525 {\r
526 protected:\r
527         typedef CEditFindReplaceImpl<T, TFindReplaceDialog> thisClass;\r
528         typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> baseClass;\r
529 \r
530 // Data members\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
534 \r
535 public:\r
536 // Constructors\r
537         CEditFindReplaceImpl() :\r
538                 m_pShadowBuffer(NULL),\r
539                 m_nShadowSize(0),\r
540                 m_bShadowBufferNeeded(-1)\r
541         {\r
542         }\r
543 \r
544         virtual ~CEditFindReplaceImpl()\r
545         {\r
546                 if(m_pShadowBuffer != NULL)\r
547                 {\r
548                         delete [] m_pShadowBuffer;\r
549                         m_pShadowBuffer = NULL;\r
550                 }\r
551         }\r
552 \r
553 // Message Handlers\r
554         BEGIN_MSG_MAP(thisClass)\r
555         ALT_MSG_MAP(1)\r
556                 CHAIN_MSG_MAP_ALT(baseClass, 1)\r
557         END_MSG_MAP()\r
558 \r
559 // Operations\r
560         // Supported only for RichEdit, so this does nothing for Edit\r
561         void HideSelection(BOOL /*bHide*/ = TRUE, BOOL /*bChangeStyle*/ = FALSE)\r
562         {\r
563         }\r
564 \r
565 // Operations (overrideable)\r
566         BOOL FindTextSimple(LPCTSTR lpszFind, BOOL bMatchCase, BOOL bWholeWord, BOOL bFindDown = TRUE)\r
567         {\r
568                 T* pT = static_cast<T*>(this);\r
569 \r
570                 ATLASSERT(lpszFind != NULL);\r
571                 ATLASSERT(*lpszFind != _T('\0'));\r
572 \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
578 \r
579                 // can't find a match before the first character\r
580                 if(nStart == 0 && iDir < 0)\r
581                         return FALSE;\r
582 \r
583                 LPCTSTR lpszText = pT->LockBuffer();\r
584 \r
585                 bool isDBCS = false;\r
586 #ifdef _MBCS\r
587                 CPINFO info = { 0 };\r
588                 ::GetCPInfo(::GetOEMCP(), &info);\r
589                 isDBCS = (info.MaxCharSize > 1);\r
590 #endif\r
591 \r
592                 if(iDir < 0)\r
593                 {\r
594                         // always go back one for search backwards\r
595                         nStart -= int((lpszText + nStart) - ::CharPrev(lpszText, lpszText + nStart));\r
596                 }\r
597                 else if(nStartChar != nEndChar && pT->SameAsSelected(lpszFind, bMatchCase, bWholeWord))\r
598                 {\r
599                         // easy to go backward/forward with SBCS\r
600 #ifndef _UNICODE\r
601                         if(::IsDBCSLeadByte(lpszText[nStart]))\r
602                                 nStart++;\r
603 #endif\r
604                         nStart += iDir;\r
605                 }\r
606 \r
607                 // handle search with nStart past end of buffer\r
608                 UINT nLenFind = ::lstrlen(lpszFind);\r
609                 if(nStart + nLenFind - 1 >= nLen)\r
610                 {\r
611                         if(iDir < 0 && nLen >= nLenFind)\r
612                         {\r
613                                 if(isDBCS)\r
614                                 {\r
615                                         // walk back to previous character n times\r
616                                         nStart = nLen;\r
617                                         int n = nLenFind;\r
618                                         while(n--)\r
619                                         {\r
620                                                 nStart -= int((lpszText + nStart) - ::CharPrev(lpszText, lpszText + nStart));\r
621                                         }\r
622                                 }\r
623                                 else\r
624                                 {\r
625                                         // single-byte character set is easy and fast\r
626                                         nStart = nLen - nLenFind;\r
627                                 }\r
628                                 ATLASSERT(nStart + nLenFind - 1 <= nLen);\r
629                         }\r
630                         else\r
631                         {\r
632                                 pT->UnlockBuffer();\r
633                                 return FALSE;\r
634                         }\r
635                 }\r
636 \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
641 \r
642                 if(isDBCS)\r
643                 {\r
644                         // double-byte string search\r
645                         LPCTSTR lpszStop = NULL;\r
646                         if(iDir > 0)\r
647                         {\r
648                                 // start at current and find _first_ occurrance\r
649                                 lpszStop = lpszText + nLen - nLenFind + 1;\r
650                         }\r
651                         else\r
652                         {\r
653                                 // start at top and find _last_ occurrance\r
654                                 lpszStop = lpsz;\r
655                                 lpsz = lpszText;\r
656                         }\r
657 \r
658                         LPCTSTR lpszFound = NULL;\r
659                         while(lpsz <= lpszStop)\r
660                         {\r
661 #ifndef _UNICODE\r
662                                 if(!bMatchCase || (*lpsz == *lpszFind && (!::IsDBCSLeadByte(*lpsz) || lpsz[1] == lpszFind[1])))\r
663 #else\r
664                                 if(!bMatchCase || (*lpsz == *lpszFind && lpsz[1] == lpszFind[1]))\r
665 #endif\r
666                                 {\r
667                                         LPTSTR lpch = (LPTSTR)(lpsz + nLenFind);\r
668                                         TCHAR chSave = *lpch;\r
669                                         *lpch = _T('\0');\r
670                                         int nResult = (*pfnCompare)(lpsz, lpszFind);\r
671                                         *lpch = chSave;\r
672                                         if(nResult == 0)\r
673                                         {\r
674                                                 lpszFound = lpsz;\r
675                                                 if(iDir > 0)\r
676                                                         break;\r
677                                         }\r
678                                 }\r
679                                 lpsz = ::CharNext(lpsz);\r
680                         }\r
681                         pT->UnlockBuffer();\r
682 \r
683                         if(lpszFound != NULL)\r
684                         {\r
685                                 int n = (int)(lpszFound - lpszText);\r
686                                 pT->SetSel(n, n + nLenFind);\r
687                                 return TRUE;\r
688                         }\r
689                 }\r
690                 else\r
691                 {\r
692                         // single-byte string search\r
693                         UINT nCompare;\r
694                         if(iDir < 0)\r
695                                 nCompare = (UINT)(lpsz - lpszText) + 1;\r
696                         else\r
697                                 nCompare = nLen - (UINT)(lpsz - lpszText) - nLenFind + 1;\r
698 \r
699                         while(nCompare > 0)\r
700                         {\r
701                                 ATLASSERT(lpsz >= lpszText);\r
702                                 ATLASSERT(lpsz + nLenFind - 1 <= lpszText + nLen - 1);\r
703 \r
704                                 LPSTR lpch = (LPSTR)(lpsz + nLenFind);\r
705                                 char chSave = *lpch;\r
706                                 *lpch = '\0';\r
707                                 int nResult = (*pfnCompare)(lpsz, lpszFind);\r
708                                 *lpch = chSave;\r
709                                 if(nResult == 0)\r
710                                 {\r
711                                         pT->UnlockBuffer();\r
712                                         int n = (int)(lpsz - lpszText);\r
713                                         pT->SetSel(n, n + nLenFind);\r
714                                         return TRUE;\r
715                                 }\r
716 \r
717                                 // restore character at end of search\r
718                                 *lpch = chSave;\r
719 \r
720                                 // move on to next substring\r
721                                 nCompare--;\r
722                                 lpsz += iDir;\r
723                         }\r
724                         pT->UnlockBuffer();\r
725                 }\r
726 \r
727                 return FALSE;\r
728         }\r
729 \r
730         LPCTSTR LockBuffer() const\r
731         {\r
732                 const T* pT = static_cast<const T*>(this);\r
733 \r
734                 ATLASSERT(pT->m_hWnd != NULL);\r
735 \r
736                 BOOL useShadowBuffer = pT->UseShadowBuffer();\r
737                 if(useShadowBuffer)\r
738                 {\r
739                         if(m_pShadowBuffer == NULL || pT->GetModify())\r
740                         {\r
741                                 ATLASSERT(m_pShadowBuffer != NULL || m_nShadowSize == 0);\r
742                                 UINT nSize = pT->GetWindowTextLength() + 1;\r
743                                 if(nSize > m_nShadowSize)\r
744                                 {\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
752                                 }\r
753 \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
758                         }\r
759 \r
760                         return m_pShadowBuffer;\r
761                 }\r
762 \r
763                 HLOCAL hLocal = pT->GetHandle();\r
764                 ATLASSERT(hLocal != NULL);\r
765                 LPCTSTR lpszText = (LPCTSTR)::LocalLock(hLocal);\r
766                 ATLASSERT(lpszText != NULL);\r
767 \r
768                 return lpszText;\r
769         }\r
770 \r
771         void UnlockBuffer() const\r
772         {\r
773                 const T* pT = static_cast<const T*>(this);\r
774 \r
775                 ATLASSERT(pT->m_hWnd != NULL);\r
776 \r
777                 BOOL useShadowBuffer = pT->UseShadowBuffer();\r
778                 if(!useShadowBuffer)\r
779                 {\r
780                         HLOCAL hLocal = pT->GetHandle();\r
781                         ATLASSERT(hLocal != NULL);\r
782                         ::LocalUnlock(hLocal);\r
783                 }\r
784         }\r
785 \r
786         UINT GetBufferLength() const\r
787         {\r
788                 const T* pT = static_cast<const T*>(this);\r
789 \r
790                 ATLASSERT(pT->m_hWnd != NULL);\r
791                 UINT nLen = 0;\r
792                 LPCTSTR lpszText = pT->LockBuffer();\r
793                 if(lpszText != NULL)\r
794                         nLen = ::lstrlen(lpszText);\r
795                 pT->UnlockBuffer();\r
796 \r
797                 return nLen;\r
798         }\r
799 \r
800         LONG EndOfLine(LPCTSTR lpszText, UINT nLen, UINT nIndex) const\r
801         {\r
802                 LPCTSTR lpsz = lpszText + nIndex;\r
803                 LPCTSTR lpszStop = lpszText + nLen;\r
804                 while(lpsz < lpszStop && *lpsz != _T('\r'))\r
805                         ++lpsz;\r
806                 return LONG(lpsz - lpszText);\r
807         }\r
808 \r
809         LONG GetSelText(_CSTRING_NS::CString& strText) const\r
810         {\r
811                 const T* pT = static_cast<const T*>(this);\r
812 \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
821 \r
822                 return nLen;\r
823         }\r
824 \r
825         BOOL UseShadowBuffer(void) const\r
826         {\r
827                 const T* pT = static_cast<const T*>(this);\r
828 \r
829                 if(pT->m_bShadowBufferNeeded < 0)\r
830                 {\r
831                         T* pThisNoConst = const_cast<T*>(pT);\r
832 \r
833                         OSVERSIONINFO ovi = { 0 };\r
834                         ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);\r
835                         ::GetVersionEx(&ovi);\r
836 \r
837                         bool bWin9x = (ovi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);\r
838                         if(bWin9x)\r
839                         {\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
844                         }\r
845                         else\r
846                         {\r
847                                 // Windows NT, 2000, XP, etc.\r
848                                 pThisNoConst->m_bShadowBufferNeeded = FALSE;\r
849 \r
850 #ifndef _UNICODE\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
858 \r
859                                 if((ovi.dwMajorVersion == 5 && ovi.dwMinorVersion >= 1) || (ovi.dwMajorVersion > 5))\r
860                                 {\r
861                                         // We use DLLVERSIONINFO_private so we don't have to depend on shlwapi.h\r
862                                         typedef struct _DLLVERSIONINFO_private\r
863                                         {\r
864                                                 DWORD cbSize;\r
865                                                 DWORD dwMajorVersion;\r
866                                                 DWORD dwMinorVersion;\r
867                                                 DWORD dwBuildNumber;\r
868                                                 DWORD dwPlatformID;\r
869                                         } DLLVERSIONINFO_private;\r
870 \r
871                                         HMODULE hModule = ::LoadLibrary("comctl32.dll");\r
872                                         if(hModule != NULL)\r
873                                         {\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
877                                                 {\r
878                                                         DLLVERSIONINFO_private version = { 0 };\r
879                                                         version.cbSize = sizeof(DLLVERSIONINFO_private);\r
880                                                         if(SUCCEEDED(fnDllGetVersion(&version)))\r
881                                                         {\r
882                                                                 if(version.dwMajorVersion >= 6)\r
883                                                                 {\r
884                                                                         pThisNoConst->m_bShadowBufferNeeded = TRUE;\r
885 \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
888                                                                 }\r
889                                                         }\r
890                                                 }\r
891 \r
892                                                 ::FreeLibrary(hModule);\r
893                                                 hModule = NULL;\r
894                                         }\r
895                                 }\r
896 #endif // !_UNICODE\r
897                         }\r
898                 }\r
899 \r
900                 return (pT->m_bShadowBufferNeeded == TRUE);\r
901         }\r
902 };\r
903 \r
904 \r
905 ///////////////////////////////////////////////////////////////////////////////\r
906 // CRichEditFindReplaceImpl - Mixin class for implementing Find/Replace for CRichEditCtrl\r
907 // based window classes.\r
908 \r
909 // Chain to CRichEditFindReplaceImpl message map. Your class must also derive from CRichEditCtrl.\r
910 // Example:\r
911 // class CMyRichEdit : public CWindowImpl<CMyRichEdit, CRichEditCtrl>,\r
912 //                     public CRichEditFindReplaceImpl<CMyRichEdit>\r
913 // {\r
914 // public:\r
915 //      BEGIN_MSG_MAP(CMyRichEdit)\r
916 //              // your handlers...\r
917 //              CHAIN_MSG_MAP_ALT(CRichEditFindReplaceImpl<CMyRichEdit>, 1)\r
918 //      END_MSG_MAP()\r
919 //      // other stuff...\r
920 // };\r
921 \r
922 template <class T, class TFindReplaceDialog = CFindReplaceDialog>\r
923 class CRichEditFindReplaceImpl : public CEditFindReplaceImplBase<T, TFindReplaceDialog>\r
924 {\r
925 protected:\r
926         typedef CRichEditFindReplaceImpl<T, TFindReplaceDialog> thisClass;\r
927         typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> baseClass;\r
928 \r
929 public:\r
930         BEGIN_MSG_MAP(thisClass)\r
931         ALT_MSG_MAP(1)\r
932                 CHAIN_MSG_MAP_ALT(baseClass, 1)\r
933         END_MSG_MAP()\r
934 \r
935 // Operations (overrideable)\r
936         BOOL FindTextSimple(LPCTSTR lpszFind, BOOL bMatchCase, BOOL bWholeWord, BOOL bFindDown = TRUE)\r
937         {\r
938                 T* pT = static_cast<T*>(this);\r
939 \r
940                 ATLASSERT(lpszFind != NULL);\r
941                 FINDTEXTEX ft = { 0 };\r
942 \r
943                 pT->GetSel(ft.chrg);\r
944                 if(m_bFirstSearch)\r
945                 {\r
946                         if(bFindDown)\r
947                                 m_nInitialSearchPos = ft.chrg.cpMin;\r
948                         else\r
949                                 m_nInitialSearchPos = ft.chrg.cpMax;\r
950                         m_bFirstSearch = FALSE;\r
951                 }\r
952 \r
953 #if (_RICHEDIT_VER >= 0x0200)\r
954                 ft.lpstrText = (LPTSTR)lpszFind;\r
955 #else // !(_RICHEDIT_VER >= 0x0200)\r
956                 USES_CONVERSION;\r
957                 ft.lpstrText = T2A((LPTSTR)lpszFind);\r
958 #endif // !(_RICHEDIT_VER >= 0x0200)\r
959 \r
960                 if(ft.chrg.cpMin != ft.chrg.cpMax) // i.e. there is a selection\r
961                 {\r
962                         if(bFindDown)\r
963                         {\r
964                                 ft.chrg.cpMin++;\r
965                         }\r
966                         else\r
967                         {\r
968                                 // won't wraparound backwards\r
969                                 ft.chrg.cpMin = max(ft.chrg.cpMin, 0);\r
970                         }\r
971                 }\r
972 \r
973                 DWORD dwFlags = bMatchCase ? FR_MATCHCASE : 0;\r
974                 dwFlags |= bWholeWord ? FR_WHOLEWORD : 0;\r
975 \r
976                 ft.chrg.cpMax = pT->GetTextLength() + m_nInitialSearchPos;\r
977 \r
978                 if(bFindDown)\r
979                 {\r
980                         if(m_nInitialSearchPos >= 0)\r
981                                 ft.chrg.cpMax = pT->GetTextLength();\r
982 \r
983                         dwFlags |= FR_DOWN;\r
984                         ATLASSERT(ft.chrg.cpMax >= ft.chrg.cpMin);\r
985                 }\r
986                 else\r
987                 {\r
988                         if(m_nInitialSearchPos >= 0)\r
989                                 ft.chrg.cpMax = 0;\r
990 \r
991                         dwFlags &= ~FR_DOWN;\r
992                         ATLASSERT(ft.chrg.cpMax <= ft.chrg.cpMin);\r
993                 }\r
994 \r
995                 BOOL bRet = FALSE;\r
996 \r
997                 if(pT->FindAndSelect(dwFlags, ft) != -1)\r
998                 {\r
999                         bRet = TRUE;   // we found the text\r
1000                 }\r
1001                 else if(m_nInitialSearchPos > 0)\r
1002                 {\r
1003                         // if the original starting point was not the beginning\r
1004                         // of the buffer and we haven't already been here\r
1005                         if(bFindDown)\r
1006                         {\r
1007                                 ft.chrg.cpMin = 0;\r
1008                                 ft.chrg.cpMax = m_nInitialSearchPos;\r
1009                         }\r
1010                         else\r
1011                         {\r
1012                                 ft.chrg.cpMin = pT->GetTextLength();\r
1013                                 ft.chrg.cpMax = m_nInitialSearchPos;\r
1014                         }\r
1015                         m_nInitialSearchPos = m_nInitialSearchPos - pT->GetTextLength();\r
1016 \r
1017                         bRet = (pT->FindAndSelect(dwFlags, ft) != -1) ? TRUE : FALSE;\r
1018                 }\r
1019 \r
1020                 return bRet;\r
1021         }\r
1022 \r
1023         long FindAndSelect(DWORD dwFlags, FINDTEXTEX& ft)\r
1024         {\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
1029 \r
1030                 return index;\r
1031         }\r
1032 };\r
1033 \r
1034 }; // namespace WTL\r
1035 \r
1036 #endif // __ATLFIND_H__\r