NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / undo / undo_manager.cc
blob89ffe30b99fc9e14a5d5e49520277d897ddaad8e
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.
5 #include "chrome/browser/undo/undo_manager.h"
7 #include "base/auto_reset.h"
8 #include "base/logging.h"
9 #include "chrome/browser/undo/undo_operation.h"
10 #include "grit/generated_resources.h"
11 #include "ui/base/l10n/l10n_util.h"
13 namespace {
15 // Maximum number of changes that can be undone.
16 const size_t kMaxUndoGroups = 100;
18 } // namespace
20 // UndoGroup ------------------------------------------------------------------
22 UndoGroup::UndoGroup()
23 : undo_label_id_(IDS_BOOKMARK_BAR_UNDO),
24 redo_label_id_(IDS_BOOKMARK_BAR_REDO) {
27 UndoGroup::~UndoGroup() {
30 void UndoGroup::AddOperation(scoped_ptr<UndoOperation> operation) {
31 if (operations_.empty()) {
32 set_undo_label_id(operation->GetUndoLabelId());
33 set_redo_label_id(operation->GetRedoLabelId());
35 operations_.push_back(operation.release());
38 void UndoGroup::Undo() {
39 for (ScopedVector<UndoOperation>::reverse_iterator ri = operations_.rbegin();
40 ri != operations_.rend(); ++ri) {
41 (*ri)->Undo();
45 // UndoManager ----------------------------------------------------------------
47 UndoManager::UndoManager()
48 : group_actions_count_(0),
49 undo_in_progress_action_(NULL),
50 undo_suspended_count_(0),
51 performing_undo_(false),
52 performing_redo_(false) {
55 UndoManager::~UndoManager() {
56 DCHECK_EQ(0, group_actions_count_);
57 DCHECK_EQ(0, undo_suspended_count_);
58 DCHECK(!performing_undo_);
59 DCHECK(!performing_redo_);
62 void UndoManager::Undo() {
63 Undo(&performing_undo_, &undo_actions_);
66 void UndoManager::Redo() {
67 Undo(&performing_redo_, &redo_actions_);
70 base::string16 UndoManager::GetUndoLabel() const {
71 return l10n_util::GetStringUTF16(
72 undo_actions_.empty() ? IDS_BOOKMARK_BAR_UNDO
73 : undo_actions_.back()->get_undo_label_id());
76 base::string16 UndoManager::GetRedoLabel() const {
77 return l10n_util::GetStringUTF16(
78 redo_actions_.empty() ? IDS_BOOKMARK_BAR_REDO
79 : redo_actions_.back()->get_redo_label_id());
82 void UndoManager::AddUndoOperation(scoped_ptr<UndoOperation> operation) {
83 if (IsUndoTrakingSuspended()) {
84 RemoveAllOperations();
85 operation.reset();
86 return;
89 if (group_actions_count_) {
90 pending_grouped_action_->AddOperation(operation.Pass());
91 } else {
92 UndoGroup* new_action = new UndoGroup();
93 new_action->AddOperation(operation.Pass());
94 AddUndoGroup(new_action);
98 void UndoManager::StartGroupingActions() {
99 if (!group_actions_count_)
100 pending_grouped_action_.reset(new UndoGroup());
101 ++group_actions_count_;
104 void UndoManager::EndGroupingActions() {
105 --group_actions_count_;
106 if (group_actions_count_ > 0)
107 return;
109 // Check that StartGroupingActions and EndGroupingActions are paired.
110 DCHECK_GE(group_actions_count_, 0);
112 bool is_user_action = !performing_undo_ && !performing_redo_;
113 if (!pending_grouped_action_->undo_operations().empty()) {
114 AddUndoGroup(pending_grouped_action_.release());
115 } else {
116 // No changes were executed since we started grouping actions, so the
117 // pending UndoGroup should be discarded.
118 pending_grouped_action_.reset();
120 // This situation is only expected when it is a user initiated action.
121 // Undo/Redo should have at least one operation performed.
122 DCHECK(is_user_action);
126 void UndoManager::SuspendUndoTracking() {
127 ++undo_suspended_count_;
130 void UndoManager::ResumeUndoTracking() {
131 DCHECK_GT(undo_suspended_count_, 0);
132 --undo_suspended_count_;
135 bool UndoManager::IsUndoTrakingSuspended() const {
136 return undo_suspended_count_ > 0;
139 std::vector<UndoOperation*> UndoManager::GetAllUndoOperations() const {
140 std::vector<UndoOperation*> result;
141 for (size_t i = 0; i < undo_actions_.size(); ++i) {
142 const std::vector<UndoOperation*>& operations =
143 undo_actions_[i]->undo_operations();
144 result.insert(result.end(), operations.begin(), operations.end());
146 for (size_t i = 0; i < redo_actions_.size(); ++i) {
147 const std::vector<UndoOperation*>& operations =
148 redo_actions_[i]->undo_operations();
149 result.insert(result.end(), operations.begin(), operations.end());
151 // Ensure that if an Undo is in progress the UndoOperations part of that
152 // UndoGroup are included in the returned set. This will ensure that any
153 // changes (such as renumbering) will be applied to any potentially
154 // unprocessed UndoOperations.
155 if (undo_in_progress_action_) {
156 const std::vector<UndoOperation*>& operations =
157 undo_in_progress_action_->undo_operations();
158 result.insert(result.end(), operations.begin(), operations.end());
161 return result;
164 void UndoManager::RemoveAllOperations() {
165 DCHECK(!group_actions_count_);
166 undo_actions_.clear();
167 redo_actions_.clear();
170 void UndoManager::Undo(bool* performing_indicator,
171 ScopedVector<UndoGroup>* active_undo_group) {
172 // Check that action grouping has been correctly ended.
173 DCHECK(!group_actions_count_);
175 if (active_undo_group->empty())
176 return;
178 base::AutoReset<bool> incoming_changes(performing_indicator, true);
179 scoped_ptr<UndoGroup> action(active_undo_group->back());
180 base::AutoReset<UndoGroup*> action_context(&undo_in_progress_action_,
181 action.get());
182 active_undo_group->weak_erase(
183 active_undo_group->begin() + active_undo_group->size() - 1);
185 StartGroupingActions();
186 action->Undo();
187 EndGroupingActions();
190 void UndoManager::AddUndoGroup(UndoGroup* new_undo_group) {
191 GetActiveUndoGroup()->push_back(new_undo_group);
193 // User actions invalidate any available redo actions.
194 if (is_user_action())
195 redo_actions_.clear();
197 // Limit the number of undo levels so the undo stack does not grow unbounded.
198 if (GetActiveUndoGroup()->size() > kMaxUndoGroups)
199 GetActiveUndoGroup()->erase(GetActiveUndoGroup()->begin());
202 ScopedVector<UndoGroup>* UndoManager::GetActiveUndoGroup() {
203 return performing_undo_ ? &redo_actions_ : &undo_actions_;