Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

599 lines
15 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "stdafx.h"
  8. #include "History.h"
  9. #include "GlobalFunctions.h"
  10. #include "MapDoc.h"
  11. #include "MapWorld.h"
  12. #include "SearchReplaceDlg.h"
  13. #include "hammer.h"
  14. #include "Selection.h"
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include <tier0/memdbgon.h>
  17. //
  18. // Context data for a FindFirstObject/FindNextObject session.
  19. //
  20. struct FindObject_t
  21. {
  22. //
  23. // Where to look: in the world or in the selection set.
  24. //
  25. FindReplaceIn_t eFindIn;
  26. CMapWorld *pWorld;
  27. EnumChildrenPos_t WorldPos; // A position in the world tree for world searches.
  28. CMapObjectRefList SelectionList; // A copy of the selection list for selection only searches.
  29. int nSelectionIndex; // The index into the selection list for iterating the selection list.
  30. //
  31. // What to look for.
  32. //
  33. CString strFindText;
  34. bool bVisiblesOnly;
  35. bool bCaseSensitive;
  36. bool bWholeWord;
  37. };
  38. CMapClass *FindNextObject(FindObject_t &FindObject);
  39. bool FindCheck(CMapClass *pObject, FindObject_t &FindObject);
  40. //-----------------------------------------------------------------------------
  41. // Purpose: Returns true if the string matches the search criteria, false if not.
  42. // Input : pszString - String to check.
  43. // FindObject - Search criteria, including string to search for.
  44. //-----------------------------------------------------------------------------
  45. bool MatchString(const char *pszString, FindObject_t &FindObject)
  46. {
  47. if (FindObject.bWholeWord)
  48. {
  49. if (FindObject.bCaseSensitive)
  50. {
  51. return (!strcmp(pszString, FindObject.strFindText));
  52. }
  53. return (!stricmp(pszString, FindObject.strFindText));
  54. }
  55. if (FindObject.bCaseSensitive)
  56. {
  57. return (strstr(pszString, FindObject.strFindText) != NULL);
  58. }
  59. return (Q_stristr(pszString, FindObject.strFindText) != NULL);
  60. }
  61. //-----------------------------------------------------------------------------
  62. // Purpose: Returns true if the string matches the search criteria, false if not.
  63. // Input : pszIn -
  64. // pszOut - String to check.
  65. // FindObject - Search criteria, including string to search for.
  66. //-----------------------------------------------------------------------------
  67. bool ReplaceString(char *pszOut, const char *pszIn, FindObject_t &FindObject, const char *pszReplace)
  68. {
  69. //
  70. // Whole matches are simple, just strcpy the replacement string into the out buffer.
  71. //
  72. if (FindObject.bWholeWord)
  73. {
  74. if (FindObject.bCaseSensitive && (!strcmp(pszIn, FindObject.strFindText)))
  75. {
  76. strcpy(pszOut, pszReplace);
  77. return true;
  78. }
  79. if (!stricmp(pszIn, FindObject.strFindText))
  80. {
  81. strcpy(pszOut, pszReplace);
  82. return true;
  83. }
  84. }
  85. //
  86. // Partial matches are a little tougher.
  87. //
  88. const char *pszStart = NULL;
  89. if (FindObject.bCaseSensitive)
  90. {
  91. pszStart = strstr(pszIn, FindObject.strFindText);
  92. }
  93. else
  94. {
  95. pszStart = Q_stristr(pszIn, FindObject.strFindText);
  96. }
  97. if (pszStart != NULL)
  98. {
  99. int nOffset = pszStart - pszIn;
  100. strncpy(pszOut, pszIn, nOffset);
  101. pszOut += nOffset;
  102. pszIn += nOffset + strlen(FindObject.strFindText);
  103. strcpy(pszOut, pszReplace);
  104. pszOut += strlen(pszReplace);
  105. strcpy(pszOut, pszIn);
  106. return true;
  107. }
  108. return false;
  109. }
  110. //-----------------------------------------------------------------------------
  111. // Purpose: Begins a Find or Find/Replace operation.
  112. //-----------------------------------------------------------------------------
  113. CMapClass *FindFirstObject(FindObject_t &FindObject)
  114. {
  115. CMapClass *pObject = NULL;
  116. if (FindObject.eFindIn == FindInWorld)
  117. {
  118. // Search the entire world.
  119. pObject = FindObject.pWorld->GetFirstDescendent(FindObject.WorldPos);
  120. }
  121. else
  122. {
  123. // Search the selection only.
  124. if (FindObject.SelectionList.Count())
  125. {
  126. pObject = FindObject.SelectionList.Element(0);
  127. FindObject.nSelectionIndex = 1;
  128. }
  129. }
  130. if (!pObject)
  131. {
  132. return NULL;
  133. }
  134. if (FindCheck(pObject, FindObject))
  135. {
  136. return pObject;
  137. }
  138. return FindNextObject(FindObject);
  139. }
  140. //-----------------------------------------------------------------------------
  141. // Purpose:
  142. // Input : pObject -
  143. //-----------------------------------------------------------------------------
  144. CMapClass *FindNextObject(FindObject_t &FindObject)
  145. {
  146. while (true)
  147. {
  148. CMapClass *pObject = NULL;
  149. if (FindObject.eFindIn == FindInWorld)
  150. {
  151. // Search the entire world.
  152. pObject = FindObject.pWorld->GetNextDescendent(FindObject.WorldPos);
  153. }
  154. else
  155. {
  156. // Search the selection only.
  157. if (FindObject.nSelectionIndex < FindObject.SelectionList.Count())
  158. {
  159. pObject = FindObject.SelectionList.Element(FindObject.nSelectionIndex);
  160. FindObject.nSelectionIndex++;
  161. }
  162. }
  163. if ((!pObject) || FindCheck(pObject, FindObject))
  164. {
  165. return pObject;
  166. }
  167. }
  168. }
  169. //-----------------------------------------------------------------------------
  170. // Purpose:
  171. // Input : pObject -
  172. // FindObject -
  173. // Output : Returns true if the object matches the search criteria, false if not.
  174. //-----------------------------------------------------------------------------
  175. bool FindCheck(CMapClass *pObject, FindObject_t &FindObject)
  176. {
  177. CMapEntity *pEntity = dynamic_cast <CMapEntity *>(pObject);
  178. if (!pEntity)
  179. {
  180. return false;
  181. }
  182. if (FindObject.bVisiblesOnly && !pObject->IsVisible())
  183. {
  184. return false;
  185. }
  186. //
  187. // Search keyvalues.
  188. //
  189. for ( int i=pEntity->GetFirstKeyValue(); i != pEntity->GetInvalidKeyValue(); i=pEntity->GetNextKeyValue( i ) )
  190. {
  191. const char *pszValue = pEntity->GetKeyValue(i);
  192. if (pszValue && MatchString(pszValue, FindObject))
  193. {
  194. return true;
  195. }
  196. }
  197. //
  198. // Search connections.
  199. //
  200. int nConnCount = pEntity->Connections_GetCount();
  201. for (int i = 0; i < nConnCount; i++)
  202. {
  203. CEntityConnection *pConn = pEntity->Connections_Get(i);
  204. if (pConn)
  205. {
  206. if (MatchString(pConn->GetTargetName(), FindObject) ||
  207. MatchString(pConn->GetParam(), FindObject))
  208. {
  209. return true;
  210. }
  211. }
  212. }
  213. return false;
  214. }
  215. //-----------------------------------------------------------------------------
  216. // Purpose:
  217. // Input : pLastFound -
  218. // FindObject -
  219. // pszReplaceText -
  220. // Output : Returns the number of occurrences of the find text that were replaced.
  221. //-----------------------------------------------------------------------------
  222. int FindReplace(CMapEntity *pEntity, FindObject_t &FindObject, const char *pszReplace)
  223. {
  224. int nReplacedCount = 0;
  225. //
  226. // Replace keyvalues.
  227. //
  228. for ( int i=pEntity->GetFirstKeyValue(); i != pEntity->GetInvalidKeyValue(); i=pEntity->GetNextKeyValue( i ) )
  229. {
  230. const char *pszValue = pEntity->GetKeyValue(i);
  231. char szNewValue[MAX_PATH];
  232. if (pszValue && ReplaceString(szNewValue, pszValue, FindObject, pszReplace))
  233. {
  234. const char *pszKey = pEntity->GetKey(i);
  235. if (pszKey)
  236. {
  237. pEntity->SetKeyValue(pszKey, szNewValue);
  238. nReplacedCount++;
  239. }
  240. }
  241. }
  242. //
  243. // Replace connections.
  244. //
  245. int nConnCount = pEntity->Connections_GetCount();
  246. for (int i = 0; i < nConnCount; i++)
  247. {
  248. CEntityConnection *pConn = pEntity->Connections_Get(i);
  249. if (pConn)
  250. {
  251. char szNewValue[MAX_PATH];
  252. if (ReplaceString(szNewValue, pConn->GetTargetName(), FindObject, pszReplace))
  253. {
  254. pConn->SetTargetName(szNewValue);
  255. nReplacedCount++;
  256. }
  257. if (ReplaceString(szNewValue, pConn->GetParam(), FindObject, pszReplace))
  258. {
  259. pConn->SetParam(szNewValue);
  260. nReplacedCount++;
  261. }
  262. }
  263. }
  264. return nReplacedCount;
  265. }
  266. BEGIN_MESSAGE_MAP(CSearchReplaceDlg, CDialog)
  267. //{{AFX_MSG_MAP(CSearchReplaceDlg)
  268. ON_WM_SHOWWINDOW()
  269. ON_COMMAND_EX(IDC_FIND_NEXT, OnFindReplace)
  270. ON_COMMAND_EX(IDC_REPLACE, OnFindReplace)
  271. ON_COMMAND_EX(IDC_REPLACE_ALL, OnFindReplace)
  272. //}}AFX_MSG_MAP
  273. END_MESSAGE_MAP()
  274. //-----------------------------------------------------------------------------
  275. // Purpose:
  276. // Input : pParent -
  277. //-----------------------------------------------------------------------------
  278. CSearchReplaceDlg::CSearchReplaceDlg(CWnd *pParent)
  279. : CDialog(CSearchReplaceDlg::IDD, pParent)
  280. {
  281. m_bNewSearch = true;
  282. //{{AFX_DATA_INIT(CSearchReplaceDlg)
  283. m_bVisiblesOnly = FALSE;
  284. m_nFindIn = FindInWorld;
  285. m_bWholeWord = FALSE;
  286. m_bCaseSensitive = FALSE;
  287. //}}AFX_DATA_INIT
  288. }
  289. //-----------------------------------------------------------------------------
  290. // Purpose:
  291. // Output : Returns TRUE on success, FALSE on failure.
  292. //-----------------------------------------------------------------------------
  293. BOOL CSearchReplaceDlg::Create(CWnd *pwndParent)
  294. {
  295. return CDialog::Create(CSearchReplaceDlg::IDD, pwndParent);
  296. }
  297. //-----------------------------------------------------------------------------
  298. // Purpose:
  299. // Input : pDX -
  300. //-----------------------------------------------------------------------------
  301. void CSearchReplaceDlg::DoDataExchange(CDataExchange* pDX)
  302. {
  303. CDialog::DoDataExchange(pDX);
  304. //{{AFX_DATA_MAP(CSearchReplaceDlg)
  305. DDX_Check(pDX, IDC_VISIBLES_ONLY, m_bVisiblesOnly);
  306. DDX_Check(pDX, IDC_WHOLE_WORD, m_bWholeWord);
  307. DDX_Check(pDX, IDC_CASE_SENSITIVE, m_bCaseSensitive);
  308. DDX_Text(pDX, IDC_FIND_TEXT, m_strFindText);
  309. DDX_Text(pDX, IDC_REPLACE_TEXT, m_strReplaceText);
  310. DDX_Radio(pDX, IDC_SELECTION, m_nFindIn);
  311. //}}AFX_DATA_MAP
  312. }
  313. //-----------------------------------------------------------------------------
  314. // Purpose:
  315. //-----------------------------------------------------------------------------
  316. void CSearchReplaceDlg::OnCancel(void)
  317. {
  318. ShowWindow(SW_HIDE);
  319. }
  320. //-----------------------------------------------------------------------------
  321. // Purpose: Fill out the find criteria from the dialog controls.
  322. // Input : FindObject -
  323. //-----------------------------------------------------------------------------
  324. void CSearchReplaceDlg::GetFindCriteria(FindObject_t &FindObject, CMapDoc *pDoc)
  325. {
  326. FindObject.pWorld = pDoc->GetMapWorld();
  327. if (m_nFindIn == FindInSelection)
  328. {
  329. FindObject.eFindIn = FindInSelection;
  330. FindObject.SelectionList.RemoveAll();
  331. const CMapObjectList *pSelection = pDoc->GetSelection()->GetList();
  332. for (int i = 0; i < pSelection->Count(); i++)
  333. {
  334. CUtlReference< CMapClass > object = pSelection->Element(i);
  335. if ( object->IsGroup() )
  336. {
  337. // If it's a group, get all the entities in the group.
  338. const CMapObjectList *pChildren = object->GetChildren();
  339. FOR_EACH_OBJ( *pChildren, pos )
  340. {
  341. FindObject.SelectionList.AddToTail( pChildren->Element(pos) );
  342. }
  343. }
  344. else
  345. {
  346. FindObject.SelectionList.AddToTail( object );
  347. }
  348. }
  349. }
  350. else
  351. {
  352. FindObject.eFindIn = FindInWorld;
  353. }
  354. FindObject.strFindText = m_strFindText;
  355. FindObject.bVisiblesOnly = (m_bVisiblesOnly == TRUE);
  356. FindObject.bWholeWord = (m_bWholeWord == TRUE);
  357. FindObject.bCaseSensitive = (m_bCaseSensitive == TRUE);
  358. }
  359. //-----------------------------------------------------------------------------
  360. // Purpose: Called when they hit the Find, the Replace, or the Replace All button.
  361. // Input : uCmd - The ID of the button the user hit, IDC_FIND_NEXT or IDC_REPLACE.
  362. // Output : Returns TRUE to indicate that the message was handled.
  363. //-----------------------------------------------------------------------------
  364. BOOL CSearchReplaceDlg::OnFindReplace(UINT uCmd)
  365. {
  366. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  367. if (!pDoc)
  368. {
  369. return TRUE;
  370. }
  371. static FindObject_t FindObject;
  372. static CMapClass *pLastFound = NULL;
  373. static int nReplaceCount = 0;
  374. FindObject_t TempFindObject;
  375. bool bDone = false;
  376. UpdateData();
  377. GetFindCriteria(TempFindObject, pDoc);
  378. if ( strcmp(TempFindObject.strFindText, FindObject.strFindText) != 0 )
  379. {
  380. m_bNewSearch = true;
  381. }
  382. do
  383. {
  384. CMapClass *pObject = NULL;
  385. if (m_bNewSearch)
  386. {
  387. //
  388. // New search. Fetch the data from the controls.
  389. //
  390. UpdateData();
  391. GetFindCriteria(FindObject, pDoc);
  392. //
  393. // We have to keep track of the last object in the iteration for replacement,
  394. // because replacement is done when me advance to the next object.
  395. //
  396. pLastFound = NULL;
  397. nReplaceCount = 0;
  398. pObject = FindFirstObject(FindObject);
  399. }
  400. else
  401. {
  402. pObject = FindNextObject(FindObject);
  403. }
  404. //
  405. // Replace All is undone as single operation. Mark the undo position the first time
  406. // we find a match during a Replace All.
  407. //
  408. if (m_bNewSearch && (uCmd == IDC_REPLACE_ALL) && pObject)
  409. {
  410. GetHistory()->MarkUndoPosition(pDoc->GetSelection()->GetList(), "Replace Text");
  411. }
  412. //
  413. // If we have an object to do the replace on, do the replace.
  414. //
  415. if (pLastFound && ((uCmd == IDC_REPLACE) || (uCmd == IDC_REPLACE_ALL)))
  416. {
  417. if (uCmd == IDC_REPLACE)
  418. {
  419. // Allow for undo each time we do a Replace.
  420. GetHistory()->MarkUndoPosition(NULL, "Replace Text");
  421. }
  422. //
  423. // Do the replace on the last matching object we found. This lets the user see what
  424. // object will be modified before it is done.
  425. //
  426. GetHistory()->Keep(pLastFound);
  427. nReplaceCount += FindReplace((CMapEntity *)pLastFound, FindObject, m_strReplaceText);
  428. GetDlgItem(IDCANCEL)->SetWindowText("Close");
  429. }
  430. if (pObject)
  431. {
  432. //
  433. // We found an object that satisfies our search.
  434. //
  435. if ((uCmd == IDC_FIND_NEXT) || (uCmd == IDC_REPLACE))
  436. {
  437. //
  438. // Highlight the match.
  439. //
  440. pDoc->SelectObject(pObject, scClear | scSelect);
  441. pDoc->CenterViewsOnSelection();
  442. }
  443. //
  444. // Stop after one match unless we are doing a Replace All.
  445. //
  446. if (uCmd != IDC_REPLACE_ALL)
  447. {
  448. bDone = true;
  449. }
  450. m_bNewSearch = false;
  451. pLastFound = pObject;
  452. }
  453. else
  454. {
  455. //
  456. // No more objects in the search set match our criteria.
  457. //
  458. if ((m_bNewSearch) || (uCmd != IDC_REPLACE_ALL))
  459. {
  460. CString str;
  461. str.Format("Finished searching for '%s'.", m_strFindText);
  462. MessageBox(str, "Find/Replace Text", MB_OK);
  463. // TODO: put the old selection back
  464. }
  465. else if (uCmd == IDC_REPLACE_ALL)
  466. {
  467. CString str;
  468. str.Format("Replaced %d occurrences of the string '%s' with '%s'.", nReplaceCount, m_strFindText, m_strReplaceText);
  469. MessageBox(str, "Find/Replace Text", MB_OK);
  470. }
  471. m_bNewSearch = true;
  472. bDone = true;
  473. }
  474. } while (!bDone);
  475. return TRUE;
  476. }
  477. //-----------------------------------------------------------------------------
  478. // Purpose:
  479. //-----------------------------------------------------------------------------
  480. void CSearchReplaceDlg::OnOK()
  481. {
  482. }
  483. //-----------------------------------------------------------------------------
  484. // Purpose: Called any time we are hidden or shown.
  485. // Input : bShow -
  486. // nStatus -
  487. //-----------------------------------------------------------------------------
  488. void CSearchReplaceDlg::OnShowWindow(BOOL bShow, UINT nStatus)
  489. {
  490. if (bShow)
  491. {
  492. m_bNewSearch = true;
  493. GetDlgItem(IDCANCEL)->SetWindowText("Cancel");
  494. m_nFindIn = FindInWorld;
  495. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  496. if (pDoc)
  497. {
  498. if ( !pDoc->GetSelection()->IsEmpty() )
  499. {
  500. m_nFindIn = FindInSelection;
  501. }
  502. }
  503. // Populate the controls with the current data.
  504. UpdateData(FALSE);
  505. }
  506. CDialog::OnShowWindow(bShow, nStatus);
  507. }