1 // Windows Template Library - WTL version 9.10 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 9 #ifndef __ATLFIND_H__ 10 #define __ATLFIND_H__ 11 12 #pragma once 13 14 #ifdef _WIN32_WCE 15 #error atlfind.h is not supported on Windows CE 16 #endif 17 18 #ifndef __ATLCTRLS_H__ 19 #error atlfind.h requires atlctrls.h to be included first 20 #endif 21 22 #ifndef __ATLDLGS_H__ 23 #error atlfind.h requires atldlgs.h to be included first 24 #endif 25 26 #if !((defined(__ATLMISC_H__) && defined(_WTL_USE_CSTRING)) || defined(__ATLSTR_H__)) 27 #error atlfind.h requires CString (either from ATL's atlstr.h or WTL's atlmisc.h with _WTL_USE_CSTRING) 28 #endif 29 30 31 /////////////////////////////////////////////////////////////////////////////// 32 // Classes in this file: 33 // 34 // CEditFindReplaceImplBase<T, TFindReplaceDialog> 35 // CEditFindReplaceImpl<T, TFindReplaceDialog> 36 // CRichEditFindReplaceImpl<T, TFindReplaceDialog> 37 38 39 namespace WTL 40 { 41 42 /////////////////////////////////////////////////////////////////////////////// 43 // CEditFindReplaceImplBase - Base class for mixin classes that 44 // help implement Find/Replace for CEdit or CRichEditCtrl based window classes. 45 46 template <class T, class TFindReplaceDialog = CFindReplaceDialog> 47 class CEditFindReplaceImplBase 48 { 49 protected: 50 // Typedefs 51 typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> thisClass; 52 53 // Data members 54 TFindReplaceDialog* m_pFindReplaceDialog; 55 _CSTRING_NS::CString m_sFindNext, m_sReplaceWith; 56 BOOL m_bFindOnly, m_bFirstSearch, m_bMatchCase, m_bWholeWord, m_bFindDown; 57 LONG m_nInitialSearchPos; 58 HCURSOR m_hOldCursor; 59 60 // Enumerations 61 enum TranslationTextItem 62 { 63 eText_OnReplaceAllMessage = 0, 64 eText_OnReplaceAllTitle = 1, 65 eText_OnTextNotFoundMessage = 2, 66 eText_OnTextNotFoundTitle = 3 67 }; 68 69 public: 70 // Constructors CEditFindReplaceImplBase()71 CEditFindReplaceImplBase() : 72 m_pFindReplaceDialog(NULL), 73 m_bFindOnly(TRUE), 74 m_bFirstSearch(TRUE), 75 m_bMatchCase(FALSE), 76 m_bWholeWord(FALSE), 77 m_bFindDown(TRUE), 78 m_nInitialSearchPos(0), 79 m_hOldCursor(NULL) 80 { 81 } 82 83 // Message Handlers 84 BEGIN_MSG_MAP(thisClass) 85 ALT_MSG_MAP(1) MESSAGE_HANDLER(WM_DESTROY,OnDestroy)86 MESSAGE_HANDLER(WM_DESTROY, OnDestroy) 87 MESSAGE_HANDLER(TFindReplaceDialog::GetFindReplaceMsg(), OnFindReplaceCmd) 88 COMMAND_ID_HANDLER(ID_EDIT_FIND, OnEditFind) 89 COMMAND_ID_HANDLER(ID_EDIT_REPEAT, OnEditRepeat) 90 COMMAND_ID_HANDLER(ID_EDIT_REPLACE, OnEditReplace) 91 END_MSG_MAP() 92 93 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) 94 { 95 if(m_pFindReplaceDialog != NULL) 96 { 97 m_pFindReplaceDialog->SendMessage(WM_CLOSE); 98 ATLASSERT(m_pFindReplaceDialog == NULL); 99 } 100 101 bHandled = FALSE; 102 return 0; 103 } 104 OnFindReplaceCmd(UINT,WPARAM,LPARAM lParam,BOOL &)105 LRESULT OnFindReplaceCmd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/) 106 { 107 T* pT = static_cast<T*>(this); 108 109 TFindReplaceDialog* pDialog = TFindReplaceDialog::GetNotifier(lParam); 110 if(pDialog == NULL) 111 { 112 ATLASSERT(FALSE); 113 ::MessageBeep(MB_ICONERROR); 114 return 1; 115 } 116 ATLASSERT(pDialog == m_pFindReplaceDialog); 117 118 LPFINDREPLACE findReplace = (LPFINDREPLACE)lParam; 119 if((m_pFindReplaceDialog != NULL) && (findReplace != NULL)) 120 { 121 if(pDialog->FindNext()) 122 { 123 pT->OnFindNext(pDialog->GetFindString(), pDialog->SearchDown(), 124 pDialog->MatchCase(), pDialog->MatchWholeWord()); 125 } 126 else if(pDialog->ReplaceCurrent()) 127 { 128 pT->OnReplaceSel(pDialog->GetFindString(), 129 pDialog->SearchDown(), pDialog->MatchCase(), pDialog->MatchWholeWord(), 130 pDialog->GetReplaceString()); 131 } 132 else if(pDialog->ReplaceAll()) 133 { 134 pT->OnReplaceAll(pDialog->GetFindString(), pDialog->GetReplaceString(), 135 pDialog->MatchCase(), pDialog->MatchWholeWord()); 136 } 137 else if(pDialog->IsTerminating()) 138 { 139 // Dialog is going away (but hasn't gone away yet) 140 // OnFinalMessage will "delete this" 141 pT->OnTerminatingFindReplaceDialog(m_pFindReplaceDialog); 142 m_pFindReplaceDialog = NULL; 143 } 144 } 145 146 return 0; 147 } 148 OnEditFind(WORD,WORD,HWND,BOOL &)149 LRESULT OnEditFind(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 150 { 151 T* pT = static_cast<T*>(this); 152 pT->FindReplace(TRUE); 153 154 return 0; 155 } 156 OnEditRepeat(WORD,WORD,HWND,BOOL &)157 LRESULT OnEditRepeat(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) 158 { 159 T* pT = static_cast<T*>(this); 160 161 // If the user is holding down SHIFT when hitting F3, we'll 162 // search in reverse. Otherwise, we'll search forward. 163 // (be sure to have an accelerator mapped to ID_EDIT_REPEAT 164 // for both F3 and Shift+F3) 165 m_bFindDown = !((::GetKeyState(VK_SHIFT) & 0x8000) == 0x8000); 166 167 if(m_sFindNext.IsEmpty()) 168 { 169 pT->FindReplace(TRUE); 170 } 171 else 172 { 173 if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown)) 174 pT->TextNotFound(m_sFindNext); 175 } 176 177 return 0; 178 } 179 OnEditReplace(WORD,WORD,HWND,BOOL & bHandled)180 LRESULT OnEditReplace(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& bHandled) 181 { 182 T* pT = static_cast<T*>(this); 183 184 DWORD style = pT->GetStyle(); 185 if((style & ES_READONLY) != ES_READONLY) 186 { 187 pT->FindReplace(FALSE); 188 } 189 else 190 { 191 // Don't allow replace when the edit control is read only 192 bHandled = FALSE; 193 } 194 195 return 0; 196 } 197 198 // Operations (overrideable) 199 TFindReplaceDialog* CreateFindReplaceDialog(BOOL bFindOnly, // TRUE for Find, FALSE for FindReplace 200 LPCTSTR lpszFindWhat, 201 LPCTSTR lpszReplaceWith = NULL, 202 DWORD dwFlags = FR_DOWN, 203 HWND hWndParent = NULL) 204 { 205 // You can override all of this in a derived class 206 207 TFindReplaceDialog* findReplaceDialog = new TFindReplaceDialog(); 208 if(findReplaceDialog == NULL) 209 { 210 ::MessageBeep(MB_ICONHAND); 211 } 212 else 213 { 214 HWND hWndFindReplace = findReplaceDialog->Create(bFindOnly, 215 lpszFindWhat, lpszReplaceWith, dwFlags, hWndParent); 216 if(hWndFindReplace == NULL) 217 { 218 delete findReplaceDialog; 219 findReplaceDialog = NULL; 220 } 221 else 222 { 223 findReplaceDialog->SetActiveWindow(); 224 findReplaceDialog->ShowWindow(SW_SHOW); 225 } 226 } 227 228 return findReplaceDialog; 229 } 230 AdjustDialogPosition(HWND hWndDialog)231 void AdjustDialogPosition(HWND hWndDialog) 232 { 233 ATLASSERT((hWndDialog != NULL) && ::IsWindow(hWndDialog)); 234 235 T* pT = static_cast<T*>(this); 236 LONG nStartChar = 0, nEndChar = 0; 237 // Send EM_GETSEL so we can use both Edit and RichEdit 238 // (CEdit::GetSel uses int&, and CRichEditCtrlT::GetSel uses LONG&) 239 ::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARAM)&nEndChar); 240 POINT point = pT->PosFromChar(nStartChar); 241 ::ClientToScreen(pT->GetParent(), &point); 242 CRect rect; 243 ::GetWindowRect(hWndDialog, &rect); 244 if(rect.PtInRect(point)) 245 { 246 if(point.y > rect.Height()) 247 { 248 rect.OffsetRect(0, point.y - rect.bottom - 20); 249 } 250 else 251 { 252 int nVertExt = GetSystemMetrics(SM_CYSCREEN); 253 if(point.y + rect.Height() < nVertExt) 254 rect.OffsetRect(0, 40 + point.y - rect.top); 255 } 256 257 ::MoveWindow(hWndDialog, rect.left, rect.top, rect.Width(), rect.Height(), TRUE); 258 } 259 } 260 GetFindReplaceDialogFlags(void)261 DWORD GetFindReplaceDialogFlags(void) const 262 { 263 DWORD dwFlags = 0; 264 if(m_bFindDown) 265 dwFlags |= FR_DOWN; 266 if(m_bMatchCase) 267 dwFlags |= FR_MATCHCASE; 268 if(m_bWholeWord) 269 dwFlags |= FR_WHOLEWORD; 270 271 return dwFlags; 272 } 273 FindReplace(BOOL bFindOnly)274 void FindReplace(BOOL bFindOnly) 275 { 276 T* pT = static_cast<T*>(this); 277 m_bFirstSearch = TRUE; 278 if(m_pFindReplaceDialog != NULL) 279 { 280 if(m_bFindOnly == bFindOnly) 281 { 282 m_pFindReplaceDialog->SetActiveWindow(); 283 m_pFindReplaceDialog->ShowWindow(SW_SHOW); 284 return; 285 } 286 else 287 { 288 m_pFindReplaceDialog->SendMessage(WM_CLOSE); 289 ATLASSERT(m_pFindReplaceDialog == NULL); 290 } 291 } 292 293 ATLASSERT(m_pFindReplaceDialog == NULL); 294 295 _CSTRING_NS::CString findNext; 296 pT->GetSelText(findNext); 297 // if selection is empty or spans multiple lines use old find text 298 if(findNext.IsEmpty() || (findNext.FindOneOf(_T("\n\r")) != -1)) 299 findNext = m_sFindNext; 300 _CSTRING_NS::CString replaceWith = m_sReplaceWith; 301 DWORD dwFlags = pT->GetFindReplaceDialogFlags(); 302 303 m_pFindReplaceDialog = pT->CreateFindReplaceDialog(bFindOnly, 304 findNext, replaceWith, dwFlags, pT->operator HWND()); 305 ATLASSERT(m_pFindReplaceDialog != NULL); 306 if(m_pFindReplaceDialog != NULL) 307 m_bFindOnly = bFindOnly; 308 } 309 SameAsSelected(LPCTSTR lpszCompare,BOOL bMatchCase,BOOL)310 BOOL SameAsSelected(LPCTSTR lpszCompare, BOOL bMatchCase, BOOL /*bWholeWord*/) 311 { 312 T* pT = static_cast<T*>(this); 313 314 // check length first 315 size_t nLen = lstrlen(lpszCompare); 316 LONG nStartChar = 0, nEndChar = 0; 317 // Send EM_GETSEL so we can use both Edit and RichEdit 318 // (CEdit::GetSel uses int&, and CRichEditCtrlT::GetSel uses LONG&) 319 ::SendMessage(pT->m_hWnd, EM_GETSEL, (WPARAM)&nStartChar, (LPARAM)&nEndChar); 320 if(nLen != (size_t)(nEndChar - nStartChar)) 321 return FALSE; 322 323 // length is the same, check contents 324 _CSTRING_NS::CString selectedText; 325 pT->GetSelText(selectedText); 326 327 return (bMatchCase && selectedText.Compare(lpszCompare) == 0) || 328 (!bMatchCase && selectedText.CompareNoCase(lpszCompare) == 0); 329 } 330 TextNotFound(LPCTSTR lpszFind)331 void TextNotFound(LPCTSTR lpszFind) 332 { 333 T* pT = static_cast<T*>(this); 334 m_bFirstSearch = TRUE; 335 pT->OnTextNotFound(lpszFind); 336 } 337 GetTranslationText(enum TranslationTextItem eItem)338 _CSTRING_NS::CString GetTranslationText(enum TranslationTextItem eItem) const 339 { 340 _CSTRING_NS::CString text; 341 switch(eItem) 342 { 343 case eText_OnReplaceAllMessage: 344 text = _T("Replaced %d occurances of \"%s\" with \"%s\""); 345 break; 346 case eText_OnReplaceAllTitle: 347 text = _T("Replace All"); 348 break; 349 case eText_OnTextNotFoundMessage: 350 text = _T("Unable to find the text \"%s\""); 351 break; 352 case eText_OnTextNotFoundTitle: 353 text = _T("Text not found"); 354 break; 355 } 356 357 return text; 358 } 359 360 // Overrideable Handlers OnFindNext(LPCTSTR lpszFind,BOOL bFindDown,BOOL bMatchCase,BOOL bWholeWord)361 void OnFindNext(LPCTSTR lpszFind, BOOL bFindDown, BOOL bMatchCase, BOOL bWholeWord) 362 { 363 T* pT = static_cast<T*>(this); 364 365 m_sFindNext = lpszFind; 366 m_bMatchCase = bMatchCase; 367 m_bWholeWord = bWholeWord; 368 m_bFindDown = bFindDown; 369 370 if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown)) 371 pT->TextNotFound(m_sFindNext); 372 else 373 pT->AdjustDialogPosition(m_pFindReplaceDialog->operator HWND()); 374 } 375 OnReplaceSel(LPCTSTR lpszFind,BOOL bFindDown,BOOL bMatchCase,BOOL bWholeWord,LPCTSTR lpszReplace)376 void OnReplaceSel(LPCTSTR lpszFind, BOOL bFindDown, BOOL bMatchCase, BOOL bWholeWord, LPCTSTR lpszReplace) 377 { 378 T* pT = static_cast<T*>(this); 379 380 m_sFindNext = lpszFind; 381 m_sReplaceWith = lpszReplace; 382 m_bMatchCase = bMatchCase; 383 m_bWholeWord = bWholeWord; 384 m_bFindDown = bFindDown; 385 386 if(pT->SameAsSelected(m_sFindNext, m_bMatchCase, m_bWholeWord)) 387 pT->ReplaceSel(m_sReplaceWith); 388 389 if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown)) 390 pT->TextNotFound(m_sFindNext); 391 else 392 pT->AdjustDialogPosition(m_pFindReplaceDialog->operator HWND()); 393 } 394 OnReplaceAll(LPCTSTR lpszFind,LPCTSTR lpszReplace,BOOL bMatchCase,BOOL bWholeWord)395 void OnReplaceAll(LPCTSTR lpszFind, LPCTSTR lpszReplace, BOOL bMatchCase, BOOL bWholeWord) 396 { 397 T* pT = static_cast<T*>(this); 398 399 m_sFindNext = lpszFind; 400 m_sReplaceWith = lpszReplace; 401 m_bMatchCase = bMatchCase; 402 m_bWholeWord = bWholeWord; 403 m_bFindDown = TRUE; 404 405 // no selection or different than what looking for 406 if(!pT->SameAsSelected(m_sFindNext, m_bMatchCase, m_bWholeWord)) 407 { 408 if(!pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown)) 409 { 410 pT->TextNotFound(m_sFindNext); 411 return; 412 } 413 } 414 415 pT->OnReplaceAllCoreBegin(); 416 417 int replaceCount=0; 418 do 419 { 420 ++replaceCount; 421 pT->ReplaceSel(m_sReplaceWith); 422 } while(pT->FindTextSimple(m_sFindNext, m_bMatchCase, m_bWholeWord, m_bFindDown)); 423 424 pT->OnReplaceAllCoreEnd(replaceCount); 425 } 426 OnReplaceAllCoreBegin()427 void OnReplaceAllCoreBegin() 428 { 429 T* pT = static_cast<T*>(this); 430 431 m_hOldCursor = ::SetCursor(::LoadCursor(NULL, IDC_WAIT)); 432 433 pT->HideSelection(TRUE, FALSE); 434 435 } 436 OnReplaceAllCoreEnd(int replaceCount)437 void OnReplaceAllCoreEnd(int replaceCount) 438 { 439 T* pT = static_cast<T*>(this); 440 pT->HideSelection(FALSE, FALSE); 441 442 ::SetCursor(m_hOldCursor); 443 444 _CSTRING_NS::CString message = pT->GetTranslationText(eText_OnReplaceAllMessage); 445 if(message.GetLength() > 0) 446 { 447 _CSTRING_NS::CString formattedMessage; 448 formattedMessage.Format(message, replaceCount, m_sFindNext, m_sReplaceWith); 449 if(m_pFindReplaceDialog != NULL) 450 { 451 m_pFindReplaceDialog->MessageBox(formattedMessage, 452 pT->GetTranslationText(eText_OnReplaceAllTitle), 453 MB_OK | MB_ICONINFORMATION | MB_APPLMODAL); 454 } 455 else 456 { 457 pT->MessageBox(formattedMessage, 458 pT->GetTranslationText(eText_OnReplaceAllTitle), 459 MB_OK | MB_ICONINFORMATION | MB_APPLMODAL); 460 } 461 } 462 } 463 OnTextNotFound(LPCTSTR lpszFind)464 void OnTextNotFound(LPCTSTR lpszFind) 465 { 466 T* pT = static_cast<T*>(this); 467 _CSTRING_NS::CString message = pT->GetTranslationText(eText_OnTextNotFoundMessage); 468 if(message.GetLength() > 0) 469 { 470 _CSTRING_NS::CString formattedMessage; 471 formattedMessage.Format(message, lpszFind); 472 if(m_pFindReplaceDialog != NULL) 473 { 474 m_pFindReplaceDialog->MessageBox(formattedMessage, 475 pT->GetTranslationText(eText_OnTextNotFoundTitle), 476 MB_OK | MB_ICONINFORMATION | MB_APPLMODAL); 477 } 478 else 479 { 480 pT->MessageBox(formattedMessage, 481 pT->GetTranslationText(eText_OnTextNotFoundTitle), 482 MB_OK | MB_ICONINFORMATION | MB_APPLMODAL); 483 } 484 } 485 else 486 { 487 ::MessageBeep(MB_ICONHAND); 488 } 489 } 490 OnTerminatingFindReplaceDialog(TFindReplaceDialog * &)491 void OnTerminatingFindReplaceDialog(TFindReplaceDialog*& /*findReplaceDialog*/) 492 { 493 } 494 }; 495 496 497 /////////////////////////////////////////////////////////////////////////////// 498 // CEditFindReplaceImpl - Mixin class for implementing Find/Replace for CEdit 499 // based window classes. 500 501 // Chain to CEditFindReplaceImpl message map. Your class must also derive from CEdit. 502 // Example: 503 // class CMyEdit : public CWindowImpl<CMyEdit, CEdit>, 504 // public CEditFindReplaceImpl<CMyEdit> 505 // { 506 // public: 507 // BEGIN_MSG_MAP(CMyEdit) 508 // // your handlers... 509 // CHAIN_MSG_MAP_ALT(CEditFindReplaceImpl<CMyEdit>, 1) 510 // END_MSG_MAP() 511 // // other stuff... 512 // }; 513 514 template <class T, class TFindReplaceDialog = CFindReplaceDialog> 515 class CEditFindReplaceImpl : public CEditFindReplaceImplBase<T, TFindReplaceDialog> 516 { 517 protected: 518 typedef CEditFindReplaceImpl<T, TFindReplaceDialog> thisClass; 519 typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> baseClass; 520 521 // Data members 522 LPTSTR m_pShadowBuffer; // Special shadow buffer only used in some cases. 523 UINT m_nShadowSize; 524 int m_bShadowBufferNeeded; // TRUE, FALSE, < 0 => Need to check 525 526 public: 527 // Constructors CEditFindReplaceImpl()528 CEditFindReplaceImpl() : 529 m_pShadowBuffer(NULL), 530 m_nShadowSize(0), 531 m_bShadowBufferNeeded(-1) 532 { 533 } 534 ~CEditFindReplaceImpl()535 virtual ~CEditFindReplaceImpl() 536 { 537 if(m_pShadowBuffer != NULL) 538 { 539 delete [] m_pShadowBuffer; 540 m_pShadowBuffer = NULL; 541 } 542 } 543 544 // Message Handlers 545 BEGIN_MSG_MAP(thisClass) 546 ALT_MSG_MAP(1) 547 CHAIN_MSG_MAP_ALT(baseClass, 1) END_MSG_MAP()548 END_MSG_MAP() 549 550 // Operations 551 // Supported only for RichEdit, so this does nothing for Edit 552 void HideSelection(BOOL /*bHide*/ = TRUE, BOOL /*bChangeStyle*/ = FALSE) 553 { 554 } 555 556 // Operations (overrideable) 557 BOOL FindTextSimple(LPCTSTR lpszFind, BOOL bMatchCase, BOOL bWholeWord, BOOL bFindDown = TRUE) 558 { 559 T* pT = static_cast<T*>(this); 560 561 ATLASSERT(lpszFind != NULL); 562 ATLASSERT(*lpszFind != _T('\0')); 563 564 UINT nLen = pT->GetBufferLength(); 565 int nStartChar = 0, nEndChar = 0; 566 pT->GetSel(nStartChar, nEndChar); 567 UINT nStart = nStartChar; 568 int iDir = bFindDown ? +1 : -1; 569 570 // can't find a match before the first character 571 if((nStart == 0) && (iDir < 0)) 572 return FALSE; 573 574 LPCTSTR lpszText = pT->LockBuffer(); 575 576 bool isDBCS = false; 577 #ifdef _MBCS 578 CPINFO info = { 0 }; 579 ::GetCPInfo(::GetOEMCP(), &info); 580 isDBCS = (info.MaxCharSize > 1); 581 #endif 582 583 if(iDir < 0) 584 { 585 // always go back one for search backwards 586 nStart -= int((lpszText + nStart) - ::CharPrev(lpszText, lpszText + nStart)); 587 } 588 else if((nStartChar != nEndChar) && (pT->SameAsSelected(lpszFind, bMatchCase, bWholeWord))) 589 { 590 // easy to go backward/forward with SBCS 591 #ifndef _UNICODE 592 if(::IsDBCSLeadByte(lpszText[nStart])) 593 nStart++; 594 #endif 595 nStart += iDir; 596 } 597 598 // handle search with nStart past end of buffer 599 UINT nLenFind = ::lstrlen(lpszFind); 600 if((nStart + nLenFind - 1) >= nLen) 601 { 602 if((iDir < 0) && (nLen >= nLenFind)) 603 { 604 if(isDBCS) 605 { 606 // walk back to previous character n times 607 nStart = nLen; 608 int n = nLenFind; 609 while(n--) 610 { 611 nStart -= int((lpszText + nStart) - ::CharPrev(lpszText, lpszText + nStart)); 612 } 613 } 614 else 615 { 616 // single-byte character set is easy and fast 617 nStart = nLen - nLenFind; 618 } 619 ATLASSERT((nStart + nLenFind - 1) <= nLen); 620 } 621 else 622 { 623 pT->UnlockBuffer(); 624 return FALSE; 625 } 626 } 627 628 // start the search at nStart 629 LPCTSTR lpsz = lpszText + nStart; 630 typedef int (WINAPI* CompareProc)(LPCTSTR str1, LPCTSTR str2); 631 CompareProc pfnCompare = bMatchCase ? lstrcmp : lstrcmpi; 632 633 if(isDBCS) 634 { 635 // double-byte string search 636 LPCTSTR lpszStop = NULL; 637 if(iDir > 0) 638 { 639 // start at current and find _first_ occurrance 640 lpszStop = lpszText + nLen - nLenFind + 1; 641 } 642 else 643 { 644 // start at top and find _last_ occurrance 645 lpszStop = lpsz; 646 lpsz = lpszText; 647 } 648 649 LPCTSTR lpszFound = NULL; 650 while(lpsz <= lpszStop) 651 { 652 #ifndef _UNICODE 653 if(!bMatchCase || (*lpsz == *lpszFind && (!::IsDBCSLeadByte(*lpsz) || lpsz[1] == lpszFind[1]))) 654 #else 655 if(!bMatchCase || (*lpsz == *lpszFind && lpsz[1] == lpszFind[1])) 656 #endif 657 { 658 LPTSTR lpch = (LPTSTR)(lpsz + nLenFind); 659 TCHAR chSave = *lpch; 660 *lpch = _T('\0'); 661 int nResult = (*pfnCompare)(lpsz, lpszFind); 662 *lpch = chSave; 663 if(nResult == 0) 664 { 665 lpszFound = lpsz; 666 if(iDir > 0) 667 break; 668 } 669 } 670 lpsz = ::CharNext(lpsz); 671 } 672 pT->UnlockBuffer(); 673 674 if(lpszFound != NULL) 675 { 676 int n = (int)(lpszFound - lpszText); 677 pT->SetSel(n, n + nLenFind); 678 return TRUE; 679 } 680 } 681 else 682 { 683 // single-byte string search 684 UINT nCompare = 0; 685 if(iDir < 0) 686 nCompare = (UINT)(lpsz - lpszText) + 1; 687 else 688 nCompare = nLen - (UINT)(lpsz - lpszText) - nLenFind + 1; 689 690 while(nCompare > 0) 691 { 692 ATLASSERT(lpsz >= lpszText); 693 ATLASSERT((lpsz + nLenFind - 1) <= (lpszText + nLen - 1)); 694 695 LPSTR lpch = (LPSTR)(lpsz + nLenFind); 696 char chSave = *lpch; 697 *lpch = '\0'; 698 int nResult = (*pfnCompare)(lpsz, lpszFind); 699 *lpch = chSave; 700 if(nResult == 0) 701 { 702 pT->UnlockBuffer(); 703 int n = (int)(lpsz - lpszText); 704 pT->SetSel(n, n + nLenFind); 705 return TRUE; 706 } 707 708 // restore character at end of search 709 *lpch = chSave; 710 711 // move on to next substring 712 nCompare--; 713 lpsz += iDir; 714 } 715 pT->UnlockBuffer(); 716 } 717 718 return FALSE; 719 } 720 LockBuffer()721 LPCTSTR LockBuffer() const 722 { 723 const T* pT = static_cast<const T*>(this); 724 ATLASSERT(pT->m_hWnd != NULL); 725 726 BOOL useShadowBuffer = pT->UseShadowBuffer(); 727 if(useShadowBuffer) 728 { 729 if((m_pShadowBuffer == NULL) || pT->GetModify()) 730 { 731 ATLASSERT((m_pShadowBuffer != NULL) || (m_nShadowSize == 0)); 732 UINT nSize = pT->GetWindowTextLength() + 1; 733 if(nSize > m_nShadowSize) 734 { 735 // need more room for shadow buffer 736 T* pThisNoConst = const_cast<T*>(pT); 737 delete[] m_pShadowBuffer; 738 pThisNoConst->m_pShadowBuffer = NULL; 739 pThisNoConst->m_nShadowSize = 0; 740 pThisNoConst->m_pShadowBuffer = new TCHAR[nSize]; 741 pThisNoConst->m_nShadowSize = nSize; 742 } 743 744 // update the shadow buffer with GetWindowText 745 ATLASSERT(m_nShadowSize >= nSize); 746 ATLASSERT(m_pShadowBuffer != NULL); 747 pT->GetWindowText(m_pShadowBuffer, nSize); 748 } 749 750 return m_pShadowBuffer; 751 } 752 753 HLOCAL hLocal = pT->GetHandle(); 754 ATLASSERT(hLocal != NULL); 755 LPCTSTR lpszText = (LPCTSTR)::LocalLock(hLocal); 756 ATLASSERT(lpszText != NULL); 757 758 return lpszText; 759 } 760 UnlockBuffer()761 void UnlockBuffer() const 762 { 763 const T* pT = static_cast<const T*>(this); 764 ATLASSERT(pT->m_hWnd != NULL); 765 766 BOOL useShadowBuffer = pT->UseShadowBuffer(); 767 if(!useShadowBuffer) 768 { 769 HLOCAL hLocal = pT->GetHandle(); 770 ATLASSERT(hLocal != NULL); 771 ::LocalUnlock(hLocal); 772 } 773 } 774 GetBufferLength()775 UINT GetBufferLength() const 776 { 777 const T* pT = static_cast<const T*>(this); 778 ATLASSERT(pT->m_hWnd != NULL); 779 780 UINT nLen = 0; 781 LPCTSTR lpszText = pT->LockBuffer(); 782 if(lpszText != NULL) 783 nLen = ::lstrlen(lpszText); 784 pT->UnlockBuffer(); 785 786 return nLen; 787 } 788 EndOfLine(LPCTSTR lpszText,UINT nLen,UINT nIndex)789 LONG EndOfLine(LPCTSTR lpszText, UINT nLen, UINT nIndex) const 790 { 791 LPCTSTR lpsz = lpszText + nIndex; 792 LPCTSTR lpszStop = lpszText + nLen; 793 while(lpsz < lpszStop && *lpsz != _T('\r')) 794 ++lpsz; 795 return LONG(lpsz - lpszText); 796 } 797 GetSelText(_CSTRING_NS::CString & strText)798 LONG GetSelText(_CSTRING_NS::CString& strText) const 799 { 800 const T* pT = static_cast<const T*>(this); 801 802 int nStartChar = 0, nEndChar = 0; 803 pT->GetSel(nStartChar, nEndChar); 804 ATLASSERT((UINT)nEndChar <= pT->GetBufferLength()); 805 LPCTSTR lpszText = pT->LockBuffer(); 806 LONG nLen = pT->EndOfLine(lpszText, nEndChar, nStartChar) - nStartChar; 807 SecureHelper::memcpy_x(strText.GetBuffer(nLen), nLen * sizeof(TCHAR), lpszText + nStartChar, nLen * sizeof(TCHAR)); 808 strText.ReleaseBuffer(nLen); 809 pT->UnlockBuffer(); 810 811 return nLen; 812 } 813 UseShadowBuffer(void)814 BOOL UseShadowBuffer(void) const 815 { 816 const T* pT = static_cast<const T*>(this); 817 818 if(pT->m_bShadowBufferNeeded < 0) 819 { 820 T* pThisNoConst = const_cast<T*>(pT); 821 822 #ifdef _versionhelpers_H_INCLUDED_ 823 OSVERSIONINFOEX ovi = { sizeof(OSVERSIONINFOEX) }; 824 ovi.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS; 825 DWORDLONG const dwlConditionMask = ::VerSetConditionMask(0, VER_PLATFORMID, VER_EQUAL); 826 bool bWin9x = (::VerifyVersionInfo(&ovi, VER_PLATFORMID, dwlConditionMask) != FALSE); 827 #else // !_versionhelpers_H_INCLUDED_ 828 OSVERSIONINFO ovi = { sizeof(OSVERSIONINFO) }; 829 ::GetVersionEx(&ovi); 830 831 bool bWin9x = (ovi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); 832 #endif // _versionhelpers_H_INCLUDED_ 833 if(bWin9x) 834 { 835 // Windows 95, 98, ME 836 // Under Win9x, it is necessary to maintain a shadow buffer. 837 // It is only updated when the control contents have been changed. 838 pThisNoConst->m_bShadowBufferNeeded = TRUE; 839 } 840 else 841 { 842 // Windows NT, 2000, XP, etc. 843 pThisNoConst->m_bShadowBufferNeeded = FALSE; 844 845 #ifndef _UNICODE 846 // On Windows XP (or later), if common controls version 6 is in use 847 // (such as via theming), then EM_GETHANDLE will always return a UNICODE string. 848 // If theming is enabled and Common Controls version 6 is in use, 849 // you're really not suppose to superclass or subclass common controls 850 // with an ANSI windows procedure (so its best to only theme if you use UNICODE). 851 // Using a shadow buffer uses GetWindowText instead, so it solves 852 // this problem for us (although it makes it a little less efficient). 853 854 #ifdef _versionhelpers_H_INCLUDED_ 855 if(::IsWindowsXPOrGreater()) 856 #else // !_versionhelpers_H_INCLUDED_ 857 if ((ovi.dwMajorVersion == 5 && ovi.dwMinorVersion >= 1) || (ovi.dwMajorVersion > 5)) 858 #endif // _versionhelpers_H_INCLUDED_ 859 { 860 DWORD dwMajor = 0, dwMinor = 0; 861 HRESULT hRet = ATL::AtlGetCommCtrlVersion(&dwMajor, &dwMinor); 862 if(SUCCEEDED(hRet)) 863 { 864 if(dwMajor >= 6) 865 { 866 pThisNoConst->m_bShadowBufferNeeded = TRUE; 867 868 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")); 869 ATLTRACE2(atlTraceUI, 0, _T("If you use common controls version 6 or later, you should only do so for UNICODE builds.\r\n")); 870 } 871 } 872 } 873 #endif // !_UNICODE 874 } 875 } 876 877 return (pT->m_bShadowBufferNeeded != FALSE); 878 } 879 }; 880 881 882 /////////////////////////////////////////////////////////////////////////////// 883 // CRichEditFindReplaceImpl - Mixin class for implementing Find/Replace for CRichEditCtrl 884 // based window classes. 885 886 // Chain to CRichEditFindReplaceImpl message map. Your class must also derive from CRichEditCtrl. 887 // Example: 888 // class CMyRichEdit : public CWindowImpl<CMyRichEdit, CRichEditCtrl>, 889 // public CRichEditFindReplaceImpl<CMyRichEdit> 890 // { 891 // public: 892 // BEGIN_MSG_MAP(CMyRichEdit) 893 // // your handlers... 894 // CHAIN_MSG_MAP_ALT(CRichEditFindReplaceImpl<CMyRichEdit>, 1) 895 // END_MSG_MAP() 896 // // other stuff... 897 // }; 898 899 template <class T, class TFindReplaceDialog = CFindReplaceDialog> 900 class CRichEditFindReplaceImpl : public CEditFindReplaceImplBase<T, TFindReplaceDialog> 901 { 902 protected: 903 typedef CRichEditFindReplaceImpl<T, TFindReplaceDialog> thisClass; 904 typedef CEditFindReplaceImplBase<T, TFindReplaceDialog> baseClass; 905 906 public: BEGIN_MSG_MAP(thisClass)907 BEGIN_MSG_MAP(thisClass) 908 ALT_MSG_MAP(1) 909 CHAIN_MSG_MAP_ALT(baseClass, 1) 910 END_MSG_MAP() 911 912 // Operations (overrideable) 913 BOOL FindTextSimple(LPCTSTR lpszFind, BOOL bMatchCase, BOOL bWholeWord, BOOL bFindDown = TRUE) 914 { 915 T* pT = static_cast<T*>(this); 916 917 ATLASSERT(lpszFind != NULL); 918 FINDTEXTEX ft = { 0 }; 919 920 pT->GetSel(ft.chrg); 921 if(m_bFirstSearch) 922 { 923 if(bFindDown) 924 m_nInitialSearchPos = ft.chrg.cpMin; 925 else 926 m_nInitialSearchPos = ft.chrg.cpMax; 927 m_bFirstSearch = FALSE; 928 } 929 930 #if (_RICHEDIT_VER >= 0x0200) 931 ft.lpstrText = (LPTSTR)lpszFind; 932 #else // !(_RICHEDIT_VER >= 0x0200) 933 USES_CONVERSION; 934 ft.lpstrText = T2A((LPTSTR)lpszFind); 935 #endif // !(_RICHEDIT_VER >= 0x0200) 936 937 if(ft.chrg.cpMin != ft.chrg.cpMax) // i.e. there is a selection 938 { 939 if(bFindDown) 940 { 941 ft.chrg.cpMin++; 942 } 943 else 944 { 945 // won't wraparound backwards 946 ft.chrg.cpMin = __max(ft.chrg.cpMin, 0); 947 } 948 } 949 950 DWORD dwFlags = bMatchCase ? FR_MATCHCASE : 0; 951 dwFlags |= bWholeWord ? FR_WHOLEWORD : 0; 952 953 ft.chrg.cpMax = pT->GetTextLength() + m_nInitialSearchPos; 954 955 if(bFindDown) 956 { 957 if(m_nInitialSearchPos >= 0) 958 ft.chrg.cpMax = pT->GetTextLength(); 959 960 dwFlags |= FR_DOWN; 961 ATLASSERT(ft.chrg.cpMax >= ft.chrg.cpMin); 962 } 963 else 964 { 965 if(m_nInitialSearchPos >= 0) 966 ft.chrg.cpMax = 0; 967 968 dwFlags &= ~FR_DOWN; 969 ATLASSERT(ft.chrg.cpMax <= ft.chrg.cpMin); 970 } 971 972 BOOL bRet = FALSE; 973 if(pT->FindAndSelect(dwFlags, ft) != -1) 974 { 975 bRet = TRUE; // we found the text 976 } 977 else if(m_nInitialSearchPos > 0) 978 { 979 // if the original starting point was not the beginning 980 // of the buffer and we haven't already been here 981 if(bFindDown) 982 { 983 ft.chrg.cpMin = 0; 984 ft.chrg.cpMax = m_nInitialSearchPos; 985 } 986 else 987 { 988 ft.chrg.cpMin = pT->GetTextLength(); 989 ft.chrg.cpMax = m_nInitialSearchPos; 990 } 991 m_nInitialSearchPos = m_nInitialSearchPos - pT->GetTextLength(); 992 993 bRet = (pT->FindAndSelect(dwFlags, ft) != -1) ? TRUE : FALSE; 994 } 995 996 return bRet; 997 } 998 FindAndSelect(DWORD dwFlags,FINDTEXTEX & ft)999 long FindAndSelect(DWORD dwFlags, FINDTEXTEX& ft) 1000 { 1001 T* pT = static_cast<T*>(this); 1002 LONG index = pT->FindText(dwFlags, ft); 1003 if(index != -1) // i.e. we found something 1004 pT->SetSel(ft.chrgText); 1005 1006 return index; 1007 } 1008 }; 1009 1010 }; // namespace WTL 1011 1012 #endif // __ATLFIND_H__ 1013