undo_manager.cc 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. // Copyright 2013 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/undo/undo_manager.h"
  5. #include <memory>
  6. #include <utility>
  7. #include "base/auto_reset.h"
  8. #include "base/check_op.h"
  9. #include "base/containers/adapters.h"
  10. #include "base/memory/ptr_util.h"
  11. #include "base/observer_list.h"
  12. #include "components/strings/grit/components_strings.h"
  13. #include "components/undo/undo_manager_observer.h"
  14. #include "components/undo/undo_operation.h"
  15. #include "ui/base/l10n/l10n_util.h"
  16. namespace {
  17. // Maximum number of changes that can be undone.
  18. const size_t kMaxUndoGroups = 100;
  19. } // namespace
  20. // UndoGroup ------------------------------------------------------------------
  21. UndoGroup::UndoGroup()
  22. : undo_label_id_(IDS_BOOKMARK_BAR_UNDO),
  23. redo_label_id_(IDS_BOOKMARK_BAR_REDO) {
  24. }
  25. UndoGroup::~UndoGroup() {
  26. }
  27. void UndoGroup::AddOperation(std::unique_ptr<UndoOperation> operation) {
  28. if (operations_.empty()) {
  29. set_undo_label_id(operation->GetUndoLabelId());
  30. set_redo_label_id(operation->GetRedoLabelId());
  31. }
  32. operations_.push_back(std::move(operation));
  33. }
  34. void UndoGroup::Undo() {
  35. for (const std::unique_ptr<UndoOperation>& operation :
  36. base::Reversed(operations_))
  37. operation->Undo();
  38. }
  39. // UndoManager ----------------------------------------------------------------
  40. UndoManager::UndoManager()
  41. : group_actions_count_(0),
  42. undo_in_progress_action_(nullptr),
  43. undo_suspended_count_(0),
  44. performing_undo_(false),
  45. performing_redo_(false) {}
  46. UndoManager::~UndoManager() {
  47. DCHECK_EQ(0, group_actions_count_);
  48. DCHECK_EQ(0, undo_suspended_count_);
  49. DCHECK(!performing_undo_);
  50. DCHECK(!performing_redo_);
  51. }
  52. void UndoManager::Undo() {
  53. Undo(&performing_undo_, &undo_actions_);
  54. }
  55. void UndoManager::Redo() {
  56. Undo(&performing_redo_, &redo_actions_);
  57. }
  58. std::u16string UndoManager::GetUndoLabel() const {
  59. return l10n_util::GetStringUTF16(
  60. undo_actions_.empty() ? IDS_BOOKMARK_BAR_UNDO
  61. : undo_actions_.back()->get_undo_label_id());
  62. }
  63. std::u16string UndoManager::GetRedoLabel() const {
  64. return l10n_util::GetStringUTF16(
  65. redo_actions_.empty() ? IDS_BOOKMARK_BAR_REDO
  66. : redo_actions_.back()->get_redo_label_id());
  67. }
  68. void UndoManager::AddUndoOperation(std::unique_ptr<UndoOperation> operation) {
  69. if (IsUndoTrakingSuspended()) {
  70. RemoveAllOperations();
  71. operation.reset();
  72. return;
  73. }
  74. if (group_actions_count_) {
  75. pending_grouped_action_->AddOperation(std::move(operation));
  76. } else {
  77. auto new_action = std::make_unique<UndoGroup>();
  78. new_action->AddOperation(std::move(operation));
  79. AddUndoGroup(std::move(new_action));
  80. }
  81. }
  82. void UndoManager::StartGroupingActions() {
  83. if (!group_actions_count_)
  84. pending_grouped_action_ = std::make_unique<UndoGroup>();
  85. ++group_actions_count_;
  86. }
  87. void UndoManager::EndGroupingActions() {
  88. --group_actions_count_;
  89. if (group_actions_count_ > 0)
  90. return;
  91. // Check that StartGroupingActions and EndGroupingActions are paired.
  92. DCHECK_GE(group_actions_count_, 0);
  93. bool is_user_action = !performing_undo_ && !performing_redo_;
  94. if (!pending_grouped_action_->undo_operations().empty()) {
  95. AddUndoGroup(std::move(pending_grouped_action_));
  96. } else {
  97. // No changes were executed since we started grouping actions, so the
  98. // pending UndoGroup should be discarded.
  99. pending_grouped_action_.reset();
  100. // This situation is only expected when it is a user initiated action.
  101. // Undo/Redo should have at least one operation performed.
  102. DCHECK(is_user_action);
  103. }
  104. }
  105. void UndoManager::SuspendUndoTracking() {
  106. ++undo_suspended_count_;
  107. }
  108. void UndoManager::ResumeUndoTracking() {
  109. DCHECK_GT(undo_suspended_count_, 0);
  110. --undo_suspended_count_;
  111. }
  112. bool UndoManager::IsUndoTrakingSuspended() const {
  113. return undo_suspended_count_ > 0;
  114. }
  115. void UndoManager::RemoveAllOperations() {
  116. DCHECK(!group_actions_count_);
  117. undo_actions_.clear();
  118. redo_actions_.clear();
  119. NotifyOnUndoManagerStateChange();
  120. }
  121. void UndoManager::AddObserver(UndoManagerObserver* observer) {
  122. observers_.AddObserver(observer);
  123. }
  124. void UndoManager::RemoveObserver(UndoManagerObserver* observer) {
  125. observers_.RemoveObserver(observer);
  126. }
  127. void UndoManager::Undo(
  128. bool* performing_indicator,
  129. std::vector<std::unique_ptr<UndoGroup>>* active_undo_group) {
  130. // Check that action grouping has been correctly ended.
  131. DCHECK(!group_actions_count_);
  132. if (active_undo_group->empty())
  133. return;
  134. base::AutoReset<bool> incoming_changes(performing_indicator, true);
  135. std::unique_ptr<UndoGroup> action = std::move(active_undo_group->back());
  136. base::AutoReset<UndoGroup*> action_context(&undo_in_progress_action_,
  137. action.get());
  138. active_undo_group->erase(active_undo_group->begin() +
  139. active_undo_group->size() - 1);
  140. StartGroupingActions();
  141. action->Undo();
  142. EndGroupingActions();
  143. NotifyOnUndoManagerStateChange();
  144. }
  145. void UndoManager::NotifyOnUndoManagerStateChange() {
  146. for (auto& observer : observers_)
  147. observer.OnUndoManagerStateChange();
  148. }
  149. void UndoManager::AddUndoGroup(std::unique_ptr<UndoGroup> new_undo_group) {
  150. GetActiveUndoGroup()->push_back(std::move(new_undo_group));
  151. // User actions invalidate any available redo actions.
  152. if (is_user_action())
  153. redo_actions_.clear();
  154. // Limit the number of undo levels so the undo stack does not grow unbounded.
  155. if (GetActiveUndoGroup()->size() > kMaxUndoGroups)
  156. GetActiveUndoGroup()->erase(GetActiveUndoGroup()->begin());
  157. NotifyOnUndoManagerStateChange();
  158. }
  159. std::vector<std::unique_ptr<UndoGroup>>* UndoManager::GetActiveUndoGroup() {
  160. return performing_undo_ ? &redo_actions_ : &undo_actions_;
  161. }