Upstream version 8.37.180.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / undo / bookmark_undo_service.cc
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 }