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