blob: 6b1c3de33df11abea08858ab16a2aaa185c2b14c [file] [log] [blame]
[email protected]be5cf822013-07-10 16:36:211// 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// UndoGroup ------------------------------------------------------------------
12
13UndoGroup::UndoGroup() {
14}
15
16UndoGroup::~UndoGroup() {
17}
18
19void UndoGroup::AddOperation(scoped_ptr<UndoOperation> operation) {
20 operations_.push_back(operation.release());
21}
22
23void UndoGroup::Undo() {
24 for (ScopedVector<UndoOperation>::reverse_iterator ri = operations_.rbegin();
25 ri != operations_.rend(); ++ri) {
26 (*ri)->Undo();
27 }
28}
29
30// UndoManager ----------------------------------------------------------------
31
32UndoManager::UndoManager()
33 : group_actions_count_(0),
34 undo_suspended_count_(0),
35 performing_undo_(false),
36 performing_redo_(false) {
37}
38
39UndoManager::~UndoManager() {
40 DCHECK_EQ(0, group_actions_count_);
41 DCHECK_EQ(0, undo_suspended_count_);
42 DCHECK(!performing_undo_);
43 DCHECK(!performing_redo_);
44}
45
46void UndoManager::Undo() {
47 Undo(&performing_undo_, &undo_actions_);
48}
49
50void UndoManager::Redo() {
51 Undo(&performing_redo_, &redo_actions_);
52}
53
54void UndoManager::AddUndoOperation(scoped_ptr<UndoOperation> operation) {
55 if (IsUndoTrakingSuspended()) {
56 RemoveAllActions();
57 operation.reset();
58 return;
59 }
60
61 if (group_actions_count_) {
62 pending_grouped_action_->AddOperation(operation.Pass());
63 } else {
64 UndoGroup* new_action = new UndoGroup();
65 new_action->AddOperation(operation.Pass());
66 GetActiveUndoGroup()->insert(GetActiveUndoGroup()->end(), new_action);
67
68 // A new user action invalidates any available redo actions.
69 RemoveAllRedoActions();
70 }
71}
72
73void UndoManager::StartGroupingActions() {
74 if (!group_actions_count_)
75 pending_grouped_action_.reset(new UndoGroup());
76 ++group_actions_count_;
77}
78
79void UndoManager::EndGroupingActions() {
80 --group_actions_count_;
81 if (group_actions_count_ > 0)
82 return;
83
84 // Check that StartGroupingActions and EndGroupingActions are paired.
85 DCHECK_GE(group_actions_count_, 0);
86
87 bool is_user_action = !performing_undo_ && !performing_redo_;
88 if (pending_grouped_action_->has_operations()) {
89 GetActiveUndoGroup()->push_back(pending_grouped_action_.release());
90 // User actions invalidate any available redo actions.
91 if (is_user_action)
92 RemoveAllRedoActions();
93 } else {
94 // No changes were executed since we started grouping actions, so the
95 // pending UndoGroup should be discarded.
96 pending_grouped_action_.reset();
97
98 // This situation is only expected when it is a user initiated action.
99 // Undo/Redo should have at least one operation performed.
100 DCHECK(is_user_action);
101 }
102}
103
104void UndoManager::SuspendUndoTracking() {
105 ++undo_suspended_count_;
106}
107
108void UndoManager::ResumeUndoTracking() {
109 DCHECK_GT(undo_suspended_count_, 0);
110 --undo_suspended_count_;
111}
112
113bool UndoManager::IsUndoTrakingSuspended() const {
114 return undo_suspended_count_ > 0;
115}
116
117void UndoManager::Undo(bool* performing_indicator,
118 ScopedVector<UndoGroup>* active_undo_group) {
119 // Check that action grouping has been correctly ended.
120 DCHECK(!group_actions_count_);
121
122 if (active_undo_group->empty())
123 return;
124
125 base::AutoReset<bool> incoming_changes(performing_indicator, true);
126 scoped_ptr<UndoGroup> action(active_undo_group->back());
127 active_undo_group->weak_erase(
128 active_undo_group->begin() + active_undo_group->size() - 1);
129
130 StartGroupingActions();
131 action->Undo();
132 EndGroupingActions();
133}
134
135void UndoManager::RemoveAllActions() {
136 undo_actions_.clear();
137 RemoveAllRedoActions();
138}
139
140void UndoManager::RemoveAllRedoActions() {
141 redo_actions_.clear();
142}
143
144ScopedVector<UndoGroup>* UndoManager::GetActiveUndoGroup() {
145 return performing_undo_ ? &redo_actions_ : &undo_actions_;
146}