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 "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