Home | History | Annotate | Download | only in undo
      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