Chromium Code Reviews| Index: chrome/browser/undo/bookmark_undo_service.cc |
| diff --git a/chrome/browser/undo/bookmark_undo_service.cc b/chrome/browser/undo/bookmark_undo_service.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..94335377ac5d73d30c07f7e94839ebde180bccbf |
| --- /dev/null |
| +++ b/chrome/browser/undo/bookmark_undo_service.cc |
| @@ -0,0 +1,394 @@ |
| +// Copyright 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/undo/bookmark_undo_service.h" |
| + |
| +#include "chrome/browser/bookmarks/bookmark_model.h" |
| +#include "chrome/browser/bookmarks/bookmark_model_factory.h" |
| +#include "chrome/browser/bookmarks/bookmark_node_data.h" |
| +#include "chrome/browser/bookmarks/bookmark_utils.h" |
| +#include "chrome/browser/profiles/profile.h" |
| +#include "chrome/browser/undo/bookmark_undo_service_factory.h" |
| +#include "chrome/browser/undo/undo_manager_utils.h" |
| +#include "chrome/browser/undo/undo_operation.h" |
| + |
| +namespace { |
| + |
| +// Helper to get a mutable bookmark node. |
| +BookmarkNode* AsMutable(const BookmarkNode* node) { |
|
sky
2013/07/18 14:35:41
There should be no reason for this. If there is, i
Tom Cassiotis
2013/07/24 15:37:56
AsMutable is used in one place to reorder the chil
sky
2013/07/26 15:29:51
I think BookmarkModel::ReorderChildren should take
|
| + return const_cast<BookmarkNode*>(node); |
| +} |
| + |
| +// BookmarkUndoOperation ------------------------------------------------------ |
| + |
| +// Base class for all bookmark related UndoOperations that facilitates access to |
| +// the BookmarkUndoService. |
| +class BookmarkUndoOperation : public UndoOperation { |
|
sky
2013/07/18 14:35:41
The style guide indicates you should separate decl
Tom Cassiotis
2013/07/24 15:37:56
Done.
|
| + public: |
| + explicit BookmarkUndoOperation(BookmarkUndoService* bookmark_undo) |
| + : bookmark_undo_(bookmark_undo) { |
| + } |
| + virtual ~BookmarkUndoOperation() {} |
| + |
| + BookmarkModel* GetBookmarkModel() { |
| + return bookmark_undo_->GetBookmarkModel(); |
| + } |
| + BookmarkIdMap* GetBookmarkIdMap() { |
| + return &bookmark_undo_->bookmark_id_map_; |
| + } |
| + private: |
| + BookmarkUndoService* bookmark_undo_; |
| +}; |
|
sky
2013/07/18 14:35:41
DISALLOW... and newline between 39/40
Tom Cassiotis
2013/07/24 15:37:56
The reason I did not include a the DISALLOW_ line
|
| + |
| +// BookmarkAddOperation ------------------------------------------------------- |
| + |
| +// Handles the undo of the insertion of a bookmark or folder. |
| +class BookmarkAddOperation : public BookmarkUndoOperation { |
| + public: |
| + BookmarkAddOperation(BookmarkUndoService* bookmark_undo, |
| + const BookmarkNode* parent, |
| + int index); |
| + virtual ~BookmarkAddOperation() {} |
| + |
| + virtual void Undo() OVERRIDE; |
| + |
| + private: |
| + int64 parent_id_; |
|
sky
2013/07/18 14:35:41
Use const where you can in all these classes.
Tom Cassiotis
2013/07/24 15:37:56
I have not seen any parameters or functions that I
|
| + int index_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(BookmarkAddOperation); |
| +}; |
| + |
| +BookmarkAddOperation::BookmarkAddOperation(BookmarkUndoService* bookmark_undo, |
| + const BookmarkNode* parent, |
| + int index) |
| + : BookmarkUndoOperation(bookmark_undo), |
| + parent_id_(parent->id()), |
| + index_(index) { |
| +} |
| + |
| +void BookmarkAddOperation::Undo() { |
| + BookmarkModel* model = GetBookmarkModel(); |
| + BookmarkIdMap* id_map = GetBookmarkIdMap(); |
| + const BookmarkNode* parent = |
| + model->GetNodeByID(id_map->GetCurrentId(parent_id_)); |
| + DCHECK(parent); |
| + |
| + model->Remove(parent, index_); |
| +} |
| + |
| +// BookmarkRemoveOperation ---------------------------------------------------- |
| + |
| +// Handles the undo of the deletion of a bookmark node. For a bookmark folder, |
| +// the information for all descendant bookmark nodes is maintained. |
| +// |
| +// The BookmarkModel allows only single bookmark node to be removed. |
| +class BookmarkRemoveOperation : public BookmarkUndoOperation { |
| + public: |
| + BookmarkRemoveOperation(BookmarkUndoService* undo, |
| + const BookmarkNode* parent, |
| + int old_index, |
| + const BookmarkNode* node); |
| + virtual ~BookmarkRemoveOperation() {} |
| + |
| + virtual void Undo() OVERRIDE; |
| + |
| + private: |
| + int64 parent_id_; |
| + int old_index_; |
| + BookmarkNodeData removed_node_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(BookmarkRemoveOperation); |
| +}; |
| + |
| +BookmarkRemoveOperation::BookmarkRemoveOperation(BookmarkUndoService* undo, |
| + const BookmarkNode* parent, |
| + int old_index, |
| + const BookmarkNode* node) |
| + : BookmarkUndoOperation(undo), |
| + parent_id_(parent->id()), |
| + old_index_(old_index), |
| + removed_node_(node) { |
| +} |
| + |
| +void BookmarkRemoveOperation::Undo() { |
| + DCHECK(removed_node_.is_valid()); |
| + BookmarkModel* model = GetBookmarkModel(); |
| + BookmarkIdMap* id_map = GetBookmarkIdMap(); |
| + const BookmarkNode* parent = |
| + model->GetNodeByID(id_map->GetCurrentId(parent_id_)); |
| + DCHECK(parent); |
| + |
| + bookmark_utils::CloneBookmarkNode(model, |
| + removed_node_.elements, |
| + parent, |
| + old_index_); |
| + id_map->ExtractMappings(model, removed_node_.elements[0], parent, old_index_); |
| +} |
| + |
| +// BookmarkEditOperation ------------------------------------------------------ |
| + |
| +// Handles the undo of the modification of a bookmark node. |
| +class BookmarkEditOperation : public BookmarkUndoOperation { |
| + public: |
| + BookmarkEditOperation(BookmarkUndoService* bookmark_undo, |
| + const BookmarkNode* node); |
| + virtual ~BookmarkEditOperation() {} |
| + |
| + virtual void Undo() OVERRIDE; |
| + |
| + private: |
| + int64 nodeId_; |
| + BookmarkNodeData original_bookmark_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(BookmarkEditOperation); |
| +}; |
| + |
| +BookmarkEditOperation::BookmarkEditOperation(BookmarkUndoService* bookmark_undo, |
| + const BookmarkNode* node) |
| + : BookmarkUndoOperation(bookmark_undo), |
| + nodeId_(node->id()), |
| + original_bookmark_(node) { |
| +} |
| + |
| +void BookmarkEditOperation::Undo() { |
| + DCHECK(original_bookmark_.is_valid()); |
| + BookmarkModel* model = GetBookmarkModel(); |
| + BookmarkIdMap* id_map = GetBookmarkIdMap(); |
| + const BookmarkNode* node = model->GetNodeByID(id_map->GetCurrentId(nodeId_)); |
| + DCHECK(node); |
| + |
| + model->SetTitle(node, original_bookmark_.elements[0].title); |
| + if (original_bookmark_.elements[0].is_url) |
| + model->SetURL(node, original_bookmark_.elements[0].url); |
| +} |
| + |
| +// BookmarkMoveOperation ------------------------------------------------------ |
| + |
| +// Handles the undo of a bookmark being moved to a new location. |
| +class BookmarkMoveOperation : public BookmarkUndoOperation { |
| + public: |
| + BookmarkMoveOperation(BookmarkUndoService* bookmark_undo, |
| + const BookmarkNode* old_parent, |
| + int old_index, |
| + const BookmarkNode* new_parent, |
| + int new_index); |
| + virtual ~BookmarkMoveOperation() {} |
| + |
| + virtual void Undo() OVERRIDE; |
| + |
| + private: |
| + int64 old_parent_id_; |
| + int64 new_parent_id_; |
| + int old_index_; |
| + int new_index_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(BookmarkMoveOperation); |
| +}; |
| + |
| +BookmarkMoveOperation::BookmarkMoveOperation(BookmarkUndoService* bookmark_undo, |
| + const BookmarkNode* old_parent, |
| + int old_index, |
| + const BookmarkNode* new_parent, |
| + int new_index) |
| + : BookmarkUndoOperation(bookmark_undo), |
| + old_parent_id_(old_parent->id()), |
| + old_index_(old_index), |
| + new_parent_id_(new_parent->id()), |
| + new_index_(new_index) { |
| +} |
| + |
| +void BookmarkMoveOperation::Undo() { |
| + BookmarkModel* model = GetBookmarkModel(); |
| + BookmarkIdMap* id_map = GetBookmarkIdMap(); |
| + const BookmarkNode* old_parent = |
| + model->GetNodeByID(id_map->GetCurrentId(old_parent_id_)); |
| + const BookmarkNode* new_parent = |
| + model->GetNodeByID(id_map->GetCurrentId(new_parent_id_)); |
| + DCHECK(old_parent); |
| + DCHECK(new_parent); |
| + |
| + const BookmarkNode* node = new_parent->GetChild(new_index_); |
| + int destination_index = old_index_; |
| + |
| + // If the bookmark was moved up within the same parent then the destination |
| + // index needs to be incremented since the old index did not account for the |
| + // moved bookmark. |
| + if (old_parent == new_parent && new_index_ < old_index_) |
| + ++destination_index; |
| + |
| + model->Move(node, old_parent, destination_index); |
| +} |
| + |
| +// BookmarkReorderOperation --------------------------------------------------- |
| + |
| +// Handle the undo of reordering of bookmarks that can happen as a result of |
| +// sorting a bookmark folder by name or the undo of that operation. The change |
| +// of order is not recursive so only the order of the immediate children of the |
| +// folder need to be restored. |
| +class BookmarkReorderOperation : public BookmarkUndoOperation { |
| + public: |
| + BookmarkReorderOperation(BookmarkUndoService* bookmark_undo, |
| + const BookmarkNode* parent); |
| + virtual ~BookmarkReorderOperation(); |
| + |
| + virtual void Undo() OVERRIDE; |
| + |
| + private: |
| + int64 parent_id_; |
| + std::vector<int64> ordered_bookmarks_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(BookmarkReorderOperation); |
| +}; |
| + |
| +BookmarkReorderOperation::BookmarkReorderOperation(BookmarkUndoService* undo, |
| + const BookmarkNode* parent) |
| + : BookmarkUndoOperation(undo), |
| + parent_id_(parent->id()) { |
| + ordered_bookmarks_.resize(parent->child_count()); |
| + for (int i = 0; i < parent->child_count(); ++i) |
| + ordered_bookmarks_[i] = parent->GetChild(i)->id(); |
| +} |
| + |
| +BookmarkReorderOperation::~BookmarkReorderOperation() { |
| +} |
| + |
| +void BookmarkReorderOperation::Undo() { |
| + BookmarkModel* model = GetBookmarkModel(); |
| + BookmarkIdMap* id_map = GetBookmarkIdMap(); |
| + const BookmarkNode* parent = |
| + model->GetNodeByID(id_map->GetCurrentId(parent_id_)); |
| + DCHECK(parent); |
| + |
| + std::vector<BookmarkNode*> ordered_nodes; |
| + for (size_t i = 0; i < ordered_bookmarks_.size(); ++i) { |
| + const BookmarkNode* node = |
| + model->GetNodeByID(id_map->GetCurrentId(ordered_bookmarks_[i])); |
| + ordered_nodes.push_back(AsMutable(node)); |
| + } |
| + |
| + model->ReorderChildren(parent, ordered_nodes); |
| +} |
| + |
| +} // namespace |
| + |
| +// BookmarkIdMap -------------------------------------------------------------- |
| + |
| +BookmarkIdMap::BookmarkIdMap() { |
| +} |
| + |
| +BookmarkIdMap::~BookmarkIdMap() { |
| +} |
| + |
| +int64 BookmarkIdMap::GetCurrentId(int64 oldIndex) { |
| + int64 currentIndex = oldIndex; |
| + |
| + std::map<int64, int64>::const_iterator it; |
| + while ((it = identifier_map_.find(currentIndex)) != identifier_map_.end()) |
| + currentIndex = it->second; |
| + return currentIndex; |
| +} |
| + |
| +void BookmarkIdMap::AddMapping(int64 old_index, int64 new_index) { |
| + identifier_map_[old_index] = new_index; |
| +} |
| + |
| +void BookmarkIdMap::ExtractMappings(BookmarkModel* model, |
| + const BookmarkNodeData::Element& element, |
| + const BookmarkNode* parent, |
| + int index_added_at) { |
| + const BookmarkNode* node = parent->GetChild(index_added_at); |
| + if (element.id() != node->id()) |
| + AddMapping(element.id(), node->id()); |
| + if (!element.is_url) { |
| + for (int i = 0; i < static_cast<int>(element.children.size()); ++i) |
| + ExtractMappings(model, element.children[i], node, 0); |
| + } |
| +} |
| + |
| +// BookmarkUndoService -------------------------------------------------------- |
| + |
| +BookmarkUndoService::BookmarkUndoService(Profile* profile) |
| + : profile_(profile), |
| + model_(NULL) { |
| + BookmarkModelFactory::GetForProfile(profile_)->AddObserver(this); |
| +} |
| + |
| +BookmarkUndoService::BookmarkUndoService(BookmarkModel* model) |
| + : profile_(NULL), |
| + model_(model) { |
| + model_->AddObserver(this); |
| +} |
| + |
| +BookmarkUndoService::~BookmarkUndoService() { |
| + GetBookmarkModel()->RemoveObserver(this); |
| +} |
| + |
| +BookmarkModel* BookmarkUndoService::GetBookmarkModel() { |
| + return profile_ ? BookmarkModelFactory::GetForProfile(profile_) : model_; |
| +} |
| + |
| +void BookmarkUndoService::Loaded(BookmarkModel* model, bool ids_reassigned) { |
| + undo_manager_.Reset(); |
| +} |
| + |
| +void BookmarkUndoService::BookmarkModelBeingDeleted(BookmarkModel* model) { |
| + undo_manager_.Reset(); |
| +} |
| + |
| +void BookmarkUndoService::BookmarkNodeMoved(BookmarkModel* model, |
| + const BookmarkNode* old_parent, |
| + int old_index, |
| + const BookmarkNode* new_parent, |
| + int new_index) { |
| + scoped_ptr<UndoOperation> op(new BookmarkMoveOperation(this, |
| + old_parent, |
| + old_index, |
| + new_parent, |
| + new_index)); |
| + GetUndoManager()->AddUndoOperation(op.Pass()); |
| +} |
| + |
| +void BookmarkUndoService::BookmarkNodeAdded(BookmarkModel* model, |
| + const BookmarkNode* parent, |
| + int index) { |
| + scoped_ptr<UndoOperation> op(new BookmarkAddOperation(this, parent, index)); |
| + GetUndoManager()->AddUndoOperation(op.Pass()); |
| +} |
| + |
| +void BookmarkUndoService::OnWillRemoveBookmarks(BookmarkModel* model, |
| + const BookmarkNode* parent, |
| + int old_index, |
| + const BookmarkNode* node) { |
| + scoped_ptr<UndoOperation> op(new BookmarkRemoveOperation(this, |
| + parent, |
| + old_index, |
| + node)); |
| + GetUndoManager()->AddUndoOperation(op.Pass()); |
| +} |
| + |
| +void BookmarkUndoService::OnWillRemoveAllBookmarks(BookmarkModel* model) { |
| + ScopedGroupingAction merge_removes(GetUndoManager()); |
| + for (int i = 0; i < model->root_node()->child_count(); ++i) { |
| + const BookmarkNode* permanent_node = model->root_node()->GetChild(i); |
| + for (int j = permanent_node->child_count() - 1; j >= 0; --j) { |
| + scoped_ptr<UndoOperation> op(new BookmarkRemoveOperation(this, |
| + permanent_node, j, permanent_node->GetChild(j))); |
| + GetUndoManager()->AddUndoOperation(op.Pass()); |
| + } |
| + } |
| +} |
| + |
| + |
| +void BookmarkUndoService::OnWillChangeBookmarkNode(BookmarkModel* model, |
| + const BookmarkNode* node) { |
| + scoped_ptr<UndoOperation> op(new BookmarkEditOperation(this, node)); |
| + GetUndoManager()->AddUndoOperation(op.Pass()); |
| +} |
| + |
| +void BookmarkUndoService::OnWillReorderBookmarkNode(BookmarkModel* model, |
| + const BookmarkNode* node) { |
| + scoped_ptr<UndoOperation> op(new BookmarkReorderOperation(this, node)); |
| + GetUndoManager()->AddUndoOperation(op.Pass()); |
| +} |