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