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.

808 lines
20 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implements the Undo/Redo system.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "stdafx.h"
  8. #include "History.h"
  9. #include "hammer.h"
  10. #include "Options.h"
  11. #include "MainFrm.h"
  12. #include "MapDoc.h"
  13. #include "GlobalFunctions.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include <tier0/memdbgon.h>
  16. static CHistory *pCurHistory; // The Undo/Redo history associated with the active doc.
  17. static CHistory FakeHistory; // Used when there is no active doc. Always paused.
  18. //-----------------------------------------------------------------------------
  19. // Purpose: Returns the current active Undo/Redo history.
  20. //-----------------------------------------------------------------------------
  21. CHistory *GetHistory(void)
  22. {
  23. if (!pCurHistory)
  24. {
  25. return(&FakeHistory);
  26. }
  27. return(pCurHistory);
  28. }
  29. //-----------------------------------------------------------------------------
  30. // Purpose: Constructor.
  31. //-----------------------------------------------------------------------------
  32. CHistory::CHistory(void)
  33. {
  34. static BOOL bFirst = TRUE; // fake history is always first
  35. Opposite = NULL;
  36. CurTrack = NULL;
  37. bPaused = bFirst ? 2 : FALSE; // if 2, never unpaused
  38. bFirst = FALSE;
  39. m_bActive = TRUE;
  40. }
  41. //-----------------------------------------------------------------------------
  42. // Purpose: Destructor.
  43. //-----------------------------------------------------------------------------
  44. CHistory::~CHistory()
  45. {
  46. Tracks.PurgeAndDeleteElements();
  47. }
  48. //-----------------------------------------------------------------------------
  49. // Purpose:
  50. // Input : bUndo -
  51. // pOpposite -
  52. //-----------------------------------------------------------------------------
  53. void CHistory::SetOpposite(BOOL bUndo, CHistory *pOpposite)
  54. {
  55. this->bUndo = bUndo;
  56. Opposite = pOpposite;
  57. }
  58. //-----------------------------------------------------------------------------
  59. // Purpose:
  60. // Output : Returns TRUE on success, FALSE on failure.
  61. //-----------------------------------------------------------------------------
  62. BOOL CHistory::IsUndoable()
  63. {
  64. // return status flag depending on the current track
  65. return (CurTrack && m_bActive) ? TRUE : FALSE;
  66. }
  67. //-----------------------------------------------------------------------------
  68. // Purpose:
  69. // Input : bActive -
  70. //-----------------------------------------------------------------------------
  71. void CHistory::SetActive(BOOL bActive)
  72. {
  73. m_bActive = bActive;
  74. if (!m_bActive)
  75. {
  76. // kill all tracks right now
  77. FOR_EACH_OBJ( Tracks, pos )
  78. {
  79. CHistoryTrack *pTrack = Tracks.Element(pos);
  80. delete pTrack;
  81. }
  82. Tracks.RemoveAll();
  83. MarkUndoPosition();
  84. }
  85. }
  86. //-----------------------------------------------------------------------------
  87. // Purpose: Actually, this implements both Undo and Redo, because a Redo is just
  88. // an Undo in the opposite history track.
  89. // Input : pNewSelection - List to populate with the new selection set after the Undo.
  90. //-----------------------------------------------------------------------------
  91. void CHistory::Undo(CMapObjectList *pNewSelection)
  92. {
  93. Opposite->MarkUndoPosition(&CurTrack->Selected, GetCurTrackName(), TRUE);
  94. //
  95. // Track entries are consumed LIFO.
  96. //
  97. int pos = Tracks.Count()-1;
  98. Tracks.Remove(pos);
  99. //
  100. // Perform the undo.
  101. //
  102. Pause();
  103. CurTrack->Undo();
  104. Resume();
  105. //
  106. // Get the objects that should be selected from the track entry.
  107. //
  108. pNewSelection->RemoveAll();
  109. pNewSelection->AddVectorToTail(CurTrack->Selected);
  110. //
  111. // Done with this track entry. This track entry will be recreated by the
  112. // opposite history track if necessary.
  113. //
  114. uDataSize -= CurTrack->uDataSize;
  115. delete CurTrack;
  116. //
  117. // Move to the previous track entry.
  118. //
  119. if ( Tracks.Count() > 0 )
  120. {
  121. CurTrack = Tracks.Element(Tracks.Count()-1);
  122. }
  123. else
  124. {
  125. CurTrack = NULL;
  126. }
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose:
  130. // Input : *pSelection -
  131. // pszName -
  132. // bFromOpposite -
  133. //-----------------------------------------------------------------------------
  134. void CHistory::MarkUndoPosition( const CMapObjectList *pSelection, LPCTSTR pszName, BOOL bFromOpposite)
  135. {
  136. if(Opposite && bUndo && !bFromOpposite)
  137. {
  138. // this is the undo tracker and the call is NOT from the redo
  139. // tracker. kill the redo tracker's history.
  140. FOR_EACH_OBJ( Opposite->Tracks, pos )
  141. {
  142. CHistoryTrack *pTrack = Opposite->Tracks.Element(pos);
  143. pTrack->m_bAutoDestruct = true;
  144. delete pTrack;
  145. }
  146. Opposite->Tracks.RemoveAll();
  147. Opposite->CurTrack = NULL;
  148. }
  149. // create a new track
  150. CurTrack = new CHistoryTrack(this, pSelection);
  151. Tracks.AddToTail(CurTrack);
  152. CurTrack->SetName(pszName);
  153. // check # of undo levels ..
  154. if(Tracks.Count() > Options.general.iUndoLevels)
  155. {
  156. // remove some.
  157. int i, i2;
  158. i = i2 = Tracks.Count() - Options.general.iUndoLevels;
  159. int pos = 0;
  160. while(i--)
  161. {
  162. CHistoryTrack *pTrack = Tracks.Element(pos); pos++;
  163. if(pTrack == CurTrack)
  164. {
  165. i2 -= (i2 - i);
  166. break; // safeguard
  167. }
  168. delete pTrack;
  169. }
  170. // delete them from the list now
  171. while(i2--)
  172. {
  173. Tracks.Remove(0);
  174. }
  175. }
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Purpose: Keeps an object, so changes to it can be undone.
  179. // Input : pObject - Object to keep.
  180. //-----------------------------------------------------------------------------
  181. void CHistory::Keep(CMapClass *pObject)
  182. {
  183. if (CurTrack == NULL)
  184. {
  185. MarkUndoPosition();
  186. }
  187. CurTrack->Keep(pObject, true);
  188. //
  189. // Keep this object's children.
  190. //
  191. EnumChildrenPos_t pos;
  192. CMapClass *pChild = pObject->GetFirstDescendent(pos);
  193. while (pChild != NULL)
  194. {
  195. CurTrack->Keep(pChild, true);
  196. pChild = pObject->GetNextDescendent(pos);
  197. }
  198. }
  199. //-----------------------------------------------------------------------------
  200. // Purpose: Keeps an object, so changes to it can be undone.
  201. // Input : pObject - Object to keep.
  202. //-----------------------------------------------------------------------------
  203. void CHistory::KeepNoChildren(CMapClass *pObject)
  204. {
  205. if (CurTrack == NULL)
  206. {
  207. MarkUndoPosition();
  208. }
  209. CurTrack->Keep(pObject, false);
  210. }
  211. //-----------------------------------------------------------------------------
  212. // Purpose: Keeps a list of objects, so changes to them can be undone.
  213. // Input : pList - List of objects to keep.
  214. //-----------------------------------------------------------------------------
  215. void CHistory::Keep(const CMapObjectList *pList)
  216. {
  217. FOR_EACH_OBJ( *pList, pos )
  218. {
  219. CMapClass *pObject = (CUtlReference< CMapClass >)pList->Element(pos);
  220. Keep(pObject);
  221. }
  222. }
  223. //-----------------------------------------------------------------------------
  224. // Purpose:
  225. // Input : *pObject -
  226. //-----------------------------------------------------------------------------
  227. void CHistory::KeepForDestruction(CMapClass *pObject)
  228. {
  229. if (CurTrack == NULL)
  230. {
  231. MarkUndoPosition();
  232. }
  233. CurTrack->KeepForDestruction(pObject);
  234. }
  235. //-----------------------------------------------------------------------------
  236. // Purpose: Keeps a new object, so it can be deleted on an undo.
  237. // Input : pObject - Object to keep.
  238. //-----------------------------------------------------------------------------
  239. void CHistory::KeepNew(CMapClass *pObject, bool bKeepChildren)
  240. {
  241. if (CurTrack == NULL)
  242. {
  243. MarkUndoPosition();
  244. }
  245. //
  246. // Keep this object's children.
  247. //
  248. if (bKeepChildren)
  249. {
  250. EnumChildrenPos_t pos;
  251. CMapClass *pChild = pObject->GetFirstDescendent(pos);
  252. while (pChild != NULL)
  253. {
  254. CurTrack->KeepNew(pChild);
  255. pChild = pObject->GetNextDescendent(pos);
  256. }
  257. }
  258. CurTrack->KeepNew(pObject);
  259. }
  260. //-----------------------------------------------------------------------------
  261. // Purpose: Keeps a list of new objects, so changes to them can be undone.
  262. // Input : pList - List of objects to keep.
  263. //-----------------------------------------------------------------------------
  264. void CHistory::KeepNew( const CMapObjectList *pList, bool bKeepChildren)
  265. {
  266. FOR_EACH_OBJ( *pList, pos )
  267. {
  268. CMapClass *pObject = (CUtlReference< CMapClass >)pList->Element(pos);
  269. KeepNew(pObject, bKeepChildren);
  270. }
  271. }
  272. //-----------------------------------------------------------------------------
  273. // Purpose: Sets the given history object as the one to use for all Undo operations.
  274. //-----------------------------------------------------------------------------
  275. void CHistory::SetHistory(class CHistory *pHistory)
  276. {
  277. pCurHistory = pHistory;
  278. }
  279. //-----------------------------------------------------------------------------
  280. // Purpose:
  281. //-----------------------------------------------------------------------------
  282. void CHistory::OnRemoveVisGroup(CVisGroup *pVisGroup)
  283. {
  284. if (CurTrack)
  285. {
  286. CurTrack->OnRemoveVisGroup(pVisGroup);
  287. }
  288. if (Opposite && Opposite->CurTrack)
  289. {
  290. Opposite->CurTrack->OnRemoveVisGroup(pVisGroup);
  291. }
  292. }
  293. //-----------------------------------------------------------------------------
  294. // Purpose: Constructor.
  295. //-----------------------------------------------------------------------------
  296. CTrackEntry::CTrackEntry()
  297. {
  298. m_bAutoDestruct = true;
  299. m_nDataSize = 0;
  300. m_eType = ttNone;
  301. m_bUndone = false;
  302. m_bKeptChildren = false;
  303. }
  304. //-----------------------------------------------------------------------------
  305. // Purpose: Constructs a track entry from a list of parameters.
  306. // Input : t -
  307. //-----------------------------------------------------------------------------
  308. CTrackEntry::CTrackEntry(TrackType_t eType, ...)
  309. {
  310. m_bAutoDestruct = false;
  311. m_eType = eType;
  312. m_bUndone = false;
  313. m_bKeptChildren = false;
  314. va_list vl;
  315. va_start(vl, eType);
  316. switch (m_eType)
  317. {
  318. //
  319. // Keep track of an object that was modified by the user. An Undo will cause this
  320. // object to revert to its original state.
  321. //
  322. case ttCopy:
  323. {
  324. m_Copy.pCurrent = va_arg(vl, CMapClass *);
  325. m_Copy.pKeptObject = m_Copy.pCurrent->Copy(false);
  326. m_nDataSize = sizeof(*this) + m_Copy.pKeptObject->GetSize();
  327. break;
  328. }
  329. //
  330. // Keep track of an object that was created by the user. An Undo will cause this
  331. // object to be removed from the world.
  332. //
  333. case ttCreate:
  334. {
  335. m_Create.pCreated = va_arg(vl, CMapClass *);
  336. Assert(m_Create.pCreated != NULL);
  337. Assert(m_Create.pCreated->m_pParent != NULL);
  338. m_nDataSize = sizeof(*this);
  339. break;
  340. }
  341. //
  342. // Keep track of an object that was deleted by the user. An Undo will cause this
  343. // object to be added back into the world.
  344. //
  345. case ttDelete:
  346. {
  347. m_Delete.pDeleted = va_arg(vl, CMapClass *);
  348. m_Delete.pKeptParent = m_Delete.pDeleted->GetParent();
  349. m_nDataSize = sizeof(*this);
  350. break;
  351. }
  352. }
  353. va_end(vl);
  354. }
  355. //-----------------------------------------------------------------------------
  356. // Purpose: Destructor. Called when history events are removed from the Undo
  357. // history. The goal here is to clean up any copies of objects that
  358. // were kept in the history.
  359. //
  360. // Once a track entry object is destroyed, the user event that it
  361. // tracks can no longer be undone or redone.
  362. //-----------------------------------------------------------------------------
  363. CTrackEntry::~CTrackEntry()
  364. {
  365. if (!m_bAutoDestruct || m_eType == ttNone)
  366. {
  367. return;
  368. }
  369. switch (m_eType)
  370. {
  371. //
  372. // We kept a copy of an object. Delete our copy of the object.
  373. //
  374. case ttCopy:
  375. {
  376. if (!m_bUndone)
  377. {
  378. delete m_Copy.pKeptObject;
  379. }
  380. break;
  381. }
  382. //
  383. // We kept track of an object's creation. Nothing to delete here. The object is in the world.
  384. //
  385. case ttCreate:
  386. {
  387. break;
  388. }
  389. //
  390. // We kept a pointer to an object that was deleted from the world. We need to delete the object,
  391. // because the object's deletion can no longer be undone.
  392. //
  393. case ttDelete:
  394. {
  395. //
  396. // If this entry was undone, the object has been added back into the world, so we
  397. // should not delete the object.
  398. //
  399. if (!m_bUndone)
  400. {
  401. delete m_Delete.pDeleted;
  402. }
  403. break;
  404. }
  405. default:
  406. {
  407. Assert( false );
  408. }
  409. }
  410. }
  411. //-----------------------------------------------------------------------------
  412. // Purpose:
  413. //-----------------------------------------------------------------------------
  414. void CTrackEntry::SetKeptChildren(bool bSet)
  415. {
  416. m_bKeptChildren = bSet;
  417. }
  418. //-----------------------------------------------------------------------------
  419. // Purpose: Performs the undo by restoring the kept object to its original state.
  420. // Input : Opposite - Pointer to the opposite history track. If we are in the
  421. // undo history, it points to the redo history, and vice-versa.
  422. //-----------------------------------------------------------------------------
  423. void CTrackEntry::Undo(CHistory *Opposite)
  424. {
  425. switch (m_eType)
  426. {
  427. //
  428. // We are undoing a change to an object. Restore it to its original state.
  429. //
  430. case ttCopy:
  431. {
  432. if (m_bKeptChildren)
  433. {
  434. Opposite->Keep(m_Copy.pCurrent);
  435. }
  436. else
  437. {
  438. Opposite->KeepNoChildren(m_Copy.pCurrent);
  439. }
  440. //
  441. // Copying back into the world, so update object dependencies.
  442. //
  443. m_Copy.pCurrent->CopyFrom(m_Copy.pKeptObject, true);
  444. //
  445. // Delete the copy of the kept object.
  446. //
  447. delete m_Copy.pKeptObject;
  448. m_Copy.pKeptObject = NULL;
  449. break;
  450. }
  451. //
  452. // We are undoing the deletion of an object. Add it to the world.
  453. //
  454. case ttDelete:
  455. {
  456. //
  457. // First restore the deleted object's parent so that it is properly kept in the
  458. // opposite history track. The opposite history track sees this as a new object
  459. // being created.
  460. //
  461. m_Delete.pDeleted->m_pParent = m_Delete.pKeptParent;
  462. Opposite->KeepNew(m_Delete.pDeleted, false);
  463. //
  464. // Put the object back in the world.
  465. //
  466. Opposite->GetDocument()->AddObjectToWorld(m_Delete.pDeleted, m_Delete.pKeptParent);
  467. break;
  468. }
  469. //
  470. // We are undoing the creation of an object. Remove it from the world.
  471. //
  472. case ttCreate:
  473. {
  474. //
  475. // Create a symmetrical track event in the other history track.
  476. //
  477. Opposite->KeepForDestruction(m_Create.pCreated);
  478. //
  479. // Remove the object from the world, but not its children. If its children
  480. // were new to the world they were kept seperately.
  481. //
  482. Opposite->GetDocument()->RemoveObjectFromWorld(m_Create.pCreated, false);
  483. m_Create.pCreated = NULL; // dvs: why do we do this?
  484. break;
  485. }
  486. }
  487. m_bUndone = true;
  488. }
  489. //-----------------------------------------------------------------------------
  490. // Purpose: Notifies the object that it has been undone/redone. Called after all
  491. // undo entries have been handled so that objects are dealing with the
  492. // correct data set when they calculate bounds, etc.
  493. //-----------------------------------------------------------------------------
  494. void CTrackEntry::DispatchUndoNotify(void)
  495. {
  496. switch (m_eType)
  497. {
  498. //
  499. // We are undoing a change to an object. Restore it to its original state.
  500. //
  501. case ttCopy:
  502. {
  503. m_Copy.pCurrent->OnUndoRedo();
  504. m_Copy.pCurrent->NotifyDependents(Notify_Changed);
  505. break;
  506. }
  507. }
  508. }
  509. //-----------------------------------------------------------------------------
  510. // Purpose: The given visgroup is being deleted. Remove pointers to it from
  511. // the object in this track entry.
  512. // Input : pVisGroup -
  513. //-----------------------------------------------------------------------------
  514. void CTrackEntry::OnRemoveVisGroup(CVisGroup *pVisGroup)
  515. {
  516. switch (m_eType)
  517. {
  518. case ttCopy:
  519. {
  520. m_Copy.pKeptObject->RemoveVisGroup(pVisGroup);
  521. break;
  522. }
  523. case ttCreate:
  524. {
  525. break;
  526. }
  527. case ttDelete:
  528. {
  529. m_Delete.pDeleted->RemoveVisGroup(pVisGroup);
  530. break;
  531. }
  532. }
  533. }
  534. //-----------------------------------------------------------------------------
  535. // Purpose:
  536. // Input : *pParent -
  537. // *pSelected -
  538. // Output :
  539. //-----------------------------------------------------------------------------
  540. CHistoryTrack::CHistoryTrack(CHistory *pParent, const CMapObjectList *pSelected)
  541. {
  542. Parent = pParent;
  543. Data.EnsureCapacity(16);
  544. uDataSize = 0;
  545. static int dwTrackerID = 1; // objects start at 0, so we don't want to
  546. dwID = dwTrackerID ++;
  547. // add to local list of selected objects at time of creation
  548. if (pSelected)
  549. {
  550. Selected.AddVectorToTail(*pSelected);
  551. }
  552. m_bAutoDestruct = true;
  553. szName[0] = 0;
  554. }
  555. //-----------------------------------------------------------------------------
  556. // Purpose: Destructor. Called when this track's document is being deleted.
  557. // Marks all entries in this track for autodestruction, so that when
  558. // their destructor gets called, they free any object pointers that they
  559. // hold.
  560. //-----------------------------------------------------------------------------
  561. CHistoryTrack::~CHistoryTrack()
  562. {
  563. for (int i = 0; i < Data.Count(); i++)
  564. {
  565. Data[i].m_bAutoDestruct = m_bAutoDestruct;
  566. }
  567. }
  568. //-----------------------------------------------------------------------------
  569. // Purpose:
  570. // Input : *pObject -
  571. // iFlag -
  572. // Output : Returns TRUE on success, FALSE on failure.
  573. //-----------------------------------------------------------------------------
  574. BOOL CHistoryTrack::CheckObjectFlag(CMapClass *pObject, int iFlag)
  575. {
  576. // check for saved copy already..
  577. if(pObject->Kept.ID != dwID)
  578. {
  579. // no id.. make sure types is flag only
  580. pObject->Kept.ID = dwID;
  581. pObject->Kept.Types = iFlag;
  582. }
  583. else if(!(pObject->Kept.Types & iFlag))
  584. {
  585. // if we've already stored that this is a new object in this
  586. // track, there is no point in storing a copy since UNDOing
  587. // this track will delete the object.
  588. if(iFlag == CTrackEntry::ttCopy &&
  589. (pObject->Kept.Types & CTrackEntry::ttCreate))
  590. {
  591. return TRUE;
  592. }
  593. // id, but no copy flag.. make sure types has flag set
  594. pObject->Kept.Types |= iFlag;
  595. }
  596. else
  597. {
  598. // both here.. we have a copy
  599. return TRUE;
  600. }
  601. return FALSE;
  602. }
  603. //-----------------------------------------------------------------------------
  604. // Purpose:
  605. //-----------------------------------------------------------------------------
  606. void CHistoryTrack::OnRemoveVisGroup(CVisGroup *pVisGroup)
  607. {
  608. for (int i = 0; i < Data.Count(); i++)
  609. {
  610. Data[i].OnRemoveVisGroup(pVisGroup);
  611. }
  612. }
  613. //-----------------------------------------------------------------------------
  614. // Purpose:
  615. // Input : *pObject -
  616. //-----------------------------------------------------------------------------
  617. void CHistoryTrack::Keep(CMapClass *pObject, bool bKeepChildren)
  618. {
  619. if(Parent->IsPaused() || pObject->IsTemporary())
  620. return;
  621. // make a copy of this object so we can undo changes to it
  622. if(CheckObjectFlag(pObject, CTrackEntry::ttCopy))
  623. return;
  624. Parent->Pause();
  625. CTrackEntry te(CTrackEntry::ttCopy, pObject);
  626. te.SetKeptChildren(bKeepChildren);
  627. Data.AddToTail(te);
  628. te.m_bAutoDestruct = false;
  629. uDataSize += te.GetSize();
  630. Parent->Resume();
  631. }
  632. //-----------------------------------------------------------------------------
  633. // Purpose:
  634. // Input : *pObject -
  635. //-----------------------------------------------------------------------------
  636. void CHistoryTrack::KeepForDestruction(CMapClass *pObject)
  637. {
  638. if(Parent->IsPaused() || pObject->IsTemporary())
  639. return;
  640. // check for saved destruction already..
  641. if(CheckObjectFlag(pObject, CTrackEntry::ttDelete))
  642. return;
  643. Parent->Pause();
  644. CTrackEntry te(CTrackEntry::ttDelete, pObject);
  645. Data.AddToTail(te);
  646. te.m_bAutoDestruct = false;
  647. uDataSize += te.GetSize();
  648. Parent->Resume();
  649. }
  650. //-----------------------------------------------------------------------------
  651. // Purpose:
  652. // Input : *pObject -
  653. //-----------------------------------------------------------------------------
  654. void CHistoryTrack::KeepNew(CMapClass *pObject)
  655. {
  656. if(Parent->IsPaused() || pObject->IsTemporary())
  657. return;
  658. // check for saved creation already..
  659. VERIFY(!CheckObjectFlag(pObject, CTrackEntry::ttCreate));
  660. Parent->Pause();
  661. CTrackEntry te(CTrackEntry::ttCreate, pObject);
  662. Data.AddToTail(te);
  663. te.m_bAutoDestruct = false;
  664. uDataSize += te.GetSize();
  665. Parent->Resume();
  666. }
  667. //-----------------------------------------------------------------------------
  668. // Purpose: Undoes all the track entries in this track.
  669. //-----------------------------------------------------------------------------
  670. void CHistoryTrack::Undo()
  671. {
  672. for (int i = Data.Count() - 1; i >= 0; i--)
  673. {
  674. Data[i].Undo(Parent->Opposite);
  675. }
  676. //
  677. // Do notification separately so that objects are dealing with the
  678. // correct data set when they calculate bounds, etc.
  679. //
  680. for (int i = Data.Count() - 1; i >= 0; i--)
  681. {
  682. Data[i].DispatchUndoNotify();
  683. }
  684. }