blob: d5e74421b2fd2d5a230b6e28b72d86b73100b305 [file] [log] [blame]
[email protected]be5cf822013-07-10 16:36:211// 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
sdefresnec083d1f2015-04-17 21:12:185#include "components/undo/undo_manager.h"
[email protected]be5cf822013-07-10 16:36:216
dcheng51ace48a2015-12-26 22:45:177#include <utility>
8
[email protected]be5cf822013-07-10 16:36:219#include "base/auto_reset.h"
10#include "base/logging.h"
ke.heda6df4de2017-01-26 00:20:0311#include "base/memory/ptr_util.h"
thakisfe8fa0a2017-02-23 19:46:3612#include "components/strings/grit/components_strings.h"
sdefresnec083d1f2015-04-17 21:12:1813#include "components/undo/undo_manager_observer.h"
14#include "components/undo/undo_operation.h"
[email protected]7d63a262013-12-17 17:15:5515#include "ui/base/l10n/l10n_util.h"
[email protected]be5cf822013-07-10 16:36:2116
[email protected]f0541032013-07-31 07:27:5317namespace {
18
19// Maximum number of changes that can be undone.
20const size_t kMaxUndoGroups = 100;
21
22} // namespace
23
[email protected]be5cf822013-07-10 16:36:2124// UndoGroup ------------------------------------------------------------------
25
[email protected]7d63a262013-12-17 17:15:5526UndoGroup::UndoGroup()
27 : undo_label_id_(IDS_BOOKMARK_BAR_UNDO),
28 redo_label_id_(IDS_BOOKMARK_BAR_REDO) {
[email protected]be5cf822013-07-10 16:36:2129}
30
31UndoGroup::~UndoGroup() {
32}
33
dcheng3f767dc32016-04-25 22:54:2234void UndoGroup::AddOperation(std::unique_ptr<UndoOperation> operation) {
[email protected]7d63a262013-12-17 17:15:5535 if (operations_.empty()) {
36 set_undo_label_id(operation->GetUndoLabelId());
37 set_redo_label_id(operation->GetRedoLabelId());
38 }
ke.heda6df4de2017-01-26 00:20:0339 operations_.push_back(std::move(operation));
[email protected]be5cf822013-07-10 16:36:2140}
41
42void UndoGroup::Undo() {
ke.heda6df4de2017-01-26 00:20:0343 for (auto ri = operations_.rbegin(); ri != operations_.rend(); ++ri)
[email protected]be5cf822013-07-10 16:36:2144 (*ri)->Undo();
[email protected]be5cf822013-07-10 16:36:2145}
46
47// UndoManager ----------------------------------------------------------------
48
49UndoManager::UndoManager()
50 : group_actions_count_(0),
Ivan Kotenkov75b1c3a2017-10-24 14:47:2451 undo_in_progress_action_(nullptr),
[email protected]be5cf822013-07-10 16:36:2152 undo_suspended_count_(0),
53 performing_undo_(false),
Ivan Kotenkov75b1c3a2017-10-24 14:47:2454 performing_redo_(false) {}
[email protected]be5cf822013-07-10 16:36:2155
56UndoManager::~UndoManager() {
57 DCHECK_EQ(0, group_actions_count_);
58 DCHECK_EQ(0, undo_suspended_count_);
59 DCHECK(!performing_undo_);
60 DCHECK(!performing_redo_);
61}
62
63void UndoManager::Undo() {
64 Undo(&performing_undo_, &undo_actions_);
65}
66
67void UndoManager::Redo() {
68 Undo(&performing_redo_, &redo_actions_);
69}
70
[email protected]7d63a262013-12-17 17:15:5571base::string16 UndoManager::GetUndoLabel() const {
72 return l10n_util::GetStringUTF16(
73 undo_actions_.empty() ? IDS_BOOKMARK_BAR_UNDO
74 : undo_actions_.back()->get_undo_label_id());
75}
76
77base::string16 UndoManager::GetRedoLabel() const {
78 return l10n_util::GetStringUTF16(
79 redo_actions_.empty() ? IDS_BOOKMARK_BAR_REDO
80 : redo_actions_.back()->get_redo_label_id());
81}
82
dcheng3f767dc32016-04-25 22:54:2283void UndoManager::AddUndoOperation(std::unique_ptr<UndoOperation> operation) {
[email protected]be5cf822013-07-10 16:36:2184 if (IsUndoTrakingSuspended()) {
[email protected]f0541032013-07-31 07:27:5385 RemoveAllOperations();
[email protected]be5cf822013-07-10 16:36:2186 operation.reset();
87 return;
88 }
89
90 if (group_actions_count_) {
dcheng51ace48a2015-12-26 22:45:1791 pending_grouped_action_->AddOperation(std::move(operation));
[email protected]be5cf822013-07-10 16:36:2192 } else {
93 UndoGroup* new_action = new UndoGroup();
dcheng51ace48a2015-12-26 22:45:1794 new_action->AddOperation(std::move(operation));
[email protected]f0541032013-07-31 07:27:5395 AddUndoGroup(new_action);
[email protected]be5cf822013-07-10 16:36:2196 }
97}
98
99void UndoManager::StartGroupingActions() {
100 if (!group_actions_count_)
101 pending_grouped_action_.reset(new UndoGroup());
102 ++group_actions_count_;
103}
104
105void UndoManager::EndGroupingActions() {
106 --group_actions_count_;
107 if (group_actions_count_ > 0)
108 return;
109
110 // Check that StartGroupingActions and EndGroupingActions are paired.
111 DCHECK_GE(group_actions_count_, 0);
112
113 bool is_user_action = !performing_undo_ && !performing_redo_;
[email protected]a175b9d2013-07-31 17:44:21114 if (!pending_grouped_action_->undo_operations().empty()) {
[email protected]f0541032013-07-31 07:27:53115 AddUndoGroup(pending_grouped_action_.release());
[email protected]be5cf822013-07-10 16:36:21116 } else {
117 // No changes were executed since we started grouping actions, so the
118 // pending UndoGroup should be discarded.
119 pending_grouped_action_.reset();
120
121 // This situation is only expected when it is a user initiated action.
122 // Undo/Redo should have at least one operation performed.
123 DCHECK(is_user_action);
124 }
125}
126
127void UndoManager::SuspendUndoTracking() {
128 ++undo_suspended_count_;
129}
130
131void UndoManager::ResumeUndoTracking() {
132 DCHECK_GT(undo_suspended_count_, 0);
133 --undo_suspended_count_;
134}
135
136bool UndoManager::IsUndoTrakingSuspended() const {
137 return undo_suspended_count_ > 0;
138}
139
[email protected]f0541032013-07-31 07:27:53140void UndoManager::RemoveAllOperations() {
141 DCHECK(!group_actions_count_);
142 undo_actions_.clear();
143 redo_actions_.clear();
[email protected]0167050e2014-04-15 21:14:22144
145 NotifyOnUndoManagerStateChange();
146}
147
148void UndoManager::AddObserver(UndoManagerObserver* observer) {
149 observers_.AddObserver(observer);
150}
151
152void UndoManager::RemoveObserver(UndoManagerObserver* observer) {
153 observers_.RemoveObserver(observer);
[email protected]f0541032013-07-31 07:27:53154}
155
ke.heda6df4de2017-01-26 00:20:03156void UndoManager::Undo(
157 bool* performing_indicator,
158 std::vector<std::unique_ptr<UndoGroup>>* active_undo_group) {
[email protected]be5cf822013-07-10 16:36:21159 // Check that action grouping has been correctly ended.
160 DCHECK(!group_actions_count_);
161
162 if (active_undo_group->empty())
163 return;
164
165 base::AutoReset<bool> incoming_changes(performing_indicator, true);
ke.heda6df4de2017-01-26 00:20:03166 std::unique_ptr<UndoGroup> action = std::move(active_undo_group->back());
[email protected]cb8f79e2013-12-18 20:08:42167 base::AutoReset<UndoGroup*> action_context(&undo_in_progress_action_,
168 action.get());
ke.heda6df4de2017-01-26 00:20:03169 active_undo_group->erase(active_undo_group->begin() +
170 active_undo_group->size() - 1);
[email protected]be5cf822013-07-10 16:36:21171
172 StartGroupingActions();
173 action->Undo();
174 EndGroupingActions();
[email protected]0167050e2014-04-15 21:14:22175
176 NotifyOnUndoManagerStateChange();
177}
178
179void UndoManager::NotifyOnUndoManagerStateChange() {
ericwilligersff2af332016-10-19 00:16:50180 for (auto& observer : observers_)
181 observer.OnUndoManagerStateChange();
[email protected]be5cf822013-07-10 16:36:21182}
183
[email protected]f0541032013-07-31 07:27:53184void UndoManager::AddUndoGroup(UndoGroup* new_undo_group) {
ke.heda6df4de2017-01-26 00:20:03185 GetActiveUndoGroup()->push_back(base::WrapUnique<UndoGroup>(new_undo_group));
[email protected]be5cf822013-07-10 16:36:21186
[email protected]f0541032013-07-31 07:27:53187 // User actions invalidate any available redo actions.
188 if (is_user_action())
189 redo_actions_.clear();
190
191 // Limit the number of undo levels so the undo stack does not grow unbounded.
192 if (GetActiveUndoGroup()->size() > kMaxUndoGroups)
193 GetActiveUndoGroup()->erase(GetActiveUndoGroup()->begin());
[email protected]0167050e2014-04-15 21:14:22194
195 NotifyOnUndoManagerStateChange();
[email protected]be5cf822013-07-10 16:36:21196}
197
ke.heda6df4de2017-01-26 00:20:03198std::vector<std::unique_ptr<UndoGroup>>* UndoManager::GetActiveUndoGroup() {
[email protected]be5cf822013-07-10 16:36:21199 return performing_undo_ ? &redo_actions_ : &undo_actions_;
200}