Home | History | Annotate | Download | only in undo
      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