Undo.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "EditorDefs.h"
  9. #include "Undo.h"
  10. #include "Settings.h"
  11. #include "IUndoManagerListener.h"
  12. #include <list>
  13. #include <QString>
  14. #include "QtUtilWin.h"
  15. #define UNDOREDO_BUTTON_POPUP_TEXT_WIDTH 81
  16. #define UNDOREDO_MULTIPLE_OBJECTS_TEXT " (Multiple Objects)"
  17. //! CSuperUndo objects groups together a block of UndoStepto to allow them to be Undo by single operation.
  18. class CSuperUndoStep
  19. : public CUndoStep
  20. {
  21. public:
  22. //! Add new undo object to undo step.
  23. void AddUndoStep(CUndoStep* step)
  24. {
  25. m_undoSteps.push_back(step);
  26. }
  27. int GetSize() const override
  28. {
  29. int size = 0;
  30. for (int i = 0; i < m_undoSteps.size(); i++)
  31. {
  32. size += m_undoSteps[i]->GetSize();
  33. }
  34. return size;
  35. }
  36. bool IsEmpty() const override
  37. {
  38. return m_undoSteps.empty();
  39. }
  40. void Undo(bool bUndo) override
  41. {
  42. for (int i = static_cast<int>(m_undoSteps.size()) - 1; i >= 0; i--)
  43. {
  44. m_undoSteps[i]->Undo(bUndo);
  45. }
  46. }
  47. void Redo() override
  48. {
  49. for (int i = 0; i < m_undoSteps.size(); i++)
  50. {
  51. m_undoSteps[i]->Redo();
  52. }
  53. }
  54. private:
  55. //! Undo steps included in this step.
  56. std::vector<CUndoStep*> m_undoSteps;
  57. };
  58. // Helper class for CUndoManager that monitors the Asset Manager and suspends undo recording while the Asset Manager
  59. // is processing asset loading events. The events are processed non-deterministically, so they could accidentally get captured
  60. // within an undo recording block.
  61. class AssetManagerUndoInterruptor
  62. : public AZ::Data::AssetManagerNotificationBus::Handler
  63. {
  64. public:
  65. AssetManagerUndoInterruptor()
  66. {
  67. AZ::Data::AssetManagerNotificationBus::Handler::BusConnect();
  68. }
  69. ~AssetManagerUndoInterruptor() override
  70. {
  71. AZ::Data::AssetManagerNotificationBus::Handler::BusDisconnect();
  72. }
  73. void OnAssetEventsDispatchBegin() override
  74. {
  75. GetIEditor()->GetUndoManager()->Suspend();
  76. }
  77. void OnAssetEventsDispatchEnd() override
  78. {
  79. GetIEditor()->GetUndoManager()->Resume();
  80. }
  81. };
  82. //////////////////////////////////////////////////////////////////////////
  83. CUndoManager::CUndoManager()
  84. {
  85. m_bRecording = false;
  86. m_bSuperRecording = false;
  87. m_currentUndo = nullptr;
  88. m_superUndo = nullptr;
  89. m_assetManagerUndoInterruptor = new AssetManagerUndoInterruptor();
  90. m_suspendCount = 0;
  91. m_bUndoing = false;
  92. m_bRedoing = false;
  93. }
  94. //////////////////////////////////////////////////////////////////////////
  95. CUndoManager::~CUndoManager()
  96. {
  97. m_bRecording = false;
  98. ClearRedoStack();
  99. ClearUndoStack();
  100. delete m_superUndo;
  101. delete m_currentUndo;
  102. delete m_assetManagerUndoInterruptor;
  103. }
  104. //////////////////////////////////////////////////////////////////////////
  105. void CUndoManager::Begin()
  106. {
  107. //CryLog( "<Undo> Begin SuspendCount=%d",m_suspendCount );
  108. //if (m_bSuperRecording)
  109. //CLogFile::FormatLine( "<Undo> Begin (Inside SuperSuper)" );
  110. if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
  111. {
  112. return;
  113. }
  114. // assert( m_bRecording == false );
  115. if (m_bRecording)
  116. {
  117. //CLogFile::WriteLine( "<Undo> Begin (already recording)" );
  118. // Not cancel, just combine.
  119. return;
  120. }
  121. // Begin Creates a new undo object.
  122. m_currentUndo = new CUndoStep;
  123. m_bRecording = true;
  124. //CLogFile::WriteLine( "<Undo> Begin OK" );
  125. }
  126. //////////////////////////////////////////////////////////////////////////
  127. void CUndoManager::Restore(bool bUndo)
  128. {
  129. if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
  130. {
  131. return;
  132. }
  133. if (m_currentUndo)
  134. {
  135. BeginRestoreTransaction();
  136. Suspend();
  137. if (bUndo && m_currentUndo)
  138. {
  139. m_currentUndo->Undo(false); // Undo not by Undo command (no need to store Redo)
  140. }
  141. Resume();
  142. if (m_currentUndo)
  143. {
  144. m_currentUndo->ClearObjects();
  145. }
  146. EndRestoreTransaction();
  147. }
  148. //CryLog( "Restore Undo" );
  149. }
  150. // This function is used below to decide if an operation should force a save or not. This currently
  151. // prevents selecting an entity, either from the outliner or both the old and new viewports.
  152. static bool ShouldPersist(const QString& name)
  153. {
  154. return name != "Select Object(s)" && name != "Select Entity" && name != "Box Select Entities";
  155. }
  156. //////////////////////////////////////////////////////////////////////////
  157. void CUndoManager::Accept(const QString& name)
  158. {
  159. //CryLog( "<Undo> Accept, Suspend Count=%d",m_suspendCount );
  160. if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
  161. {
  162. return;
  163. }
  164. if (!m_bRecording)
  165. {
  166. //CLogFile::WriteLine( "<Undo> Accept (Not recording)" );
  167. return;
  168. }
  169. if (!m_currentUndo->IsEmpty())
  170. {
  171. const bool persist = ShouldPersist(name);
  172. if (persist)
  173. {
  174. GetIEditor()->SetModifiedFlag();
  175. }
  176. // If accepting new undo object, must clear all redo stack.
  177. ClearRedoStack();
  178. m_currentUndo->SetName(name);
  179. if (m_bSuperRecording)
  180. {
  181. m_superUndo->AddUndoStep(m_currentUndo);
  182. }
  183. else
  184. {
  185. // Normal recording.
  186. // Keep max undo steps.
  187. while (m_undoStack.size() && (m_undoStack.size() >= GetIEditor()->GetEditorSettings()->undoLevels || GetDatabaseSize() > 100 * 1024 * 1024))
  188. {
  189. delete m_undoStack.front();
  190. m_undoStack.pop_front();
  191. }
  192. m_undoStack.push_back(m_currentUndo);
  193. }
  194. //CLogFile::FormatLine( "Undo Object Accepted (Undo:%d,Redo:%d, Size=%dKb)",m_undoStack.size(),m_redoStack.size(),GetDatabaseSize()/1024 );
  195. // If undo accepted, document modified.
  196. if (persist)
  197. {
  198. GetIEditor()->SetModifiedFlag();
  199. }
  200. if (name.compare("Select Object(s)", Qt::CaseInsensitive) == 0)
  201. {
  202. GetIEditor()->SetModifiedModule(eModifiedBrushes);
  203. }
  204. else if (name.compare("Move Selection", Qt::CaseInsensitive) == 0)
  205. {
  206. GetIEditor()->SetModifiedModule(eModifiedBrushes);
  207. }
  208. else if (name.compare("SubObject Select", Qt::CaseInsensitive) == 0)
  209. {
  210. GetIEditor()->SetModifiedModule(eModifiedBrushes);
  211. }
  212. else if (name.compare("Manipulator Drag", Qt::CaseInsensitive) == 0)
  213. {
  214. GetIEditor()->SetModifiedModule(eModifiedBrushes);
  215. }
  216. }
  217. else
  218. {
  219. // If no any object was recorded, Cancel undo operation.
  220. Cancel();
  221. }
  222. m_bRecording = false;
  223. m_currentUndo = nullptr;
  224. SignalNumUndoRedoToListeners();
  225. //CLogFile::WriteLine( "<Undo> Accept OK" );
  226. }
  227. //////////////////////////////////////////////////////////////////////////
  228. void CUndoManager::Cancel()
  229. {
  230. //CryLog( "<Undo> Cancel" );
  231. if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
  232. {
  233. return;
  234. }
  235. if (!m_bRecording)
  236. {
  237. return;
  238. }
  239. assert(m_currentUndo != 0);
  240. m_bRecording = false;
  241. if (!m_currentUndo->IsEmpty())
  242. {
  243. // Restore all objects to the state they was at Begin call and throws out all undo objects.
  244. Restore(true);
  245. //GetIEditor()->GetLogFile()->FormatLine( "Undo Object Canceled (Undo:%d,Redo:%d)",m_undoStack.size(),m_redoStack.size() );
  246. }
  247. delete m_currentUndo;
  248. m_currentUndo = nullptr;
  249. //CLogFile::WriteLine( "<Undo> Cancel OK" );
  250. }
  251. //////////////////////////////////////////////////////////////////////////
  252. void CUndoManager::Redo(int numSteps)
  253. {
  254. GetIEditor()->Notify(eNotify_OnBeginUndoRedo);
  255. if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
  256. {
  257. return;
  258. }
  259. if (m_bRecording || m_bSuperRecording)
  260. {
  261. AZ_Warning("CUndoManager", false, "Cannot Redo while Recording");
  262. return;
  263. }
  264. m_bRedoing = true;
  265. BeginUndoTransaction();
  266. m_bRedoing = false;
  267. if (!m_redoStack.empty())
  268. {
  269. Suspend();
  270. while (numSteps-- > 0 && !m_redoStack.empty() && !m_bClearRedoStackQueued)
  271. {
  272. m_bRedoing = true;
  273. CUndoStep* redo = m_redoStack.back();
  274. redo->Redo();
  275. AZ_Printf("CUndoManager",
  276. "(Undo: %d, Redo: %d) - Redo last operation: '%s'",
  277. m_undoStack.size(),
  278. m_redoStack.size(),
  279. redo->GetName().toUtf8().constData());
  280. m_redoStack.pop_back();
  281. // Push undo object to redo stack.
  282. m_undoStack.push_back(redo);
  283. m_bRedoing = false;
  284. }
  285. Resume();
  286. }
  287. if (m_suspendCount == 0)
  288. {
  289. GetIEditor()->UpdateViews(eUpdateObjects);
  290. }
  291. m_bRedoing = true;
  292. EndUndoTransaction();
  293. SignalNumUndoRedoToListeners();
  294. m_bRedoing = false;
  295. GetIEditor()->Notify(eNotify_OnEndUndoRedo);
  296. if (m_bClearRedoStackQueued)
  297. {
  298. ClearRedoStack();
  299. }
  300. }
  301. //////////////////////////////////////////////////////////////////////////
  302. void CUndoManager::Undo(int numSteps)
  303. {
  304. GetIEditor()->Notify(eNotify_OnBeginUndoRedo);
  305. if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
  306. {
  307. return;
  308. }
  309. if (m_bRecording || m_bSuperRecording)
  310. {
  311. AZ_Warning("CUndoManager", false, "Cannot Undo while Recording");
  312. return;
  313. }
  314. m_bUndoing = true;
  315. BeginUndoTransaction();
  316. m_bUndoing = false;
  317. if (!m_undoStack.empty())
  318. {
  319. Suspend();
  320. while (numSteps-- > 0 && !m_undoStack.empty())
  321. {
  322. m_bUndoing = true;
  323. CUndoStep* undo = m_undoStack.back();
  324. undo->Undo(true);
  325. AZ_Printf("CUndoManager",
  326. "(Undo: %d, Redo: %d) - Undo last operation: '%s'",
  327. m_undoStack.size(),
  328. m_redoStack.size(),
  329. undo->GetName().toUtf8().constData());
  330. m_undoStack.pop_back();
  331. // Push undo object to redo stack.
  332. m_redoStack.push_back(undo);
  333. m_bUndoing = false;
  334. }
  335. Resume();
  336. }
  337. // Update viewports.
  338. if (m_suspendCount == 0)
  339. {
  340. GetIEditor()->UpdateViews(eUpdateObjects);
  341. }
  342. m_bUndoing = true;
  343. EndUndoTransaction();
  344. SignalNumUndoRedoToListeners();
  345. m_bUndoing = false;
  346. GetIEditor()->Notify(eNotify_OnEndUndoRedo);
  347. }
  348. //////////////////////////////////////////////////////////////////////////
  349. void CUndoManager::RecordUndo(IUndoObject* obj)
  350. {
  351. //CryLog( "<Undo> RecordUndo Name=%s",obj->GetDescription() );
  352. if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
  353. {
  354. //CLogFile::WriteLine( "<Undo> RecordUndo (Undoing or Redoing)" );
  355. obj->Release();
  356. return;
  357. }
  358. if (m_bRecording && (m_suspendCount == 0))
  359. {
  360. assert(m_currentUndo != 0);
  361. m_currentUndo->AddUndoObject(obj);
  362. //CLogFile::FormatLine( "Undo Object Added: %s",obj->GetDescription() );
  363. }
  364. else
  365. {
  366. //CLogFile::WriteLine( "<Undo> RecordUndo (Not Recording)" );
  367. // Ignore this object.
  368. obj->Release();
  369. }
  370. }
  371. //////////////////////////////////////////////////////////////////////////
  372. void CUndoManager::ClearRedoStack()
  373. {
  374. if (m_bRedoing)
  375. {
  376. m_bClearRedoStackQueued = true;
  377. return;
  378. }
  379. m_bClearRedoStackQueued = false;
  380. for (std::list<CUndoStep*>::iterator it = m_redoStack.begin(); it != m_redoStack.end(); it++)
  381. {
  382. delete *it;
  383. }
  384. m_redoStack.clear();
  385. SignalNumUndoRedoToListeners();
  386. }
  387. //////////////////////////////////////////////////////////////////////////
  388. void CUndoManager::ClearUndoStack()
  389. {
  390. for (std::list<CUndoStep*>::iterator it = m_undoStack.begin(); it != m_undoStack.end(); it++)
  391. {
  392. delete *it;
  393. }
  394. m_undoStack.clear();
  395. SignalNumUndoRedoToListeners();
  396. }
  397. //////////////////////////////////////////////////////////////////////////
  398. void CUndoManager::ClearUndoStack(int num)
  399. {
  400. int i = num;
  401. while (i > 0 && !m_undoStack.empty())
  402. {
  403. delete m_undoStack.front();
  404. m_undoStack.pop_front();
  405. i--;
  406. }
  407. SignalNumUndoRedoToListeners();
  408. }
  409. //////////////////////////////////////////////////////////////////////////
  410. void CUndoManager::ClearRedoStack(int num)
  411. {
  412. int i = num;
  413. while (i > 0 && !m_redoStack.empty())
  414. {
  415. delete m_redoStack.back();
  416. m_redoStack.pop_back();
  417. i--;
  418. }
  419. SignalNumUndoRedoToListeners();
  420. }
  421. //////////////////////////////////////////////////////////////////////////
  422. bool CUndoManager::IsHaveRedo() const
  423. {
  424. return !m_redoStack.empty();
  425. }
  426. //////////////////////////////////////////////////////////////////////////
  427. bool CUndoManager::IsHaveUndo() const
  428. {
  429. return !m_undoStack.empty();
  430. }
  431. //////////////////////////////////////////////////////////////////////////
  432. void CUndoManager::Suspend()
  433. {
  434. m_suspendCount++;
  435. //CLogFile::FormatLine( "<Undo> Suspend %d",m_suspendCount );
  436. }
  437. //////////////////////////////////////////////////////////////////////////
  438. void CUndoManager::Resume()
  439. {
  440. assert(m_suspendCount >= 0);
  441. if (m_suspendCount > 0)
  442. {
  443. m_suspendCount--;
  444. }
  445. //CLogFile::FormatLine( "<Undo> Resume %d",m_suspendCount );
  446. }
  447. //////////////////////////////////////////////////////////////////////////
  448. void CUndoManager::SuperBegin()
  449. {
  450. //CLogFile::FormatLine( "<Undo> SuperBegin (SuspendCount%d)",m_suspendCount );
  451. if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
  452. {
  453. return;
  454. }
  455. m_bSuperRecording = true;
  456. m_superUndo = new CSuperUndoStep;
  457. //CLogFile::WriteLine( "<Undo> SuperBegin OK" );
  458. }
  459. //////////////////////////////////////////////////////////////////////////
  460. void CUndoManager::SuperAccept(const QString& name)
  461. {
  462. //CLogFile::WriteLine( "<Undo> SupperAccept" );
  463. if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
  464. {
  465. return;
  466. }
  467. if (!m_bSuperRecording)
  468. {
  469. return;
  470. }
  471. assert(m_superUndo != 0);
  472. if (m_bRecording)
  473. {
  474. Accept(name);
  475. }
  476. if (!m_superUndo->IsEmpty())
  477. {
  478. m_superUndo->SetName(name);
  479. // Keep max undo steps.
  480. while (m_undoStack.size() && (m_undoStack.size() >= GetIEditor()->GetEditorSettings()->undoLevels || GetDatabaseSize() > 100 * 1024 * 1024))
  481. {
  482. delete m_undoStack.front();
  483. m_undoStack.pop_front();
  484. }
  485. m_undoStack.push_back(m_superUndo);
  486. }
  487. else
  488. {
  489. // If no any object was recorded, Cancel undo operation.
  490. SuperCancel();
  491. }
  492. //CLogFile::FormatLine( "Undo Object Accepted (Undo:%d,Redo:%d)",m_undoStack.size(),m_redoStack.size() );
  493. m_bSuperRecording = false;
  494. m_superUndo = nullptr;
  495. //CLogFile::WriteLine( "<Undo> SupperAccept OK" );
  496. SignalNumUndoRedoToListeners();
  497. }
  498. //////////////////////////////////////////////////////////////////////////
  499. void CUndoManager::SuperCancel()
  500. {
  501. //CLogFile::WriteLine( "<Undo> SuperCancel" );
  502. if (m_bUndoing || m_bRedoing) // If Undoing or redoing now, ignore this calls.
  503. {
  504. return;
  505. }
  506. if (!m_bSuperRecording)
  507. {
  508. return;
  509. }
  510. assert(m_superUndo != 0);
  511. if (m_bRecording)
  512. {
  513. Cancel();
  514. }
  515. Suspend();
  516. //! Undo all changes already made.
  517. m_superUndo->Undo(false); // Undo not by Undo command (no need to store Redo)
  518. Resume();
  519. m_bSuperRecording = false;
  520. delete m_superUndo;
  521. m_superUndo = nullptr;
  522. //CLogFile::WriteLine( "<Undo> SuperCancel OK" );
  523. }
  524. //////////////////////////////////////////////////////////////////////////
  525. int CUndoManager::GetUndoStackLen() const
  526. {
  527. return static_cast<int>(m_undoStack.size());
  528. }
  529. //////////////////////////////////////////////////////////////////////////
  530. int CUndoManager::GetRedoStackLen() const
  531. {
  532. return static_cast<int>(m_redoStack.size());
  533. }
  534. //////////////////////////////////////////////////////////////////////////
  535. std::vector<QString> CUndoManager::GetUndoStackNames() const
  536. {
  537. std::vector<QString> undos(m_undoStack.size());
  538. int i = 0;
  539. QString text;
  540. for (auto it = m_undoStack.begin(); it != m_undoStack.end(); it++)
  541. {
  542. QString objNames = (*it)->GetObjectNames();
  543. text = (*it)->GetName() + objNames;
  544. if (text.length() > UNDOREDO_BUTTON_POPUP_TEXT_WIDTH)
  545. {
  546. undos[i++] = (*it)->GetName() + UNDOREDO_MULTIPLE_OBJECTS_TEXT;
  547. }
  548. else
  549. {
  550. undos[i++] = (*it)->GetName() + (objNames.isEmpty() ? "" : " (" + objNames + ")");
  551. }
  552. }
  553. return undos;
  554. }
  555. //////////////////////////////////////////////////////////////////////////
  556. std::vector<QString> CUndoManager::GetRedoStackNames() const
  557. {
  558. std::vector<QString> redos(m_redoStack.size());
  559. int i = 0;
  560. QString text;
  561. for (auto it = m_redoStack.begin(); it != m_redoStack.end(); it++)
  562. {
  563. text = (*it)->GetName() + (*it)->GetObjectNames();
  564. if (text.length() > UNDOREDO_BUTTON_POPUP_TEXT_WIDTH)
  565. {
  566. redos[i++] = (*it)->GetName() + UNDOREDO_MULTIPLE_OBJECTS_TEXT;
  567. }
  568. else
  569. {
  570. redos[i++] = (*it)->GetName() + " (" + (*it)->GetObjectNames() + ")";
  571. }
  572. }
  573. return redos;
  574. }
  575. //////////////////////////////////////////////////////////////////////////
  576. int CUndoManager::GetDatabaseSize()
  577. {
  578. int size = 0;
  579. {
  580. for (std::list<CUndoStep*>::iterator it = m_undoStack.begin(); it != m_undoStack.end(); it++)
  581. {
  582. size += (*it)->GetSize();
  583. }
  584. }
  585. {
  586. for (std::list<CUndoStep*>::iterator it = m_redoStack.begin(); it != m_redoStack.end(); it++)
  587. {
  588. size += (*it)->GetSize();
  589. }
  590. }
  591. return size;
  592. }
  593. //////////////////////////////////////////////////////////////////////////
  594. void CUndoManager::Flush()
  595. {
  596. m_bRecording = false;
  597. ClearRedoStack();
  598. ClearUndoStack();
  599. delete m_superUndo;
  600. delete m_currentUndo;
  601. m_superUndo = nullptr;
  602. m_currentUndo = nullptr;
  603. SignalUndoFlushedToListeners();
  604. }
  605. //////////////////////////////////////////////////////////////////////////
  606. CUndoStep* CUndoManager::GetNextUndo()
  607. {
  608. if (!m_undoStack.empty())
  609. {
  610. return m_undoStack.back();
  611. }
  612. return nullptr;
  613. }
  614. //////////////////////////////////////////////////////////////////////////
  615. CUndoStep* CUndoManager::GetNextRedo()
  616. {
  617. if (!m_redoStack.empty())
  618. {
  619. return m_redoStack.back();
  620. }
  621. return nullptr;
  622. }
  623. //////////////////////////////////////////////////////////////////////////
  624. void CUndoManager::SetMaxUndoStep(int steps)
  625. {
  626. GetIEditor()->GetEditorSettings()->undoLevels = steps;
  627. };
  628. //////////////////////////////////////////////////////////////////////////
  629. int CUndoManager::GetMaxUndoStep() const
  630. {
  631. return GetIEditor()->GetEditorSettings()->undoLevels;
  632. }
  633. void CUndoManager::AddListener(IUndoManagerListener* pListener)
  634. {
  635. stl::push_back_unique(m_listeners, pListener);
  636. }
  637. void CUndoManager::RemoveListener(IUndoManagerListener* pListener)
  638. {
  639. stl::find_and_erase(m_listeners, pListener);
  640. }
  641. void CUndoManager::BeginUndoTransaction()
  642. {
  643. for (auto iter = m_listeners.begin(); iter != m_listeners.end(); ++iter)
  644. {
  645. (*iter)->BeginUndoTransaction();
  646. }
  647. }
  648. void CUndoManager::EndUndoTransaction()
  649. {
  650. for (auto iter = m_listeners.begin(); iter != m_listeners.end(); ++iter)
  651. {
  652. (*iter)->EndUndoTransaction();
  653. }
  654. }
  655. void CUndoManager::BeginRestoreTransaction()
  656. {
  657. for (auto iter = m_listeners.begin(); iter != m_listeners.end(); ++iter)
  658. {
  659. (*iter)->BeginRestoreTransaction();
  660. }
  661. }
  662. void CUndoManager::EndRestoreTransaction()
  663. {
  664. for (auto iter = m_listeners.begin(); iter != m_listeners.end(); ++iter)
  665. {
  666. (*iter)->EndRestoreTransaction();
  667. }
  668. }
  669. void CUndoManager::SignalNumUndoRedoToListeners()
  670. {
  671. for (IUndoManagerListener* listener : m_listeners)
  672. {
  673. listener->SignalNumUndoRedo(static_cast<unsigned int>(m_undoStack.size()), static_cast<unsigned int>(m_redoStack.size()));
  674. }
  675. }
  676. void CUndoManager::SignalUndoFlushedToListeners()
  677. {
  678. for (IUndoManagerListener* listener : m_listeners)
  679. {
  680. listener->UndoStackFlushed();
  681. }
  682. }
  683. bool CUndoManager::IsUndoRecording() const
  684. {
  685. return (m_bRecording || m_bSuperRecording) && m_suspendCount == 0;
  686. }
  687. bool CUndoManager::IsUndoSuspended() const
  688. {
  689. return m_suspendCount != 0;
  690. }
  691. CScopedSuspendUndo::CScopedSuspendUndo()
  692. {
  693. GetIEditor()->SuspendUndo();
  694. }
  695. CScopedSuspendUndo::~CScopedSuspendUndo()
  696. {
  697. GetIEditor()->ResumeUndo();
  698. }