atlfind.h 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897
  1. // Windows Template Library - WTL version 10.0
  2. // Copyright (C) Microsoft Corporation, WTL Team. 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 Public License (http://opensource.org/licenses/MS-PL)
  7. // which can be found in the file MS-PL.txt at the root folder.
  8. #ifndef __ATLFIND_H__
  9. #define __ATLFIND_H__
  10. #pragma once
  11. #ifndef __ATLCTRLS_H__
  12. #error atlfind.h requires atlctrls.h to be included first
  13. #endif
  14. #ifndef __ATLDLGS_H__
  15. #error atlfind.h requires atldlgs.h to be included first
  16. #endif
  17. #ifndef __ATLSTR_H__
  18. #error atlfind.h requires CString
  19. #endif
  20. ///////////////////////////////////////////////////////////////////////////////
  21. // Classes in this file:
  22. //
  23. // CEditFindReplaceImplBase<T, TFindReplaceDialog>
  24. // CEditFindReplaceImpl<T, TFindReplaceDialog>
  25. // CRichEditFindReplaceImpl<T, TFindReplaceDialog>
  26. namespace WTL
  27. {
  28. ///////////////////////////////////////////////////////////////////////////////
  29. // CEditFindReplaceImplBase - Base class for mixin classes that
  30. // help implement Find/Replace for CEdit or CRichEditCtrl based window classes.
  31. template <class T, class TFindReplaceDialog = CFindReplaceDialog>
  32. class CEditFindReplaceImplBase
  33. {
  34. protected:
  35. // Typedefs
  36. typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> thisClass;
  37. // Data members
  38. TFindReplaceDialog* m_pFindReplaceDialog;
  39. ATL::CString m_sFindNext, m_sReplaceWith;
  40. BOOL m_bFindOnly, m_bFirstSearch, m_bMatchCase, m_bWholeWord, m_bFindDown;
  41. LONG m_nInitialSearchPos;
  42. HCURSOR m_hOldCursor;
  43. // Enumerations
  44. enum TranslationTextItem
  45. {
  46. eText_OnReplaceAllMessage = 0,
  47. eText_OnReplaceAllTitle = 1,
  48. eText_OnTextNotFoundMessage = 2,
  49. eText_OnTextNotFoundTitle = 3
  50. };
  51. public:
  52. // Constructors
  53. CEditFindReplaceImplBase() :
  54. m_pFindReplaceDialog(NULL),
  55. m_bFindOnly(TRUE),
  56. m_bFirstSearch(TRUE),
  57. m_bMatchCase(FALSE),
  58. m_bWholeWord(FALSE),
  59. m_bFindDown(TRUE),
  60. m_nInitialSearchPos(0),
  61. m_hOldCursor(NULL)
  62. {
  63. }
  64. // Message Handlers
  65. BEGIN_MSG_MAP(thisClass)
  66. ALT_MSG_MAP(1)
  67. MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
  68. MESSAGE_HANDLER(TFindReplaceDialog::GetFindReplaceMsg(), OnFindReplaceCmd)
  69. COMMAND_ID_HANDLER(ID_EDIT_FIND, OnEditFind)
  70. COMMAND_ID_HANDLER(ID_EDIT_REPEAT, OnEditRepeat)
  71. COMMAND_ID_HANDLER(ID_EDIT_REPLACE, OnEditReplace)
  72. END_MSG_MAP()
  73. LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
  74. {
  75. if(m_pFindReplaceDialog != NULL)
  76. {
  77. m_pFindReplaceDialog->SendMessage(WM_CLOSE);
  78. ATLASSERT(m_pFindReplaceDialog == NULL);
  79. }
  80. bHandled = FALSE;
  81. return 0;
  82. }
  83. LRESULT OnFindReplaceCmd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
  84. {
  85. T* pT = static_cast<T*>(this);
  86. TFindReplaceDialog* pDialog = TFindReplaceDialog::GetNotifier(lParam);
  87. if(pDialog == NULL)
  88. {
  89. ATLASSERT(FALSE);
  90. ::MessageBeep(MB_ICONERROR);
  91. return 1;
  92. }
  93. ATLASSERT(pDialog == m_pFindReplaceDialog);
  94. LPFINDREPLACE findReplace = (LPFINDREPLACE)lParam;
  95. if((m_pFindReplaceDialog != NULL) && (findReplace != NULL))
  96. {
  97. if(pDialog->FindNext())
  98. {
  99. pT->OnFindNext(pDialog->GetFindString(), pDialog->SearchDown(),
  100. pDialog->MatchCase(), pDialog->MatchWholeWord());
  101. }
  102. else if(pDialog->ReplaceCurrent())
  103. {
  104. pT->OnReplaceSel(pDialog->GetFindString(),
  105. pDialog->SearchDown(), pDialog->MatchCase(), pDialog->MatchWholeWord(),
  106. pDialog->GetReplaceString());
  107. }
  108. else if(pDialog->ReplaceAll())
  109. {
  110. pT->OnReplaceAll(pDialog->GetFindString(), pDialog->GetReplaceString(),
  111. pDialog->MatchCase(), pDialog->MatchWholeWord());
  112. }
  113. else if(pDialog->IsTerminating())
  114. {
  115. // Dialog is going away (but hasn't gone away yet)
  116. // OnFinalMessage will "delete this"
  117. pT->OnTerminatingFindReplaceDialog(m_pFindReplaceDialog);
  118. m_pFindReplaceDialog = NULL;
  119. }
  120. }
  121. return 0;
  122. }
  123. LRESULT OnEditFind(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
  124. {
  125. T* pT = static_cast<T*>(this);
  126. pT->FindReplace(TRUE);
  127. return 0;
  128. }
  129. LRESULT OnEditRepeat(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
  130. {
  131. T* pT = static_cast<T*>(this);
  132. // If the user is holding down SHIFT when hitting F3, we'll
  133. // search in reverse. Otherwise, we'll search forward.
  134. // (be sure to have an accelerator mapped to ID_EDIT_REPEAT
  135. // for both F3 and Shift+F3)
  136. m_bFindDown = !((::GetKeyState(VK_SHIFT) & 0x8000) == 0x8000);
  137. if(m_sFindNext.IsEmpty())
  138. {
  139. pT->FindReplace(TRUE);
  140. }
  141. else
  142. {
  143. if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown))
  144. pT->TextNotFound(m_sFindNext);
  145. }
  146. return 0;
  147. }
  148. LRESULT OnEditReplace(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& bHandled)
  149. {
  150. T* pT = static_cast<T*>(this);
  151. DWORD style = pT->GetStyle();
  152. if((style & ES_READONLY) != ES_READONLY)
  153. {
  154. pT->FindReplace(FALSE);
  155. }
  156. else
  157. {
  158. // Don't allow replace when the edit control is read only
  159. bHandled = FALSE;
  160. }
  161. return 0;
  162. }
  163. // Operations (overrideable)
  164. TFindReplaceDialog* CreateFindReplaceDialog(BOOL bFindOnly, // TRUE for Find, FALSE for FindReplace
  165. LPCTSTR lpszFindWhat,
  166. LPCTSTR lpszReplaceWith = NULL,
  167. DWORD dwFlags = FR_DOWN,
  168. HWND hWndParent = NULL)
  169. {
  170. // You can override all of this in a derived class
  171. TFindReplaceDialog* findReplaceDialog = new TFindReplaceDialog();
  172. if(findReplaceDialog == NULL)
  173. {
  174. ::MessageBeep(MB_ICONHAND);
  175. }
  176. else
  177. {
  178. HWND hWndFindReplace = findReplaceDialog->Create(bFindOnly,
  179. lpszFindWhat, lpszReplaceWith, dwFlags, hWndParent);
  180. if(hWndFindReplace == NULL)
  181. {
  182. delete findReplaceDialog;
  183. findReplaceDialog = NULL;
  184. }
  185. else
  186. {
  187. findReplaceDialog->SetActiveWindow();
  188. findReplaceDialog->ShowWindow(SW_SHOW);
  189. }
  190. }
  191. return findReplaceDialog;
  192. }
  193. void AdjustDialogPosition(HWND hWndDialog)
  194. {
  195. ATLASSERT((hWndDialog != NULL) && ::IsWindow(hWndDialog));
  196. T* pT = static_cast<T*>(this);
  197. LONG nStartChar = 0, nEndChar = 0;
  198. // Send EM_GETSEL so we can use both Edit and RichEdit
  199. // (CEdit::GetSel uses int&, and CRichEditCtrlT::GetSel uses LONG&)
  200. ::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARAM)&nEndChar);
  201. POINT point = pT->PosFromChar(nStartChar);
  202. ::ClientToScreen(pT->GetParent(), &point);
  203. RECT rect = {};
  204. ::GetWindowRect(hWndDialog, &rect);
  205. if(::PtInRect(&rect, point) != FALSE)
  206. {
  207. if(point.y > (rect.bottom - rect.top))
  208. {
  209. ::OffsetRect(&rect, 0, point.y - rect.bottom - 20);
  210. }
  211. else
  212. {
  213. int nVertExt = GetSystemMetrics(SM_CYSCREEN);
  214. if((point.y + (rect.bottom - rect.top)) < nVertExt)
  215. ::OffsetRect(&rect, 0, 40 + point.y - rect.top);
  216. }
  217. ::MoveWindow(hWndDialog, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
  218. }
  219. }
  220. DWORD GetFindReplaceDialogFlags() const
  221. {
  222. DWORD dwFlags = 0;
  223. if(m_bFindDown)
  224. dwFlags |= FR_DOWN;
  225. if(m_bMatchCase)
  226. dwFlags |= FR_MATCHCASE;
  227. if(m_bWholeWord)
  228. dwFlags |= FR_WHOLEWORD;
  229. return dwFlags;
  230. }
  231. void FindReplace(BOOL bFindOnly)
  232. {
  233. T* pT = static_cast<T*>(this);
  234. m_bFirstSearch = TRUE;
  235. if(m_pFindReplaceDialog != NULL)
  236. {
  237. if(m_bFindOnly == bFindOnly)
  238. {
  239. m_pFindReplaceDialog->SetActiveWindow();
  240. m_pFindReplaceDialog->ShowWindow(SW_SHOW);
  241. return;
  242. }
  243. else
  244. {
  245. m_pFindReplaceDialog->SendMessage(WM_CLOSE);
  246. ATLASSERT(m_pFindReplaceDialog == NULL);
  247. }
  248. }
  249. ATLASSERT(m_pFindReplaceDialog == NULL);
  250. ATL::CString findNext;
  251. pT->GetSelText(findNext);
  252. // if selection is empty or spans multiple lines use old find text
  253. if(findNext.IsEmpty() || (findNext.FindOneOf(_T("\n\r")) != -1))
  254. findNext = m_sFindNext;
  255. ATL::CString replaceWith = m_sReplaceWith;
  256. DWORD dwFlags = pT->GetFindReplaceDialogFlags();
  257. m_pFindReplaceDialog = pT->CreateFindReplaceDialog(bFindOnly,
  258. findNext, replaceWith, dwFlags, pT->operator HWND());
  259. ATLASSERT(m_pFindReplaceDialog != NULL);
  260. if(m_pFindReplaceDialog != NULL)
  261. m_bFindOnly = bFindOnly;
  262. }
  263. BOOL SameAsSelected(LPCTSTR lpszCompare, BOOL bMatchCase, BOOL /*bWholeWord*/)
  264. {
  265. T* pT = static_cast<T*>(this);
  266. // check length first
  267. size_t nLen = lstrlen(lpszCompare);
  268. LONG nStartChar = 0, nEndChar = 0;
  269. // Send EM_GETSEL so we can use both Edit and RichEdit
  270. // (CEdit::GetSel uses int&, and CRichEditCtrlT::GetSel uses LONG&)
  271. ::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARAM)&nEndChar);
  272. if(nLen != (size_t)(nEndChar - nStartChar))
  273. return FALSE;
  274. // length is the same, check contents
  275. ATL::CString selectedText;
  276. pT->GetSelText(selectedText);
  277. return (bMatchCase && (selectedText.Compare(lpszCompare) == 0)) ||
  278. (!bMatchCase && (selectedText.CompareNoCase(lpszCompare) == 0));
  279. }
  280. void TextNotFound(LPCTSTR lpszFind)
  281. {
  282. T* pT = static_cast<T*>(this);
  283. m_bFirstSearch = TRUE;
  284. pT->OnTextNotFound(lpszFind);
  285. }
  286. ATL::CString GetTranslationText(enum TranslationTextItem eItem) const
  287. {
  288. ATL::CString text;
  289. switch(eItem)
  290. {
  291. case eText_OnReplaceAllMessage:
  292. text = _T("Replaced %d occurances of \"%s\" with \"%s\"");
  293. break;
  294. case eText_OnReplaceAllTitle:
  295. text = _T("Replace All");
  296. break;
  297. case eText_OnTextNotFoundMessage:
  298. text = _T("Unable to find the text \"%s\"");
  299. break;
  300. case eText_OnTextNotFoundTitle:
  301. text = _T("Text not found");
  302. break;
  303. }
  304. return text;
  305. }
  306. // Overrideable Handlers
  307. void OnFindNext(LPCTSTR lpszFind, BOOL bFindDown, BOOL bMatchCase, BOOL bWholeWord)
  308. {
  309. T* pT = static_cast<T*>(this);
  310. m_sFindNext = lpszFind;
  311. m_bMatchCase = bMatchCase;
  312. m_bWholeWord = bWholeWord;
  313. m_bFindDown = bFindDown;
  314. if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown))
  315. pT->TextNotFound(m_sFindNext);
  316. else
  317. pT->AdjustDialogPosition(m_pFindReplaceDialog->operator HWND());
  318. }
  319. void OnReplaceSel(LPCTSTR lpszFind, BOOL bFindDown, BOOL bMatchCase, BOOL bWholeWord, LPCTSTR lpszReplace)
  320. {
  321. T* pT = static_cast<T*>(this);
  322. m_sFindNext = lpszFind;
  323. m_sReplaceWith = lpszReplace;
  324. m_bMatchCase = bMatchCase;
  325. m_bWholeWord = bWholeWord;
  326. m_bFindDown = bFindDown;
  327. if(pT->SameAsSelected(m_sFindNext, m_bMatchCase, m_bWholeWord))
  328. pT->ReplaceSel(m_sReplaceWith);
  329. if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown))
  330. pT->TextNotFound(m_sFindNext);
  331. else
  332. pT->AdjustDialogPosition(m_pFindReplaceDialog->operator HWND());
  333. }
  334. void OnReplaceAll(LPCTSTR lpszFind, LPCTSTR lpszReplace, BOOL bMatchCase, BOOL bWholeWord)
  335. {
  336. T* pT = static_cast<T*>(this);
  337. m_sFindNext = lpszFind;
  338. m_sReplaceWith = lpszReplace;
  339. m_bMatchCase = bMatchCase;
  340. m_bWholeWord = bWholeWord;
  341. m_bFindDown = TRUE;
  342. // no selection or different than what looking for
  343. if(!pT->SameAsSelected(m_sFindNext, m_bMatchCase, m_bWholeWord))
  344. {
  345. if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown))
  346. {
  347. pT->TextNotFound(m_sFindNext);
  348. return;
  349. }
  350. }
  351. pT->OnReplaceAllCoreBegin();
  352. int replaceCount=0;
  353. do
  354. {
  355. ++replaceCount;
  356. pT->ReplaceSel(m_sReplaceWith);
  357. } while(pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown));
  358. pT->OnReplaceAllCoreEnd(replaceCount);
  359. }
  360. void OnReplaceAllCoreBegin()
  361. {
  362. T* pT = static_cast<T*>(this);
  363. m_hOldCursor = ::SetCursor(::LoadCursor(NULL, IDC_WAIT));
  364. pT->HideSelection(TRUE, FALSE);
  365. }
  366. void OnReplaceAllCoreEnd(int replaceCount)
  367. {
  368. T* pT = static_cast<T*>(this);
  369. pT->HideSelection(FALSE, FALSE);
  370. ::SetCursor(m_hOldCursor);
  371. ATL::CString message = pT->GetTranslationText(eText_OnReplaceAllMessage);
  372. if(message.GetLength() > 0)
  373. {
  374. ATL::CString formattedMessage;
  375. formattedMessage.Format(message, replaceCount, (LPCTSTR)m_sFindNext, (LPCTSTR)m_sReplaceWith);
  376. if(m_pFindReplaceDialog != NULL)
  377. {
  378. m_pFindReplaceDialog->MessageBox(formattedMessage,
  379. pT->GetTranslationText(eText_OnReplaceAllTitle),
  380. MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);
  381. }
  382. else
  383. {
  384. pT->MessageBox(formattedMessage,
  385. pT->GetTranslationText(eText_OnReplaceAllTitle),
  386. MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);
  387. }
  388. }
  389. }
  390. void OnTextNotFound(LPCTSTR lpszFind)
  391. {
  392. T* pT = static_cast<T*>(this);
  393. ATL::CString message = pT->GetTranslationText(eText_OnTextNotFoundMessage);
  394. if(message.GetLength() > 0)
  395. {
  396. ATL::CString formattedMessage;
  397. formattedMessage.Format(message, lpszFind);
  398. if(m_pFindReplaceDialog != NULL)
  399. {
  400. m_pFindReplaceDialog->MessageBox(formattedMessage,
  401. pT->GetTranslationText(eText_OnTextNotFoundTitle),
  402. MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);
  403. }
  404. else
  405. {
  406. pT->MessageBox(formattedMessage,
  407. pT->GetTranslationText(eText_OnTextNotFoundTitle),
  408. MB_OK | MB_ICONINFORMATION | MB_APPLMODAL);
  409. }
  410. }
  411. else
  412. {
  413. ::MessageBeep(MB_ICONHAND);
  414. }
  415. }
  416. void OnTerminatingFindReplaceDialog(TFindReplaceDialog*& /*findReplaceDialog*/)
  417. {
  418. }
  419. };
  420. ///////////////////////////////////////////////////////////////////////////////
  421. // CEditFindReplaceImpl - Mixin class for implementing Find/Replace for CEdit
  422. // based window classes.
  423. // Chain to CEditFindReplaceImpl message map. Your class must also derive from CEdit.
  424. // Example:
  425. // class CMyEdit : public CWindowImpl<CMyEdit, CEdit>,
  426. // public CEditFindReplaceImpl<CMyEdit>
  427. // {
  428. // public:
  429. // BEGIN_MSG_MAP(CMyEdit)
  430. // // your handlers...
  431. // CHAIN_MSG_MAP_ALT(CEditFindReplaceImpl<CMyEdit>, 1)
  432. // END_MSG_MAP()
  433. // // other stuff...
  434. // };
  435. template <class T, class TFindReplaceDialog = CFindReplaceDialog>
  436. class CEditFindReplaceImpl : public CEditFindReplaceImplBase<T, TFindReplaceDialog>
  437. {
  438. protected:
  439. typedef CEditFindReplaceImpl<T, TFindReplaceDialog> thisClass;
  440. typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> baseClass;
  441. public:
  442. // Message Handlers
  443. BEGIN_MSG_MAP(thisClass)
  444. ALT_MSG_MAP(1)
  445. CHAIN_MSG_MAP_ALT(baseClass, 1)
  446. END_MSG_MAP()
  447. // Operations
  448. // Supported only for RichEdit, so this does nothing for Edit
  449. void HideSelection(BOOL /*bHide*/ = TRUE, BOOL /*bChangeStyle*/ = FALSE)
  450. {
  451. }
  452. // Operations (overrideable)
  453. BOOL FindTextSimple(LPCTSTR lpszFind, BOOL bMatchCase, BOOL bWholeWord, BOOL bFindDown = TRUE)
  454. {
  455. T* pT = static_cast<T*>(this);
  456. ATLASSERT(lpszFind != NULL);
  457. ATLASSERT(*lpszFind != _T('\0'));
  458. UINT nLen = pT->GetBufferLength();
  459. int nStartChar = 0, nEndChar = 0;
  460. pT->GetSel(nStartChar, nEndChar);
  461. UINT nStart = nStartChar;
  462. int iDir = bFindDown ? +1 : -1;
  463. // can't find a match before the first character
  464. if((nStart == 0) && (iDir < 0))
  465. return FALSE;
  466. LPCTSTR lpszText = pT->LockBuffer();
  467. bool isDBCS = false;
  468. #ifdef _MBCS
  469. CPINFO info = {};
  470. ::GetCPInfo(::GetOEMCP(), &info);
  471. isDBCS = (info.MaxCharSize > 1);
  472. #endif
  473. if(iDir < 0)
  474. {
  475. // always go back one for search backwards
  476. nStart -= int((lpszText + nStart) - ::CharPrev(lpszText, lpszText + nStart));
  477. }
  478. else if((nStartChar != nEndChar) && (pT->SameAsSelected(lpszFind, bMatchCase, bWholeWord)))
  479. {
  480. // easy to go backward/forward with SBCS
  481. #ifndef _UNICODE
  482. if(::IsDBCSLeadByte(lpszText[nStart]))
  483. nStart++;
  484. #endif
  485. nStart += iDir;
  486. }
  487. // handle search with nStart past end of buffer
  488. UINT nLenFind = ::lstrlen(lpszFind);
  489. if((nStart + nLenFind - 1) >= nLen)
  490. {
  491. if((iDir < 0) && (nLen >= nLenFind))
  492. {
  493. if(isDBCS)
  494. {
  495. // walk back to previous character n times
  496. nStart = nLen;
  497. int n = nLenFind;
  498. while(n--)
  499. {
  500. nStart -= int((lpszText + nStart) - ::CharPrev(lpszText, lpszText + nStart));
  501. }
  502. }
  503. else
  504. {
  505. // single-byte character set is easy and fast
  506. nStart = nLen - nLenFind;
  507. }
  508. ATLASSERT((nStart + nLenFind - 1) <= nLen);
  509. }
  510. else
  511. {
  512. pT->UnlockBuffer();
  513. return FALSE;
  514. }
  515. }
  516. // start the search at nStart
  517. LPCTSTR lpsz = lpszText + nStart;
  518. typedef int (WINAPI* CompareProc)(LPCTSTR str1, LPCTSTR str2);
  519. CompareProc pfnCompare = bMatchCase ? lstrcmp : lstrcmpi;
  520. if(isDBCS)
  521. {
  522. // double-byte string search
  523. LPCTSTR lpszStop = NULL;
  524. if(iDir > 0)
  525. {
  526. // start at current and find _first_ occurrance
  527. lpszStop = lpszText + nLen - nLenFind + 1;
  528. }
  529. else
  530. {
  531. // start at top and find _last_ occurrance
  532. lpszStop = lpsz;
  533. lpsz = lpszText;
  534. }
  535. LPCTSTR lpszFound = NULL;
  536. while(lpsz <= lpszStop)
  537. {
  538. #ifndef _UNICODE
  539. if(!bMatchCase || ((*lpsz == *lpszFind) && (!::IsDBCSLeadByte(*lpsz) || (lpsz[1] == lpszFind[1]))))
  540. #else
  541. if(!bMatchCase || ((*lpsz == *lpszFind) && (lpsz[1] == lpszFind[1])))
  542. #endif
  543. {
  544. LPTSTR lpch = (LPTSTR)(lpsz + nLenFind);
  545. TCHAR chSave = *lpch;
  546. *lpch = _T('\0');
  547. int nResult = (*pfnCompare)(lpsz, lpszFind);
  548. *lpch = chSave;
  549. if(nResult == 0)
  550. {
  551. lpszFound = lpsz;
  552. if(iDir > 0)
  553. break;
  554. }
  555. }
  556. lpsz = ::CharNext(lpsz);
  557. }
  558. pT->UnlockBuffer();
  559. if(lpszFound != NULL)
  560. {
  561. int n = (int)(lpszFound - lpszText);
  562. pT->SetSel(n, n + nLenFind);
  563. return TRUE;
  564. }
  565. }
  566. else
  567. {
  568. // single-byte string search
  569. UINT nCompare = 0;
  570. if(iDir < 0)
  571. nCompare = (UINT)(lpsz - lpszText) + 1;
  572. else
  573. nCompare = nLen - (UINT)(lpsz - lpszText) - nLenFind + 1;
  574. while(nCompare > 0)
  575. {
  576. ATLASSERT(lpsz >= lpszText);
  577. ATLASSERT((lpsz + nLenFind - 1) <= (lpszText + nLen - 1));
  578. LPSTR lpch = (LPSTR)(lpsz + nLenFind);
  579. char chSave = *lpch;
  580. *lpch = '\0';
  581. int nResult = (*pfnCompare)(lpsz, lpszFind);
  582. *lpch = chSave;
  583. if(nResult == 0)
  584. {
  585. pT->UnlockBuffer();
  586. int n = (int)(lpsz - lpszText);
  587. pT->SetSel(n, n + nLenFind);
  588. return TRUE;
  589. }
  590. // restore character at end of search
  591. *lpch = chSave;
  592. // move on to next substring
  593. nCompare--;
  594. lpsz += iDir;
  595. }
  596. pT->UnlockBuffer();
  597. }
  598. return FALSE;
  599. }
  600. LPCTSTR LockBuffer() const
  601. {
  602. const T* pT = static_cast<const T*>(this);
  603. ATLASSERT(pT->m_hWnd != NULL);
  604. #ifndef _UNICODE
  605. // If common controls version 6 is in use (such as via theming), then EM_GETHANDLE
  606. // will always return a UNICODE string. You're really not supposed to superclass
  607. // or subclass common controls with an ANSI windows procedure.
  608. DWORD dwMajor = 0, dwMinor = 0;
  609. HRESULT hRet = ATL::AtlGetCommCtrlVersion(&dwMajor, &dwMinor);
  610. if(SUCCEEDED(hRet) && (dwMajor >= 6))
  611. {
  612. ATLTRACE2(atlTraceUI, 0, _T("AtlFind Warning: You have compiled for MBCS/ANSI but are using common controls version 6 or later which are always Unicode.\r\n"));
  613. ATLASSERT(FALSE);
  614. }
  615. #endif // !_UNICODE
  616. HLOCAL hLocal = pT->GetHandle();
  617. ATLASSERT(hLocal != NULL);
  618. LPCTSTR lpszText = (LPCTSTR)::LocalLock(hLocal);
  619. ATLASSERT(lpszText != NULL);
  620. return lpszText;
  621. }
  622. void UnlockBuffer() const
  623. {
  624. const T* pT = static_cast<const T*>(this);
  625. ATLASSERT(pT->m_hWnd != NULL);
  626. HLOCAL hLocal = pT->GetHandle();
  627. ATLASSERT(hLocal != NULL);
  628. ::LocalUnlock(hLocal);
  629. }
  630. UINT GetBufferLength() const
  631. {
  632. const T* pT = static_cast<const T*>(this);
  633. ATLASSERT(pT->m_hWnd != NULL);
  634. UINT nLen = 0;
  635. LPCTSTR lpszText = pT->LockBuffer();
  636. if(lpszText != NULL)
  637. nLen = ::lstrlen(lpszText);
  638. pT->UnlockBuffer();
  639. return nLen;
  640. }
  641. LONG EndOfLine(LPCTSTR lpszText, UINT nLen, UINT nIndex) const
  642. {
  643. LPCTSTR lpsz = lpszText + nIndex;
  644. LPCTSTR lpszStop = lpszText + nLen;
  645. while((lpsz < lpszStop) && (*lpsz != _T('\r')))
  646. ++lpsz;
  647. return LONG(lpsz - lpszText);
  648. }
  649. LONG GetSelText(ATL::CString& strText) const
  650. {
  651. const T* pT = static_cast<const T*>(this);
  652. int nStartChar = 0, nEndChar = 0;
  653. pT->GetSel(nStartChar, nEndChar);
  654. ATLASSERT((UINT)nEndChar <= pT->GetBufferLength());
  655. LPCTSTR lpszText = pT->LockBuffer();
  656. LONG nLen = pT->EndOfLine(lpszText, nEndChar, nStartChar) - nStartChar;
  657. ATL::Checked::memcpy_s(strText.GetBuffer(nLen), nLen * sizeof(TCHAR), lpszText + nStartChar, nLen * sizeof(TCHAR));
  658. strText.ReleaseBuffer(nLen);
  659. pT->UnlockBuffer();
  660. return nLen;
  661. }
  662. };
  663. ///////////////////////////////////////////////////////////////////////////////
  664. // CRichEditFindReplaceImpl - Mixin class for implementing Find/Replace for CRichEditCtrl
  665. // based window classes.
  666. // Chain to CRichEditFindReplaceImpl message map. Your class must also derive from CRichEditCtrl.
  667. // Example:
  668. // class CMyRichEdit : public CWindowImpl<CMyRichEdit, CRichEditCtrl>,
  669. // public CRichEditFindReplaceImpl<CMyRichEdit>
  670. // {
  671. // public:
  672. // BEGIN_MSG_MAP(CMyRichEdit)
  673. // // your handlers...
  674. // CHAIN_MSG_MAP_ALT(CRichEditFindReplaceImpl<CMyRichEdit>, 1)
  675. // END_MSG_MAP()
  676. // // other stuff...
  677. // };
  678. template <class T, class TFindReplaceDialog = CFindReplaceDialog>
  679. class CRichEditFindReplaceImpl : public CEditFindReplaceImplBase<T, TFindReplaceDialog>
  680. {
  681. protected:
  682. typedef CRichEditFindReplaceImpl<T, TFindReplaceDialog> thisClass;
  683. typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> baseClass;
  684. public:
  685. BEGIN_MSG_MAP(thisClass)
  686. ALT_MSG_MAP(1)
  687. CHAIN_MSG_MAP_ALT(baseClass, 1)
  688. END_MSG_MAP()
  689. // Operations (overrideable)
  690. BOOL FindTextSimple(LPCTSTR lpszFind, BOOL bMatchCase, BOOL bWholeWord, BOOL bFindDown = TRUE)
  691. {
  692. T* pT = static_cast<T*>(this);
  693. ATLASSERT(lpszFind != NULL);
  694. FINDTEXTEX ft = {};
  695. pT->GetSel(ft.chrg);
  696. if(this->m_bFirstSearch)
  697. {
  698. if(bFindDown)
  699. this->m_nInitialSearchPos = ft.chrg.cpMin;
  700. else
  701. this->m_nInitialSearchPos = ft.chrg.cpMax;
  702. this->m_bFirstSearch = FALSE;
  703. }
  704. ft.lpstrText = (LPTSTR)lpszFind;
  705. if(ft.chrg.cpMin != ft.chrg.cpMax) // i.e. there is a selection
  706. {
  707. if(bFindDown)
  708. {
  709. ft.chrg.cpMin++;
  710. }
  711. else
  712. {
  713. // won't wraparound backwards
  714. ft.chrg.cpMin = __max(ft.chrg.cpMin, 0);
  715. }
  716. }
  717. DWORD dwFlags = bMatchCase ? FR_MATCHCASE : 0;
  718. dwFlags |= bWholeWord ? FR_WHOLEWORD : 0;
  719. ft.chrg.cpMax = pT->GetTextLength() + this->m_nInitialSearchPos;
  720. if(bFindDown)
  721. {
  722. if(this->m_nInitialSearchPos >= 0)
  723. ft.chrg.cpMax = pT->GetTextLength();
  724. dwFlags |= FR_DOWN;
  725. ATLASSERT(ft.chrg.cpMax >= ft.chrg.cpMin);
  726. }
  727. else
  728. {
  729. if(this->m_nInitialSearchPos >= 0)
  730. ft.chrg.cpMax = 0;
  731. dwFlags &= ~FR_DOWN;
  732. ATLASSERT(ft.chrg.cpMax <= ft.chrg.cpMin);
  733. }
  734. BOOL bRet = FALSE;
  735. if(pT->FindAndSelect(dwFlags, ft) != -1)
  736. {
  737. bRet = TRUE; // we found the text
  738. }
  739. else if(this->m_nInitialSearchPos > 0)
  740. {
  741. // if the original starting point was not the beginning
  742. // of the buffer and we haven't already been here
  743. if(bFindDown)
  744. {
  745. ft.chrg.cpMin = 0;
  746. ft.chrg.cpMax = this->m_nInitialSearchPos;
  747. }
  748. else
  749. {
  750. ft.chrg.cpMin = pT->GetTextLength();
  751. ft.chrg.cpMax = this->m_nInitialSearchPos;
  752. }
  753. this->m_nInitialSearchPos = this->m_nInitialSearchPos - pT->GetTextLength();
  754. bRet = (pT->FindAndSelect(dwFlags, ft) != -1) ? TRUE : FALSE;
  755. }
  756. return bRet;
  757. }
  758. long FindAndSelect(DWORD dwFlags, FINDTEXTEX& ft)
  759. {
  760. T* pT = static_cast<T*>(this);
  761. LONG index = pT->FindText(dwFlags, ft);
  762. if(index != -1) // i.e. we found something
  763. pT->SetSel(ft.chrgText);
  764. return index;
  765. }
  766. };
  767. } // namespace WTL
  768. #endif // __ATLFIND_H__