Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / undo / undo_manager.cc
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
5 #include "chrome/browser/undo/undo_manager.h"
6
7 #include "base/auto_reset.h"
8 #include "base/logging.h"
9 #include "chrome/browser/undo/undo_manager_observer.h"
10 #include "chrome/browser/undo/undo_operation.h"
11 #include "chrome/grit/generated_resources.h"
12 #include "ui/base/l10n/l10n_util.h"
13
14 namespace {
15
16 // Maximum number of changes that can be undone.
17 const size_t kMaxUndoGroups = 100;
18
19 }  // namespace
20
21 // UndoGroup ------------------------------------------------------------------
22
23 UndoGroup::UndoGroup()
24     : undo_label_id_(IDS_BOOKMARK_BAR_UNDO),
25       redo_label_id_(IDS_BOOKMARK_BAR_REDO) {
26 }
27
28 UndoGroup::~UndoGroup() {
29 }
30
31 void UndoGroup::AddOperation(scoped_ptr<UndoOperation> operation) {
32   if (operations_.empty()) {
33     set_undo_label_id(operation->GetUndoLabelId());
34     set_redo_label_id(operation->GetRedoLabelId());
35   }
36   operations_.push_back(operation.release());
37 }
38
39 void UndoGroup::Undo() {
40   for (ScopedVector<UndoOperation>::reverse_iterator ri = operations_.rbegin();
41        ri != operations_.rend(); ++ri) {
42     (*ri)->Undo();
43   }
44 }
45
46 // UndoManager ----------------------------------------------------------------
47
48 UndoManager::UndoManager()
49     : group_actions_count_(0),
50       undo_in_progress_action_(NULL),
51       undo_suspended_count_(0),
52       performing_undo_(false),
53       performing_redo_(false) {
54 }
55
56 UndoManager::~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
63 void UndoManager::Undo() {
64   Undo(&performing_undo_, &undo_actions_);
65 }
66
67 void UndoManager::Redo() {
68   Undo(&performing_redo_, &redo_actions_);
69 }
70
71 base::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
77 base::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
83 void UndoManager::AddUndoOperation(scoped_ptr<UndoOperation> operation) {
84   if (IsUndoTrakingSuspended()) {
85     RemoveAllOperations();
86     operation.reset();
87     return;
88   }
89
90   if (group_actions_count_) {
91     pending_grouped_action_->AddOperation(operation.Pass());
92   } else {
93     UndoGroup* new_action = new UndoGroup();
94     new_action->AddOperation(operation.Pass());
95     AddUndoGroup(new_action);
96   }
97 }
98
99 void UndoManager::StartGroupingActions() {
100   if (!group_actions_count_)
101     pending_grouped_action_.reset(new UndoGroup());
102   ++group_actions_count_;
103 }
104
105 void 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_;
114   if (!pending_grouped_action_->undo_operations().empty()) {
115     AddUndoGroup(pending_grouped_action_.release());
116   } 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
127 void UndoManager::SuspendUndoTracking() {
128   ++undo_suspended_count_;
129 }
130
131 void UndoManager::ResumeUndoTracking() {
132   DCHECK_GT(undo_suspended_count_, 0);
133   --undo_suspended_count_;
134 }
135
136 bool UndoManager::IsUndoTrakingSuspended() const {
137   return undo_suspended_count_ > 0;
138 }
139
140 std::vector<UndoOperation*> UndoManager::GetAllUndoOperations() const {
141   std::vector<UndoOperation*> result;
142   for (size_t i = 0; i < undo_actions_.size(); ++i) {
143     const std::vector<UndoOperation*>& operations =
144         undo_actions_[i]->undo_operations();
145     result.insert(result.end(), operations.begin(), operations.end());
146   }
147   for (size_t i = 0; i < redo_actions_.size(); ++i) {
148     const std::vector<UndoOperation*>& operations =
149         redo_actions_[i]->undo_operations();
150     result.insert(result.end(), operations.begin(), operations.end());
151   }
152   // Ensure that if an Undo is in progress the UndoOperations part of that
153   // UndoGroup are included in the returned set. This will ensure that any
154   // changes (such as renumbering) will be applied to any potentially
155   // unprocessed UndoOperations.
156   if (undo_in_progress_action_) {
157     const std::vector<UndoOperation*>& operations =
158         undo_in_progress_action_->undo_operations();
159     result.insert(result.end(), operations.begin(), operations.end());
160   }
161
162   return result;
163 }
164
165 void UndoManager::RemoveAllOperations() {
166   DCHECK(!group_actions_count_);
167   undo_actions_.clear();
168   redo_actions_.clear();
169
170   NotifyOnUndoManagerStateChange();
171 }
172
173 void UndoManager::AddObserver(UndoManagerObserver* observer) {
174   observers_.AddObserver(observer);
175 }
176
177 void UndoManager::RemoveObserver(UndoManagerObserver* observer) {
178   observers_.RemoveObserver(observer);
179 }
180
181 void UndoManager::Undo(bool* performing_indicator,
182                        ScopedVector<UndoGroup>* active_undo_group) {
183   // Check that action grouping has been correctly ended.
184   DCHECK(!group_actions_count_);
185
186   if (active_undo_group->empty())
187     return;
188
189   base::AutoReset<bool> incoming_changes(performing_indicator, true);
190   scoped_ptr<UndoGroup> action(active_undo_group->back());
191   base::AutoReset<UndoGroup*> action_context(&undo_in_progress_action_,
192       action.get());
193   active_undo_group->weak_erase(
194       active_undo_group->begin() + active_undo_group->size() - 1);
195
196   StartGroupingActions();
197   action->Undo();
198   EndGroupingActions();
199
200   NotifyOnUndoManagerStateChange();
201 }
202
203 void UndoManager::NotifyOnUndoManagerStateChange() {
204   FOR_EACH_OBSERVER(
205       UndoManagerObserver, observers_, OnUndoManagerStateChange());
206 }
207
208 void UndoManager::AddUndoGroup(UndoGroup* new_undo_group) {
209   GetActiveUndoGroup()->push_back(new_undo_group);
210
211   // User actions invalidate any available redo actions.
212   if (is_user_action())
213     redo_actions_.clear();
214
215   // Limit the number of undo levels so the undo stack does not grow unbounded.
216   if (GetActiveUndoGroup()->size() > kMaxUndoGroups)
217     GetActiveUndoGroup()->erase(GetActiveUndoGroup()->begin());
218
219   NotifyOnUndoManagerStateChange();
220 }
221
222 ScopedVector<UndoGroup>* UndoManager::GetActiveUndoGroup() {
223   return performing_undo_ ? &redo_actions_ : &undo_actions_;
224 }