UndoStack.cpp 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibGUI/Command.h>
  7. #include <LibGUI/UndoStack.h>
  8. namespace GUI {
  9. UndoStack::UndoStack()
  10. {
  11. }
  12. UndoStack::~UndoStack()
  13. {
  14. }
  15. bool UndoStack::can_undo() const
  16. {
  17. return m_stack_index > 0 || (m_stack.size() == 1 && m_stack[0].commands.size() > 0);
  18. }
  19. bool UndoStack::can_redo() const
  20. {
  21. if (m_stack.is_empty())
  22. return false;
  23. return m_stack_index != m_stack.size() - 1;
  24. }
  25. void UndoStack::undo()
  26. {
  27. if (!can_undo())
  28. return;
  29. finalize_current_combo();
  30. auto& combo = m_stack[--m_stack_index];
  31. for (auto i = static_cast<ssize_t>(combo.commands.size()) - 1; i >= 0; i--)
  32. combo.commands[i].undo();
  33. if (on_state_change)
  34. on_state_change();
  35. }
  36. void UndoStack::redo()
  37. {
  38. if (!can_redo())
  39. return;
  40. auto& commands = m_stack[m_stack_index++].commands;
  41. for (auto& command : commands)
  42. command.redo();
  43. if (on_state_change)
  44. on_state_change();
  45. }
  46. void UndoStack::pop()
  47. {
  48. VERIFY(!m_stack.is_empty());
  49. m_stack.take_last();
  50. if (m_clean_index.has_value() && m_clean_index.value() > m_stack.size())
  51. m_clean_index = {};
  52. }
  53. void UndoStack::push(NonnullOwnPtr<Command>&& command)
  54. {
  55. if (m_stack.is_empty())
  56. finalize_current_combo();
  57. // If the stack cursor is behind the top of the stack, nuke everything from here to the top.
  58. if (m_stack_index != m_stack.size() - 1) {
  59. while (m_stack.size() != m_stack_index) {
  60. pop();
  61. }
  62. finalize_current_combo();
  63. }
  64. m_stack.last().commands.append(move(command));
  65. if (on_state_change)
  66. on_state_change();
  67. }
  68. void UndoStack::finalize_current_combo()
  69. {
  70. if (m_stack.is_empty()) {
  71. m_stack.append(make<Combo>());
  72. return;
  73. }
  74. if (!m_stack.last().commands.is_empty()) {
  75. m_stack.append(make<Combo>());
  76. m_stack_index = m_stack.size() - 1;
  77. if (on_state_change)
  78. on_state_change();
  79. }
  80. }
  81. void UndoStack::set_current_unmodified()
  82. {
  83. finalize_current_combo();
  84. if (m_clean_index.has_value() && m_clean_index.value() == m_stack_index)
  85. return;
  86. m_clean_index = m_stack_index;
  87. if (on_state_change)
  88. on_state_change();
  89. }
  90. bool UndoStack::is_current_modified() const
  91. {
  92. if (!m_clean_index.has_value())
  93. return true;
  94. if (m_stack_index != m_clean_index.value())
  95. return true;
  96. if (m_stack.is_empty())
  97. return false;
  98. if (m_stack_index == m_stack.size() - 1 && !m_stack[m_stack_index].commands.is_empty())
  99. return true;
  100. return false;
  101. }
  102. void UndoStack::clear()
  103. {
  104. if (m_stack.is_empty() && m_stack_index == 0 && !m_clean_index.has_value())
  105. return;
  106. m_stack.clear();
  107. m_stack_index = 0;
  108. m_clean_index.clear();
  109. if (on_state_change)
  110. on_state_change();
  111. }
  112. }