blob: e47158d1f94ebf44f63f4e500c4a7b3be6b5c24f [file] [log] [blame]
// 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_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/undo/bookmark_renumber_observer.h"
#include "chrome/browser/undo/bookmark_undo_service_factory.h"
#include "chrome/browser/undo/undo_operation.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/browser/bookmark_node_data.h"
#include "components/bookmarks/browser/bookmark_utils.h"
#include "components/bookmarks/browser/scoped_group_bookmark_actions.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
namespace {
// BookmarkUndoOperation ------------------------------------------------------
// Base class for all bookmark related UndoOperations that facilitates access to
// the BookmarkUndoService.
class BookmarkUndoOperation : public UndoOperation,
public BookmarkRenumberObserver {
public:
explicit BookmarkUndoOperation(Profile* profile);
virtual ~BookmarkUndoOperation() {}
BookmarkModel* GetBookmarkModel() const;
BookmarkRenumberObserver* GetUndoRenumberObserver() const;
private:
Profile* profile_;
};
BookmarkUndoOperation::BookmarkUndoOperation(Profile* profile)
: profile_(profile) {
}
BookmarkModel* BookmarkUndoOperation::GetBookmarkModel() const {
return BookmarkModelFactory::GetForProfile(profile_);
}
BookmarkRenumberObserver* BookmarkUndoOperation::GetUndoRenumberObserver()
const {
return BookmarkUndoServiceFactory::GetForProfile(profile_);
}
// BookmarkAddOperation -------------------------------------------------------
// Handles the undo of the insertion of a bookmark or folder.
class BookmarkAddOperation : public BookmarkUndoOperation {
public:
BookmarkAddOperation(Profile* profile, const BookmarkNode* parent, int index);
virtual ~BookmarkAddOperation() {}
// UndoOperation:
virtual void Undo() OVERRIDE;
virtual int GetUndoLabelId() const OVERRIDE;
virtual int GetRedoLabelId() const OVERRIDE;
// BookmarkRenumberObserver:
virtual void OnBookmarkRenumbered(int64 old_id, int64 new_id) OVERRIDE;
private:
int64 parent_id_;
const int index_;
DISALLOW_COPY_AND_ASSIGN(BookmarkAddOperation);
};
BookmarkAddOperation::BookmarkAddOperation(Profile* profile,
const BookmarkNode* parent,
int index)
: BookmarkUndoOperation(profile),
parent_id_(parent->id()),
index_(index) {
}
void BookmarkAddOperation::Undo() {
BookmarkModel* model = GetBookmarkModel();
const BookmarkNode* parent = GetBookmarkNodeByID(model, parent_id_);
DCHECK(parent);
model->Remove(parent, index_);
}
int BookmarkAddOperation::GetUndoLabelId() const {
return IDS_BOOKMARK_BAR_UNDO_ADD;
}
int BookmarkAddOperation::GetRedoLabelId() const {
return IDS_BOOKMARK_BAR_REDO_DELETE;
}
void BookmarkAddOperation::OnBookmarkRenumbered(int64 old_id, int64 new_id) {
if (parent_id_ == old_id)
parent_id_ = new_id;
}
// 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(Profile* profile,
const BookmarkNode* parent,
int old_index,
const BookmarkNode* node);
virtual ~BookmarkRemoveOperation() {}
// UndoOperation:
virtual void Undo() OVERRIDE;
virtual int GetUndoLabelId() const OVERRIDE;
virtual int GetRedoLabelId() const OVERRIDE;
// BookmarkRenumberObserver:
virtual void OnBookmarkRenumbered(int64 old_id, int64 new_id) OVERRIDE;
private:
void UpdateBookmarkIds(const BookmarkNodeData::Element& element,
const BookmarkNode* parent,
int index_added_at) const;
int64 parent_id_;
const int old_index_;
BookmarkNodeData removed_node_;
DISALLOW_COPY_AND_ASSIGN(BookmarkRemoveOperation);
};
BookmarkRemoveOperation::BookmarkRemoveOperation(Profile* profile,
const BookmarkNode* parent,
int old_index,
const BookmarkNode* node)
: BookmarkUndoOperation(profile),
parent_id_(parent->id()),
old_index_(old_index),
removed_node_(node) {
}
void BookmarkRemoveOperation::Undo() {
DCHECK(removed_node_.is_valid());
BookmarkModel* model = GetBookmarkModel();
const BookmarkNode* parent = GetBookmarkNodeByID(model, parent_id_);
DCHECK(parent);
bookmark_utils::CloneBookmarkNode(model, removed_node_.elements, parent,
old_index_, false);
UpdateBookmarkIds(removed_node_.elements[0], parent, old_index_);
}
int BookmarkRemoveOperation::GetUndoLabelId() const {
return IDS_BOOKMARK_BAR_UNDO_DELETE;
}
int BookmarkRemoveOperation::GetRedoLabelId() const {
return IDS_BOOKMARK_BAR_REDO_ADD;
}
void BookmarkRemoveOperation::UpdateBookmarkIds(
const BookmarkNodeData::Element& element,
const BookmarkNode* parent,
int index_added_at) const {
const BookmarkNode* node = parent->GetChild(index_added_at);
if (element.id() != node->id())
GetUndoRenumberObserver()->OnBookmarkRenumbered(element.id(), node->id());
if (!element.is_url) {
for (int i = 0; i < static_cast<int>(element.children.size()); ++i)
UpdateBookmarkIds(element.children[i], node, 0);
}
}
void BookmarkRemoveOperation::OnBookmarkRenumbered(int64 old_id, int64 new_id) {
if (parent_id_ == old_id)
parent_id_ = new_id;
}
// BookmarkEditOperation ------------------------------------------------------
// Handles the undo of the modification of a bookmark node.
class BookmarkEditOperation : public BookmarkUndoOperation {
public:
BookmarkEditOperation(Profile* profile,
const BookmarkNode* node);
virtual ~BookmarkEditOperation() {}
// UndoOperation:
virtual void Undo() OVERRIDE;
virtual int GetUndoLabelId() const OVERRIDE;
virtual int GetRedoLabelId() const OVERRIDE;
// BookmarkRenumberObserver:
virtual void OnBookmarkRenumbered(int64 old_id, int64 new_id) OVERRIDE;
private:
int64 node_id_;
BookmarkNodeData original_bookmark_;
DISALLOW_COPY_AND_ASSIGN(BookmarkEditOperation);
};
BookmarkEditOperation::BookmarkEditOperation(Profile* profile,
const BookmarkNode* node)
: BookmarkUndoOperation(profile),
node_id_(node->id()),
original_bookmark_(node) {
}
void BookmarkEditOperation::Undo() {
DCHECK(original_bookmark_.is_valid());
BookmarkModel* model = GetBookmarkModel();
const BookmarkNode* node = GetBookmarkNodeByID(model, node_id_);
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);
}
int BookmarkEditOperation::GetUndoLabelId() const {
return IDS_BOOKMARK_BAR_UNDO_EDIT;
}
int BookmarkEditOperation::GetRedoLabelId() const {
return IDS_BOOKMARK_BAR_REDO_EDIT;
}
void BookmarkEditOperation::OnBookmarkRenumbered(int64 old_id, int64 new_id) {
if (node_id_ == old_id)
node_id_ = new_id;
}
// BookmarkMoveOperation ------------------------------------------------------
// Handles the undo of a bookmark being moved to a new location.
class BookmarkMoveOperation : public BookmarkUndoOperation {
public:
BookmarkMoveOperation(Profile* profile,
const BookmarkNode* old_parent,
int old_index,
const BookmarkNode* new_parent,
int new_index);
virtual ~BookmarkMoveOperation() {}
virtual int GetUndoLabelId() const OVERRIDE;
virtual int GetRedoLabelId() const OVERRIDE;
// UndoOperation:
virtual void Undo() OVERRIDE;
// BookmarkRenumberObserver:
virtual void OnBookmarkRenumbered(int64 old_id, int64 new_id) OVERRIDE;
private:
int64 old_parent_id_;
int64 new_parent_id_;
int old_index_;
int new_index_;
DISALLOW_COPY_AND_ASSIGN(BookmarkMoveOperation);
};
BookmarkMoveOperation::BookmarkMoveOperation(Profile* profile,
const BookmarkNode* old_parent,
int old_index,
const BookmarkNode* new_parent,
int new_index)
: BookmarkUndoOperation(profile),
old_parent_id_(old_parent->id()),
new_parent_id_(new_parent->id()),
old_index_(old_index),
new_index_(new_index) {
}
void BookmarkMoveOperation::Undo() {
BookmarkModel* model = GetBookmarkModel();
const BookmarkNode* old_parent = GetBookmarkNodeByID(model, old_parent_id_);
const BookmarkNode* new_parent = GetBookmarkNodeByID(model, 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);
}
int BookmarkMoveOperation::GetUndoLabelId() const {
return IDS_BOOKMARK_BAR_UNDO_MOVE;
}
int BookmarkMoveOperation::GetRedoLabelId() const {
return IDS_BOOKMARK_BAR_REDO_MOVE;
}
void BookmarkMoveOperation::OnBookmarkRenumbered(int64 old_id, int64 new_id) {
if (old_parent_id_ == old_id)
old_parent_id_ = new_id;
if (new_parent_id_ == old_id)
new_parent_id_ = new_id;
}
// 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(Profile* profile,
const BookmarkNode* parent);
virtual ~BookmarkReorderOperation();
// UndoOperation:
virtual void Undo() OVERRIDE;
virtual int GetUndoLabelId() const OVERRIDE;
virtual int GetRedoLabelId() const OVERRIDE;
// BookmarkRenumberObserver:
virtual void OnBookmarkRenumbered(int64 old_id, int64 new_id) OVERRIDE;
private:
int64 parent_id_;
std::vector<int64> ordered_bookmarks_;
DISALLOW_COPY_AND_ASSIGN(BookmarkReorderOperation);
};
BookmarkReorderOperation::BookmarkReorderOperation(Profile* profile,
const BookmarkNode* parent)
: BookmarkUndoOperation(profile),
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();
const BookmarkNode* parent = GetBookmarkNodeByID(model, parent_id_);
DCHECK(parent);
std::vector<const BookmarkNode*> ordered_nodes;
for (size_t i = 0; i < ordered_bookmarks_.size(); ++i)
ordered_nodes.push_back(GetBookmarkNodeByID(model, ordered_bookmarks_[i]));
model->ReorderChildren(parent, ordered_nodes);
}
int BookmarkReorderOperation::GetUndoLabelId() const {
return IDS_BOOKMARK_BAR_UNDO_REORDER;
}
int BookmarkReorderOperation::GetRedoLabelId() const {
return IDS_BOOKMARK_BAR_REDO_REORDER;
}
void BookmarkReorderOperation::OnBookmarkRenumbered(int64 old_id,
int64 new_id) {
if (parent_id_ == old_id)
parent_id_ = new_id;
for (size_t i = 0; i < ordered_bookmarks_.size(); ++i) {
if (ordered_bookmarks_[i] == old_id)
ordered_bookmarks_[i] = new_id;
}
}
} // namespace
// BookmarkUndoService --------------------------------------------------------
BookmarkUndoService::BookmarkUndoService(Profile* profile) : profile_(profile) {
}
BookmarkUndoService::~BookmarkUndoService() {
BookmarkModelFactory::GetForProfile(profile_)->RemoveObserver(this);
}
void BookmarkUndoService::BookmarkModelLoaded(BookmarkModel* model,
bool ids_reassigned) {
undo_manager_.RemoveAllOperations();
}
void BookmarkUndoService::BookmarkModelBeingDeleted(BookmarkModel* model) {
undo_manager_.RemoveAllOperations();
}
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(profile_,
old_parent,
old_index,
new_parent,
new_index));
undo_manager()->AddUndoOperation(op.Pass());
}
void BookmarkUndoService::BookmarkNodeAdded(BookmarkModel* model,
const BookmarkNode* parent,
int index) {
scoped_ptr<UndoOperation> op(new BookmarkAddOperation(profile_,
parent,
index));
undo_manager()->AddUndoOperation(op.Pass());
}
void BookmarkUndoService::OnWillRemoveBookmarks(BookmarkModel* model,
const BookmarkNode* parent,
int old_index,
const BookmarkNode* node) {
scoped_ptr<UndoOperation> op(new BookmarkRemoveOperation(profile_,
parent,
old_index,
node));
undo_manager()->AddUndoOperation(op.Pass());
}
void BookmarkUndoService::OnWillRemoveAllUserBookmarks(BookmarkModel* model) {
bookmarks::ScopedGroupBookmarkActions merge_removes(model);
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(profile_,
permanent_node, j, permanent_node->GetChild(j)));
undo_manager()->AddUndoOperation(op.Pass());
}
}
}
void BookmarkUndoService::OnWillChangeBookmarkNode(BookmarkModel* model,
const BookmarkNode* node) {
scoped_ptr<UndoOperation> op(new BookmarkEditOperation(profile_, node));
undo_manager()->AddUndoOperation(op.Pass());
}
void BookmarkUndoService::OnWillReorderBookmarkNode(BookmarkModel* model,
const BookmarkNode* node) {
scoped_ptr<UndoOperation> op(new BookmarkReorderOperation(profile_, node));
undo_manager()->AddUndoOperation(op.Pass());
}
void BookmarkUndoService::GroupedBookmarkChangesBeginning(
BookmarkModel* model) {
undo_manager()->StartGroupingActions();
}
void BookmarkUndoService::GroupedBookmarkChangesEnded(BookmarkModel* model) {
undo_manager()->EndGroupingActions();
}
void BookmarkUndoService::OnBookmarkRenumbered(int64 old_id, int64 new_id) {
std::vector<UndoOperation*> all_operations =
undo_manager()->GetAllUndoOperations();
for (std::vector<UndoOperation*>::iterator it = all_operations.begin();
it != all_operations.end(); ++it) {
static_cast<BookmarkUndoOperation*>(*it)->OnBookmarkRenumbered(old_id,
new_id);
}
}