UndoStack.cpp 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  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. void UndoStack::undo()
  16. {
  17. if (!can_undo())
  18. return;
  19. auto pop_container_and_undo = [this]() {
  20. for (;;) {
  21. if (m_stack_index >= m_stack.size())
  22. break;
  23. auto& combo = m_stack[m_stack_index++];
  24. if (combo.commands.size() == 0)
  25. continue;
  26. for (auto& command : combo.commands)
  27. command.undo();
  28. break;
  29. }
  30. };
  31. // If this is the first undo, finish off our current combo
  32. if (m_stack_index == 0)
  33. finalize_current_combo();
  34. pop_container_and_undo();
  35. }
  36. void UndoStack::redo()
  37. {
  38. if (!can_redo())
  39. return;
  40. m_stack_index -= 1;
  41. auto& commands = m_stack[m_stack_index].commands;
  42. for (int i = commands.size() - 1; i >= 0; i--)
  43. commands[i].redo();
  44. }
  45. void UndoStack::push(NonnullOwnPtr<Command>&& command)
  46. {
  47. if (m_stack.is_empty())
  48. finalize_current_combo();
  49. if (m_stack_index > 0) {
  50. for (size_t i = 0; i < m_stack_index; i++)
  51. m_stack.remove(0);
  52. if (m_clean_index.has_value()) {
  53. if (m_clean_index.value() < m_stack_index)
  54. m_clean_index.clear();
  55. else
  56. m_clean_index = m_clean_index.value() - m_stack_index;
  57. }
  58. m_stack_index = 0;
  59. finalize_current_combo();
  60. }
  61. m_stack.first().commands.prepend(move(command));
  62. }
  63. void UndoStack::finalize_current_combo()
  64. {
  65. if (m_stack_index > 0)
  66. return;
  67. if (m_stack.size() != 0 && m_stack.first().commands.size() == 0)
  68. return;
  69. auto undo_commands_container = make<Combo>();
  70. m_stack.prepend(move(undo_commands_container));
  71. if (m_clean_index.has_value())
  72. m_clean_index = m_clean_index.value() + 1;
  73. }
  74. void UndoStack::set_current_unmodified()
  75. {
  76. m_clean_index = non_empty_stack_index();
  77. }
  78. bool UndoStack::is_current_modified() const
  79. {
  80. return !m_clean_index.has_value() || non_empty_stack_index() != m_clean_index.value();
  81. }
  82. void UndoStack::clear()
  83. {
  84. m_stack.clear();
  85. m_stack_index = 0;
  86. m_clean_index.clear();
  87. }
  88. size_t UndoStack::non_empty_stack_index() const
  89. {
  90. if (can_undo() && m_stack[m_stack_index].commands.is_empty())
  91. return m_stack_index + 1;
  92. else
  93. return m_stack_index;
  94. }
  95. }