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