| OLD | NEW |
| (Empty) |
| 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 "grit/components_strings.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 } | |
| OLD | NEW |