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.
5 #include "chrome/browser/undo/bookmark_undo_service.h"
7 #include "chrome/browser/bookmarks/bookmark_model.h"
8 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
9 #include "chrome/browser/bookmarks/bookmark_node_data.h"
10 #include "chrome/browser/bookmarks/bookmark_utils.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/undo/bookmark_renumber_observer.h"
13 #include "chrome/browser/undo/bookmark_undo_service_factory.h"
14 #include "chrome/browser/undo/bookmark_undo_utils.h"
15 #include "chrome/browser/undo/undo_operation.h"
16 #include "grit/generated_resources.h"
17 #include "ui/base/l10n/l10n_util.h"
21 // BookmarkUndoOperation ------------------------------------------------------
23 // Base class for all bookmark related UndoOperations that facilitates access to
24 // the BookmarkUndoService.
25 class BookmarkUndoOperation
: public UndoOperation
,
26 public BookmarkRenumberObserver
{
28 explicit BookmarkUndoOperation(Profile
* profile
);
29 virtual ~BookmarkUndoOperation() {}
31 BookmarkModel
* GetBookmarkModel() const;
32 BookmarkRenumberObserver
* GetUndoRenumberObserver() const;
38 BookmarkUndoOperation::BookmarkUndoOperation(Profile
* profile
)
42 BookmarkModel
* BookmarkUndoOperation::GetBookmarkModel() const {
43 return BookmarkModelFactory::GetForProfile(profile_
);
46 BookmarkRenumberObserver
* BookmarkUndoOperation::GetUndoRenumberObserver()
48 return BookmarkUndoServiceFactory::GetForProfile(profile_
);
51 // BookmarkAddOperation -------------------------------------------------------
53 // Handles the undo of the insertion of a bookmark or folder.
54 class BookmarkAddOperation
: public BookmarkUndoOperation
{
56 BookmarkAddOperation(Profile
* profile
, const BookmarkNode
* parent
, int index
);
57 virtual ~BookmarkAddOperation() {}
60 virtual void Undo() OVERRIDE
;
61 virtual int GetUndoLabelId() const OVERRIDE
;
62 virtual int GetRedoLabelId() const OVERRIDE
;
64 // BookmarkRenumberObserver:
65 virtual void OnBookmarkRenumbered(int64 old_id
, int64 new_id
) OVERRIDE
;
71 DISALLOW_COPY_AND_ASSIGN(BookmarkAddOperation
);
74 BookmarkAddOperation::BookmarkAddOperation(Profile
* profile
,
75 const BookmarkNode
* parent
,
77 : BookmarkUndoOperation(profile
),
78 parent_id_(parent
->id()),
82 void BookmarkAddOperation::Undo() {
83 BookmarkModel
* model
= GetBookmarkModel();
84 const BookmarkNode
* parent
= model
->GetNodeByID(parent_id_
);
87 model
->Remove(parent
, index_
);
90 int BookmarkAddOperation::GetUndoLabelId() const {
91 return IDS_BOOKMARK_BAR_UNDO_ADD
;
94 int BookmarkAddOperation::GetRedoLabelId() const {
95 return IDS_BOOKMARK_BAR_REDO_DELETE
;
98 void BookmarkAddOperation::OnBookmarkRenumbered(int64 old_id
, int64 new_id
) {
99 if (parent_id_
== old_id
)
103 // BookmarkRemoveOperation ----------------------------------------------------
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.
108 // The BookmarkModel allows only single bookmark node to be removed.
109 class BookmarkRemoveOperation
: public BookmarkUndoOperation
{
111 BookmarkRemoveOperation(Profile
* profile
,
112 const BookmarkNode
* parent
,
114 const BookmarkNode
* node
);
115 virtual ~BookmarkRemoveOperation() {}
118 virtual void Undo() OVERRIDE
;
119 virtual int GetUndoLabelId() const OVERRIDE
;
120 virtual int GetRedoLabelId() const OVERRIDE
;
122 // BookmarkRenumberObserver:
123 virtual void OnBookmarkRenumbered(int64 old_id
, int64 new_id
) OVERRIDE
;
126 void UpdateBookmarkIds(const BookmarkNodeData::Element
& element
,
127 const BookmarkNode
* parent
,
128 int index_added_at
) const;
131 const int old_index_
;
132 BookmarkNodeData removed_node_
;
134 DISALLOW_COPY_AND_ASSIGN(BookmarkRemoveOperation
);
137 BookmarkRemoveOperation::BookmarkRemoveOperation(Profile
* profile
,
138 const BookmarkNode
* parent
,
140 const BookmarkNode
* node
)
141 : BookmarkUndoOperation(profile
),
142 parent_id_(parent
->id()),
143 old_index_(old_index
),
144 removed_node_(node
) {
147 void BookmarkRemoveOperation::Undo() {
148 DCHECK(removed_node_
.is_valid());
149 BookmarkModel
* model
= GetBookmarkModel();
150 const BookmarkNode
* parent
= model
->GetNodeByID(parent_id_
);
153 bookmark_utils::CloneBookmarkNode(model
, removed_node_
.elements
, parent
,
155 UpdateBookmarkIds(removed_node_
.elements
[0], parent
, old_index_
);
158 int BookmarkRemoveOperation::GetUndoLabelId() const {
159 return IDS_BOOKMARK_BAR_UNDO_DELETE
;
162 int BookmarkRemoveOperation::GetRedoLabelId() const {
163 return IDS_BOOKMARK_BAR_REDO_ADD
;
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);
179 void BookmarkRemoveOperation::OnBookmarkRenumbered(int64 old_id
, int64 new_id
) {
180 if (parent_id_
== old_id
)
184 // BookmarkEditOperation ------------------------------------------------------
186 // Handles the undo of the modification of a bookmark node.
187 class BookmarkEditOperation
: public BookmarkUndoOperation
{
189 BookmarkEditOperation(Profile
* profile
,
190 const BookmarkNode
* node
);
191 virtual ~BookmarkEditOperation() {}
194 virtual void Undo() OVERRIDE
;
195 virtual int GetUndoLabelId() const OVERRIDE
;
196 virtual int GetRedoLabelId() const OVERRIDE
;
198 // BookmarkRenumberObserver:
199 virtual void OnBookmarkRenumbered(int64 old_id
, int64 new_id
) OVERRIDE
;
203 BookmarkNodeData original_bookmark_
;
205 DISALLOW_COPY_AND_ASSIGN(BookmarkEditOperation
);
208 BookmarkEditOperation::BookmarkEditOperation(Profile
* profile
,
209 const BookmarkNode
* node
)
210 : BookmarkUndoOperation(profile
),
211 node_id_(node
->id()),
212 original_bookmark_(node
) {
215 void BookmarkEditOperation::Undo() {
216 DCHECK(original_bookmark_
.is_valid());
217 BookmarkModel
* model
= GetBookmarkModel();
218 const BookmarkNode
* node
= model
->GetNodeByID(node_id_
);
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
);
226 int BookmarkEditOperation::GetUndoLabelId() const {
227 return IDS_BOOKMARK_BAR_UNDO_EDIT
;
230 int BookmarkEditOperation::GetRedoLabelId() const {
231 return IDS_BOOKMARK_BAR_REDO_EDIT
;
234 void BookmarkEditOperation::OnBookmarkRenumbered(int64 old_id
, int64 new_id
) {
235 if (node_id_
== old_id
)
239 // BookmarkMoveOperation ------------------------------------------------------
241 // Handles the undo of a bookmark being moved to a new location.
242 class BookmarkMoveOperation
: public BookmarkUndoOperation
{
244 BookmarkMoveOperation(Profile
* profile
,
245 const BookmarkNode
* old_parent
,
247 const BookmarkNode
* new_parent
,
249 virtual ~BookmarkMoveOperation() {}
250 virtual int GetUndoLabelId() const OVERRIDE
;
251 virtual int GetRedoLabelId() const OVERRIDE
;
254 virtual void Undo() OVERRIDE
;
256 // BookmarkRenumberObserver:
257 virtual void OnBookmarkRenumbered(int64 old_id
, int64 new_id
) OVERRIDE
;
260 int64 old_parent_id_
;
261 int64 new_parent_id_
;
265 DISALLOW_COPY_AND_ASSIGN(BookmarkMoveOperation
);
268 BookmarkMoveOperation::BookmarkMoveOperation(Profile
* profile
,
269 const BookmarkNode
* old_parent
,
271 const BookmarkNode
* new_parent
,
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
) {
280 void BookmarkMoveOperation::Undo() {
281 BookmarkModel
* model
= GetBookmarkModel();
282 const BookmarkNode
* old_parent
= model
->GetNodeByID(old_parent_id_
);
283 const BookmarkNode
* new_parent
= model
->GetNodeByID(new_parent_id_
);
287 const BookmarkNode
* node
= new_parent
->GetChild(new_index_
);
288 int destination_index
= old_index_
;
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
293 if (old_parent
== new_parent
&& new_index_
< old_index_
)
296 model
->Move(node
, old_parent
, destination_index
);
299 int BookmarkMoveOperation::GetUndoLabelId() const {
300 return IDS_BOOKMARK_BAR_UNDO_MOVE
;
303 int BookmarkMoveOperation::GetRedoLabelId() const {
304 return IDS_BOOKMARK_BAR_REDO_MOVE
;
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
;
314 // BookmarkReorderOperation ---------------------------------------------------
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
{
322 BookmarkReorderOperation(Profile
* profile
,
323 const BookmarkNode
* parent
);
324 virtual ~BookmarkReorderOperation();
327 virtual void Undo() OVERRIDE
;
328 virtual int GetUndoLabelId() const OVERRIDE
;
329 virtual int GetRedoLabelId() const OVERRIDE
;
331 // BookmarkRenumberObserver:
332 virtual void OnBookmarkRenumbered(int64 old_id
, int64 new_id
) OVERRIDE
;
336 std::vector
<int64
> ordered_bookmarks_
;
338 DISALLOW_COPY_AND_ASSIGN(BookmarkReorderOperation
);
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();
350 BookmarkReorderOperation::~BookmarkReorderOperation() {
353 void BookmarkReorderOperation::Undo() {
354 BookmarkModel
* model
= GetBookmarkModel();
355 const BookmarkNode
* parent
= model
->GetNodeByID(parent_id_
);
358 std::vector
<const BookmarkNode
*> ordered_nodes
;
359 for (size_t i
= 0; i
< ordered_bookmarks_
.size(); ++i
)
360 ordered_nodes
.push_back(model
->GetNodeByID(ordered_bookmarks_
[i
]));
362 model
->ReorderChildren(parent
, ordered_nodes
);
365 int BookmarkReorderOperation::GetUndoLabelId() const {
366 return IDS_BOOKMARK_BAR_UNDO_REORDER
;
369 int BookmarkReorderOperation::GetRedoLabelId() const {
370 return IDS_BOOKMARK_BAR_REDO_REORDER
;
373 void BookmarkReorderOperation::OnBookmarkRenumbered(int64 old_id
,
375 if (parent_id_
== old_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
;
385 // BookmarkUndoService --------------------------------------------------------
387 BookmarkUndoService::BookmarkUndoService(Profile
* profile
) : profile_(profile
) {
390 BookmarkUndoService::~BookmarkUndoService() {
391 BookmarkModelFactory::GetForProfile(profile_
)->RemoveObserver(this);
394 void BookmarkUndoService::OnBookmarkRenumbered(int64 old_id
, int64 new_id
) {
395 std::vector
<UndoOperation
*> all_operations
=
396 undo_manager()->GetAllUndoOperations();
397 for (std::vector
<UndoOperation
*>::iterator it
= all_operations
.begin();
398 it
!= all_operations
.end(); ++it
) {
399 static_cast<BookmarkUndoOperation
*>(*it
)->OnBookmarkRenumbered(old_id
,
404 void BookmarkUndoService::BookmarkModelLoaded(BookmarkModel
* model
,
405 bool ids_reassigned
) {
406 undo_manager_
.RemoveAllOperations();
409 void BookmarkUndoService::BookmarkModelBeingDeleted(BookmarkModel
* model
) {
410 undo_manager_
.RemoveAllOperations();
413 void BookmarkUndoService::BookmarkNodeMoved(BookmarkModel
* model
,
414 const BookmarkNode
* old_parent
,
416 const BookmarkNode
* new_parent
,
418 scoped_ptr
<UndoOperation
> op(new BookmarkMoveOperation(profile_
,
423 undo_manager()->AddUndoOperation(op
.Pass());
426 void BookmarkUndoService::BookmarkNodeAdded(BookmarkModel
* model
,
427 const BookmarkNode
* parent
,
429 scoped_ptr
<UndoOperation
> op(new BookmarkAddOperation(profile_
,
432 undo_manager()->AddUndoOperation(op
.Pass());
435 void BookmarkUndoService::OnWillRemoveBookmarks(BookmarkModel
* model
,
436 const BookmarkNode
* parent
,
438 const BookmarkNode
* node
) {
439 scoped_ptr
<UndoOperation
> op(new BookmarkRemoveOperation(profile_
,
443 undo_manager()->AddUndoOperation(op
.Pass());
446 void BookmarkUndoService::OnWillRemoveAllBookmarks(BookmarkModel
* model
) {
447 ScopedGroupBookmarkActions
merge_removes(profile_
);
448 for (int i
= 0; i
< model
->root_node()->child_count(); ++i
) {
449 const BookmarkNode
* permanent_node
= model
->root_node()->GetChild(i
);
450 for (int j
= permanent_node
->child_count() - 1; j
>= 0; --j
) {
451 scoped_ptr
<UndoOperation
> op(new BookmarkRemoveOperation(profile_
,
452 permanent_node
, j
, permanent_node
->GetChild(j
)));
453 undo_manager()->AddUndoOperation(op
.Pass());
458 void BookmarkUndoService::OnWillChangeBookmarkNode(BookmarkModel
* model
,
459 const BookmarkNode
* node
) {
460 scoped_ptr
<UndoOperation
> op(new BookmarkEditOperation(profile_
, node
));
461 undo_manager()->AddUndoOperation(op
.Pass());
464 void BookmarkUndoService::OnWillReorderBookmarkNode(BookmarkModel
* model
,
465 const BookmarkNode
* node
) {
466 scoped_ptr
<UndoOperation
> op(new BookmarkReorderOperation(profile_
, node
));
467 undo_manager()->AddUndoOperation(op
.Pass());