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 18 using bookmarks::BookmarkNodeData; 19 20 namespace { 21 22 // BookmarkUndoOperation ------------------------------------------------------ 23 24 // Base class for all bookmark related UndoOperations that facilitates access to 25 // the BookmarkUndoService. 26 class 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 39 BookmarkUndoOperation::BookmarkUndoOperation(Profile* profile) 40 : profile_(profile) { 41 } 42 43 BookmarkModel* BookmarkUndoOperation::GetBookmarkModel() const { 44 return BookmarkModelFactory::GetForProfile(profile_); 45 } 46 47 BookmarkRenumberObserver* 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. 55 class 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 75 BookmarkAddOperation::BookmarkAddOperation(Profile* profile, 76 const BookmarkNode* parent, 77 int index) 78 : BookmarkUndoOperation(profile), 79 parent_id_(parent->id()), 80 index_(index) { 81 } 82 83 void 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 92 int BookmarkAddOperation::GetUndoLabelId() const { 93 return IDS_BOOKMARK_BAR_UNDO_ADD; 94 } 95 96 int BookmarkAddOperation::GetRedoLabelId() const { 97 return IDS_BOOKMARK_BAR_REDO_DELETE; 98 } 99 100 void 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. 111 class 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 139 BookmarkRemoveOperation::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 149 void 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 161 int BookmarkRemoveOperation::GetUndoLabelId() const { 162 return IDS_BOOKMARK_BAR_UNDO_DELETE; 163 } 164 165 int BookmarkRemoveOperation::GetRedoLabelId() const { 166 return IDS_BOOKMARK_BAR_REDO_ADD; 167 } 168 169 void 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 182 void 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. 190 class 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 211 BookmarkEditOperation::BookmarkEditOperation(Profile* profile, 212 const BookmarkNode* node) 213 : BookmarkUndoOperation(profile), 214 node_id_(node->id()), 215 original_bookmark_(node) { 216 } 217 218 void 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 229 int BookmarkEditOperation::GetUndoLabelId() const { 230 return IDS_BOOKMARK_BAR_UNDO_EDIT; 231 } 232 233 int BookmarkEditOperation::GetRedoLabelId() const { 234 return IDS_BOOKMARK_BAR_REDO_EDIT; 235 } 236 237 void 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. 245 class 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 271 BookmarkMoveOperation::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 283 void 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 304 int BookmarkMoveOperation::GetUndoLabelId() const { 305 return IDS_BOOKMARK_BAR_UNDO_MOVE; 306 } 307 308 int BookmarkMoveOperation::GetRedoLabelId() const { 309 return IDS_BOOKMARK_BAR_REDO_MOVE; 310 } 311 312 void 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. 325 class 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 346 BookmarkReorderOperation::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 355 BookmarkReorderOperation::~BookmarkReorderOperation() { 356 } 357 358 void 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 373 int BookmarkReorderOperation::GetUndoLabelId() const { 374 return IDS_BOOKMARK_BAR_UNDO_REORDER; 375 } 376 377 int BookmarkReorderOperation::GetRedoLabelId() const { 378 return IDS_BOOKMARK_BAR_REDO_REORDER; 379 } 380 381 void 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 395 BookmarkUndoService::BookmarkUndoService(Profile* profile) : profile_(profile) { 396 } 397 398 BookmarkUndoService::~BookmarkUndoService() { 399 BookmarkModelFactory::GetForProfile(profile_)->RemoveObserver(this); 400 } 401 402 void BookmarkUndoService::BookmarkModelLoaded(BookmarkModel* model, 403 bool ids_reassigned) { 404 undo_manager_.RemoveAllOperations(); 405 } 406 407 void BookmarkUndoService::BookmarkModelBeingDeleted(BookmarkModel* model) { 408 undo_manager_.RemoveAllOperations(); 409 } 410 411 void 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 424 void 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 433 void 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 444 void 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 456 void 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 462 void 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 468 void BookmarkUndoService::GroupedBookmarkChangesBeginning( 469 BookmarkModel* model) { 470 undo_manager()->StartGroupingActions(); 471 } 472 473 void BookmarkUndoService::GroupedBookmarkChangesEnded(BookmarkModel* model) { 474 undo_manager()->EndGroupingActions(); 475 } 476 477 void 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