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/bookmark_undo_service.h" 6 7#include "chrome/browser/bookmarks/bookmark_model_factory.h" 8#include "chrome/browser/profiles/profile.h" 9#include "chrome/browser/undo/bookmark_renumber_observer.h" 10#include "chrome/browser/undo/bookmark_undo_service_factory.h" 11#include "chrome/browser/undo/undo_operation.h" 12#include "chrome/grit/generated_resources.h" 13#include "components/bookmarks/browser/bookmark_model.h" 14#include "components/bookmarks/browser/bookmark_node_data.h" 15#include "components/bookmarks/browser/bookmark_utils.h" 16#include "components/bookmarks/browser/scoped_group_bookmark_actions.h" 17 18using bookmarks::BookmarkNodeData; 19 20namespace { 21 22// BookmarkUndoOperation ------------------------------------------------------ 23 24// Base class for all bookmark related UndoOperations that facilitates access to 25// the BookmarkUndoService. 26class BookmarkUndoOperation : public UndoOperation, 27 public BookmarkRenumberObserver { 28 public: 29 explicit BookmarkUndoOperation(Profile* profile); 30 virtual ~BookmarkUndoOperation() {} 31 32 BookmarkModel* GetBookmarkModel() const; 33 BookmarkRenumberObserver* GetUndoRenumberObserver() const; 34 35 private: 36 Profile* profile_; 37}; 38 39BookmarkUndoOperation::BookmarkUndoOperation(Profile* profile) 40 : profile_(profile) { 41} 42 43BookmarkModel* BookmarkUndoOperation::GetBookmarkModel() const { 44 return BookmarkModelFactory::GetForProfile(profile_); 45} 46 47BookmarkRenumberObserver* BookmarkUndoOperation::GetUndoRenumberObserver() 48 const { 49 return BookmarkUndoServiceFactory::GetForProfile(profile_); 50} 51 52// BookmarkAddOperation ------------------------------------------------------- 53 54// Handles the undo of the insertion of a bookmark or folder. 55class BookmarkAddOperation : public BookmarkUndoOperation { 56 public: 57 BookmarkAddOperation(Profile* profile, const BookmarkNode* parent, int index); 58 virtual ~BookmarkAddOperation() {} 59 60 // UndoOperation: 61 virtual void Undo() OVERRIDE; 62 virtual int GetUndoLabelId() const OVERRIDE; 63 virtual int GetRedoLabelId() const OVERRIDE; 64 65 // BookmarkRenumberObserver: 66 virtual void OnBookmarkRenumbered(int64 old_id, int64 new_id) OVERRIDE; 67 68 private: 69 int64 parent_id_; 70 const int index_; 71 72 DISALLOW_COPY_AND_ASSIGN(BookmarkAddOperation); 73}; 74 75BookmarkAddOperation::BookmarkAddOperation(Profile* profile, 76 const BookmarkNode* parent, 77 int index) 78 : BookmarkUndoOperation(profile), 79 parent_id_(parent->id()), 80 index_(index) { 81} 82 83void BookmarkAddOperation::Undo() { 84 BookmarkModel* model = GetBookmarkModel(); 85 const BookmarkNode* parent = 86 bookmarks::GetBookmarkNodeByID(model, parent_id_); 87 DCHECK(parent); 88 89 model->Remove(parent, index_); 90} 91 92int BookmarkAddOperation::GetUndoLabelId() const { 93 return IDS_BOOKMARK_BAR_UNDO_ADD; 94} 95 96int BookmarkAddOperation::GetRedoLabelId() const { 97 return IDS_BOOKMARK_BAR_REDO_DELETE; 98} 99 100void BookmarkAddOperation::OnBookmarkRenumbered(int64 old_id, int64 new_id) { 101 if (parent_id_ == old_id) 102 parent_id_ = new_id; 103} 104 105// BookmarkRemoveOperation ---------------------------------------------------- 106 107// Handles the undo of the deletion of a bookmark node. For a bookmark folder, 108// the information for all descendant bookmark nodes is maintained. 109// 110// The BookmarkModel allows only single bookmark node to be removed. 111class BookmarkRemoveOperation : public BookmarkUndoOperation { 112 public: 113 BookmarkRemoveOperation(Profile* profile, 114 const BookmarkNode* parent, 115 int old_index, 116 const BookmarkNode* node); 117 virtual ~BookmarkRemoveOperation() {} 118 119 // UndoOperation: 120 virtual void Undo() OVERRIDE; 121 virtual int GetUndoLabelId() const OVERRIDE; 122 virtual int GetRedoLabelId() const OVERRIDE; 123 124 // BookmarkRenumberObserver: 125 virtual void OnBookmarkRenumbered(int64 old_id, int64 new_id) OVERRIDE; 126 127 private: 128 void UpdateBookmarkIds(const BookmarkNodeData::Element& element, 129 const BookmarkNode* parent, 130 int index_added_at) const; 131 132 int64 parent_id_; 133 const int old_index_; 134 BookmarkNodeData removed_node_; 135 136 DISALLOW_COPY_AND_ASSIGN(BookmarkRemoveOperation); 137}; 138 139BookmarkRemoveOperation::BookmarkRemoveOperation(Profile* profile, 140 const BookmarkNode* parent, 141 int old_index, 142 const BookmarkNode* node) 143 : BookmarkUndoOperation(profile), 144 parent_id_(parent->id()), 145 old_index_(old_index), 146 removed_node_(node) { 147} 148 149void BookmarkRemoveOperation::Undo() { 150 DCHECK(removed_node_.is_valid()); 151 BookmarkModel* model = GetBookmarkModel(); 152 const BookmarkNode* parent = 153 bookmarks::GetBookmarkNodeByID(model, parent_id_); 154 DCHECK(parent); 155 156 bookmarks::CloneBookmarkNode( 157 model, removed_node_.elements, parent, old_index_, false); 158 UpdateBookmarkIds(removed_node_.elements[0], parent, old_index_); 159} 160 161int BookmarkRemoveOperation::GetUndoLabelId() const { 162 return IDS_BOOKMARK_BAR_UNDO_DELETE; 163} 164 165int BookmarkRemoveOperation::GetRedoLabelId() const { 166 return IDS_BOOKMARK_BAR_REDO_ADD; 167} 168 169void BookmarkRemoveOperation::UpdateBookmarkIds( 170 const BookmarkNodeData::Element& element, 171 const BookmarkNode* parent, 172 int index_added_at) const { 173 const BookmarkNode* node = parent->GetChild(index_added_at); 174 if (element.id() != node->id()) 175 GetUndoRenumberObserver()->OnBookmarkRenumbered(element.id(), node->id()); 176 if (!element.is_url) { 177 for (int i = 0; i < static_cast<int>(element.children.size()); ++i) 178 UpdateBookmarkIds(element.children[i], node, 0); 179 } 180} 181 182void BookmarkRemoveOperation::OnBookmarkRenumbered(int64 old_id, int64 new_id) { 183 if (parent_id_ == old_id) 184 parent_id_ = new_id; 185} 186 187// BookmarkEditOperation ------------------------------------------------------ 188 189// Handles the undo of the modification of a bookmark node. 190class BookmarkEditOperation : public BookmarkUndoOperation { 191 public: 192 BookmarkEditOperation(Profile* profile, 193 const BookmarkNode* node); 194 virtual ~BookmarkEditOperation() {} 195 196 // UndoOperation: 197 virtual void Undo() OVERRIDE; 198 virtual int GetUndoLabelId() const OVERRIDE; 199 virtual int GetRedoLabelId() const OVERRIDE; 200 201 // BookmarkRenumberObserver: 202 virtual void OnBookmarkRenumbered(int64 old_id, int64 new_id) OVERRIDE; 203 204 private: 205 int64 node_id_; 206 BookmarkNodeData original_bookmark_; 207 208 DISALLOW_COPY_AND_ASSIGN(BookmarkEditOperation); 209}; 210 211BookmarkEditOperation::BookmarkEditOperation(Profile* profile, 212 const BookmarkNode* node) 213 : BookmarkUndoOperation(profile), 214 node_id_(node->id()), 215 original_bookmark_(node) { 216} 217 218void BookmarkEditOperation::Undo() { 219 DCHECK(original_bookmark_.is_valid()); 220 BookmarkModel* model = GetBookmarkModel(); 221 const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(model, node_id_); 222 DCHECK(node); 223 224 model->SetTitle(node, original_bookmark_.elements[0].title); 225 if (original_bookmark_.elements[0].is_url) 226 model->SetURL(node, original_bookmark_.elements[0].url); 227} 228 229int BookmarkEditOperation::GetUndoLabelId() const { 230 return IDS_BOOKMARK_BAR_UNDO_EDIT; 231} 232 233int BookmarkEditOperation::GetRedoLabelId() const { 234 return IDS_BOOKMARK_BAR_REDO_EDIT; 235} 236 237void BookmarkEditOperation::OnBookmarkRenumbered(int64 old_id, int64 new_id) { 238 if (node_id_ == old_id) 239 node_id_ = new_id; 240} 241 242// BookmarkMoveOperation ------------------------------------------------------ 243 244// Handles the undo of a bookmark being moved to a new location. 245class BookmarkMoveOperation : public BookmarkUndoOperation { 246 public: 247 BookmarkMoveOperation(Profile* profile, 248 const BookmarkNode* old_parent, 249 int old_index, 250 const BookmarkNode* new_parent, 251 int new_index); 252 virtual ~BookmarkMoveOperation() {} 253 virtual int GetUndoLabelId() const OVERRIDE; 254 virtual int GetRedoLabelId() const OVERRIDE; 255 256 // UndoOperation: 257 virtual void Undo() OVERRIDE; 258 259 // BookmarkRenumberObserver: 260 virtual void OnBookmarkRenumbered(int64 old_id, int64 new_id) OVERRIDE; 261 262 private: 263 int64 old_parent_id_; 264 int64 new_parent_id_; 265 int old_index_; 266 int new_index_; 267 268 DISALLOW_COPY_AND_ASSIGN(BookmarkMoveOperation); 269}; 270 271BookmarkMoveOperation::BookmarkMoveOperation(Profile* profile, 272 const BookmarkNode* old_parent, 273 int old_index, 274 const BookmarkNode* new_parent, 275 int new_index) 276 : BookmarkUndoOperation(profile), 277 old_parent_id_(old_parent->id()), 278 new_parent_id_(new_parent->id()), 279 old_index_(old_index), 280 new_index_(new_index) { 281} 282 283void BookmarkMoveOperation::Undo() { 284 BookmarkModel* model = GetBookmarkModel(); 285 const BookmarkNode* old_parent = 286 bookmarks::GetBookmarkNodeByID(model, old_parent_id_); 287 const BookmarkNode* new_parent = 288 bookmarks::GetBookmarkNodeByID(model, new_parent_id_); 289 DCHECK(old_parent); 290 DCHECK(new_parent); 291 292 const BookmarkNode* node = new_parent->GetChild(new_index_); 293 int destination_index = old_index_; 294 295 // If the bookmark was moved up within the same parent then the destination 296 // index needs to be incremented since the old index did not account for the 297 // moved bookmark. 298 if (old_parent == new_parent && new_index_ < old_index_) 299 ++destination_index; 300 301 model->Move(node, old_parent, destination_index); 302} 303 304int BookmarkMoveOperation::GetUndoLabelId() const { 305 return IDS_BOOKMARK_BAR_UNDO_MOVE; 306} 307 308int BookmarkMoveOperation::GetRedoLabelId() const { 309 return IDS_BOOKMARK_BAR_REDO_MOVE; 310} 311 312void BookmarkMoveOperation::OnBookmarkRenumbered(int64 old_id, int64 new_id) { 313 if (old_parent_id_ == old_id) 314 old_parent_id_ = new_id; 315 if (new_parent_id_ == old_id) 316 new_parent_id_ = new_id; 317} 318 319// BookmarkReorderOperation --------------------------------------------------- 320 321// Handle the undo of reordering of bookmarks that can happen as a result of 322// sorting a bookmark folder by name or the undo of that operation. The change 323// of order is not recursive so only the order of the immediate children of the 324// folder need to be restored. 325class BookmarkReorderOperation : public BookmarkUndoOperation { 326 public: 327 BookmarkReorderOperation(Profile* profile, 328 const BookmarkNode* parent); 329 virtual ~BookmarkReorderOperation(); 330 331 // UndoOperation: 332 virtual void Undo() OVERRIDE; 333 virtual int GetUndoLabelId() const OVERRIDE; 334 virtual int GetRedoLabelId() const OVERRIDE; 335 336 // BookmarkRenumberObserver: 337 virtual void OnBookmarkRenumbered(int64 old_id, int64 new_id) OVERRIDE; 338 339 private: 340 int64 parent_id_; 341 std::vector<int64> ordered_bookmarks_; 342 343 DISALLOW_COPY_AND_ASSIGN(BookmarkReorderOperation); 344}; 345 346BookmarkReorderOperation::BookmarkReorderOperation(Profile* profile, 347 const BookmarkNode* parent) 348 : BookmarkUndoOperation(profile), 349 parent_id_(parent->id()) { 350 ordered_bookmarks_.resize(parent->child_count()); 351 for (int i = 0; i < parent->child_count(); ++i) 352 ordered_bookmarks_[i] = parent->GetChild(i)->id(); 353} 354 355BookmarkReorderOperation::~BookmarkReorderOperation() { 356} 357 358void BookmarkReorderOperation::Undo() { 359 BookmarkModel* model = GetBookmarkModel(); 360 const BookmarkNode* parent = 361 bookmarks::GetBookmarkNodeByID(model, parent_id_); 362 DCHECK(parent); 363 364 std::vector<const BookmarkNode*> ordered_nodes; 365 for (size_t i = 0; i < ordered_bookmarks_.size(); ++i) { 366 ordered_nodes.push_back( 367 bookmarks::GetBookmarkNodeByID(model, ordered_bookmarks_[i])); 368 } 369 370 model->ReorderChildren(parent, ordered_nodes); 371} 372 373int BookmarkReorderOperation::GetUndoLabelId() const { 374 return IDS_BOOKMARK_BAR_UNDO_REORDER; 375} 376 377int BookmarkReorderOperation::GetRedoLabelId() const { 378 return IDS_BOOKMARK_BAR_REDO_REORDER; 379} 380 381void BookmarkReorderOperation::OnBookmarkRenumbered(int64 old_id, 382 int64 new_id) { 383 if (parent_id_ == old_id) 384 parent_id_ = new_id; 385 for (size_t i = 0; i < ordered_bookmarks_.size(); ++i) { 386 if (ordered_bookmarks_[i] == old_id) 387 ordered_bookmarks_[i] = new_id; 388 } 389} 390 391} // namespace 392 393// BookmarkUndoService -------------------------------------------------------- 394 395BookmarkUndoService::BookmarkUndoService(Profile* profile) : profile_(profile) { 396} 397 398BookmarkUndoService::~BookmarkUndoService() { 399 BookmarkModelFactory::GetForProfile(profile_)->RemoveObserver(this); 400} 401 402void BookmarkUndoService::BookmarkModelLoaded(BookmarkModel* model, 403 bool ids_reassigned) { 404 undo_manager_.RemoveAllOperations(); 405} 406 407void BookmarkUndoService::BookmarkModelBeingDeleted(BookmarkModel* model) { 408 undo_manager_.RemoveAllOperations(); 409} 410 411void BookmarkUndoService::BookmarkNodeMoved(BookmarkModel* model, 412 const BookmarkNode* old_parent, 413 int old_index, 414 const BookmarkNode* new_parent, 415 int new_index) { 416 scoped_ptr<UndoOperation> op(new BookmarkMoveOperation(profile_, 417 old_parent, 418 old_index, 419 new_parent, 420 new_index)); 421 undo_manager()->AddUndoOperation(op.Pass()); 422} 423 424void BookmarkUndoService::BookmarkNodeAdded(BookmarkModel* model, 425 const BookmarkNode* parent, 426 int index) { 427 scoped_ptr<UndoOperation> op(new BookmarkAddOperation(profile_, 428 parent, 429 index)); 430 undo_manager()->AddUndoOperation(op.Pass()); 431} 432 433void BookmarkUndoService::OnWillRemoveBookmarks(BookmarkModel* model, 434 const BookmarkNode* parent, 435 int old_index, 436 const BookmarkNode* node) { 437 scoped_ptr<UndoOperation> op(new BookmarkRemoveOperation(profile_, 438 parent, 439 old_index, 440 node)); 441 undo_manager()->AddUndoOperation(op.Pass()); 442} 443 444void BookmarkUndoService::OnWillRemoveAllUserBookmarks(BookmarkModel* model) { 445 bookmarks::ScopedGroupBookmarkActions merge_removes(model); 446 for (int i = 0; i < model->root_node()->child_count(); ++i) { 447 const BookmarkNode* permanent_node = model->root_node()->GetChild(i); 448 for (int j = permanent_node->child_count() - 1; j >= 0; --j) { 449 scoped_ptr<UndoOperation> op(new BookmarkRemoveOperation(profile_, 450 permanent_node, j, permanent_node->GetChild(j))); 451 undo_manager()->AddUndoOperation(op.Pass()); 452 } 453 } 454} 455 456void BookmarkUndoService::OnWillChangeBookmarkNode(BookmarkModel* model, 457 const BookmarkNode* node) { 458 scoped_ptr<UndoOperation> op(new BookmarkEditOperation(profile_, node)); 459 undo_manager()->AddUndoOperation(op.Pass()); 460} 461 462void BookmarkUndoService::OnWillReorderBookmarkNode(BookmarkModel* model, 463 const BookmarkNode* node) { 464 scoped_ptr<UndoOperation> op(new BookmarkReorderOperation(profile_, node)); 465 undo_manager()->AddUndoOperation(op.Pass()); 466} 467 468void BookmarkUndoService::GroupedBookmarkChangesBeginning( 469 BookmarkModel* model) { 470 undo_manager()->StartGroupingActions(); 471} 472 473void BookmarkUndoService::GroupedBookmarkChangesEnded(BookmarkModel* model) { 474 undo_manager()->EndGroupingActions(); 475} 476 477void BookmarkUndoService::OnBookmarkRenumbered(int64 old_id, int64 new_id) { 478 std::vector<UndoOperation*> all_operations = 479 undo_manager()->GetAllUndoOperations(); 480 for (std::vector<UndoOperation*>::iterator it = all_operations.begin(); 481 it != all_operations.end(); ++it) { 482 static_cast<BookmarkUndoOperation*>(*it)->OnBookmarkRenumbered(old_id, 483 new_id); 484 } 485} 486