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_operation.h" 10 11 namespace { 12 13 // Maximum number of changes that can be undone. 14 const size_t kMaxUndoGroups = 100; 15 16 } // namespace 17 18 // UndoGroup ------------------------------------------------------------------ 19 20 UndoGroup::UndoGroup() { 21 } 22 23 UndoGroup::~UndoGroup() { 24 } 25 26 void UndoGroup::AddOperation(scoped_ptr<UndoOperation> operation) { 27 operations_.push_back(operation.release()); 28 } 29 30 void UndoGroup::Undo() { 31 for (ScopedVector<UndoOperation>::reverse_iterator ri = operations_.rbegin(); 32 ri != operations_.rend(); ++ri) { 33 (*ri)->Undo(); 34 } 35 } 36 37 // UndoManager ---------------------------------------------------------------- 38 39 UndoManager::UndoManager() 40 : group_actions_count_(0), 41 undo_suspended_count_(0), 42 performing_undo_(false), 43 performing_redo_(false) { 44 } 45 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 53 void UndoManager::Undo() { 54 Undo(&performing_undo_, &undo_actions_); 55 } 56 57 void UndoManager::Redo() { 58 Undo(&performing_redo_, &redo_actions_); 59 } 60 61 void UndoManager::AddUndoOperation(scoped_ptr<UndoOperation> operation) { 62 if (IsUndoTrakingSuspended()) { 63 RemoveAllOperations(); 64 operation.reset(); 65 return; 66 } 67 68 if (group_actions_count_) { 69 pending_grouped_action_->AddOperation(operation.Pass()); 70 } else { 71 UndoGroup* new_action = new UndoGroup(); 72 new_action->AddOperation(operation.Pass()); 73 AddUndoGroup(new_action); 74 } 75 } 76 77 void UndoManager::StartGroupingActions() { 78 if (!group_actions_count_) 79 pending_grouped_action_.reset(new UndoGroup()); 80 ++group_actions_count_; 81 } 82 83 void UndoManager::EndGroupingActions() { 84 --group_actions_count_; 85 if (group_actions_count_ > 0) 86 return; 87 88 // Check that StartGroupingActions and EndGroupingActions are paired. 89 DCHECK_GE(group_actions_count_, 0); 90 91 bool is_user_action = !performing_undo_ && !performing_redo_; 92 if (!pending_grouped_action_->undo_operations().empty()) { 93 AddUndoGroup(pending_grouped_action_.release()); 94 } else { 95 // No changes were executed since we started grouping actions, so the 96 // pending UndoGroup should be discarded. 97 pending_grouped_action_.reset(); 98 99 // This situation is only expected when it is a user initiated action. 100 // Undo/Redo should have at least one operation performed. 101 DCHECK(is_user_action); 102 } 103 } 104 105 void UndoManager::SuspendUndoTracking() { 106 ++undo_suspended_count_; 107 } 108 109 void UndoManager::ResumeUndoTracking() { 110 DCHECK_GT(undo_suspended_count_, 0); 111 --undo_suspended_count_; 112 } 113 114 bool UndoManager::IsUndoTrakingSuspended() const { 115 return undo_suspended_count_ > 0; 116 } 117 118 std::vector<UndoOperation*> UndoManager::GetAllUndoOperations() const { 119 std::vector<UndoOperation*> result; 120 for (size_t i = 0; i < undo_actions_.size(); ++i) { 121 const std::vector<UndoOperation*>& operations = 122 undo_actions_[i]->undo_operations(); 123 result.insert(result.end(), operations.begin(), operations.end()); 124 } 125 for (size_t i = 0; i < redo_actions_.size(); ++i) { 126 const std::vector<UndoOperation*>& operations = 127 redo_actions_[i]->undo_operations(); 128 result.insert(result.end(), operations.begin(), operations.end()); 129 } 130 131 return result; 132 } 133 134 void UndoManager::RemoveAllOperations() { 135 DCHECK(!group_actions_count_); 136 undo_actions_.clear(); 137 redo_actions_.clear(); 138 } 139 140 void UndoManager::Undo(bool* performing_indicator, 141 ScopedVector<UndoGroup>* active_undo_group) { 142 // Check that action grouping has been correctly ended. 143 DCHECK(!group_actions_count_); 144 145 if (active_undo_group->empty()) 146 return; 147 148 base::AutoReset<bool> incoming_changes(performing_indicator, true); 149 scoped_ptr<UndoGroup> action(active_undo_group->back()); 150 active_undo_group->weak_erase( 151 active_undo_group->begin() + active_undo_group->size() - 1); 152 153 StartGroupingActions(); 154 action->Undo(); 155 EndGroupingActions(); 156 } 157 158 void UndoManager::AddUndoGroup(UndoGroup* new_undo_group) { 159 GetActiveUndoGroup()->push_back(new_undo_group); 160 161 // User actions invalidate any available redo actions. 162 if (is_user_action()) 163 redo_actions_.clear(); 164 165 // Limit the number of undo levels so the undo stack does not grow unbounded. 166 if (GetActiveUndoGroup()->size() > kMaxUndoGroups) 167 GetActiveUndoGroup()->erase(GetActiveUndoGroup()->begin()); 168 } 169 170 ScopedVector<UndoGroup>* UndoManager::GetActiveUndoGroup() { 171 return performing_undo_ ? &redo_actions_ : &undo_actions_; 172 } 173