1 // Copyright (c) 2011 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/ui/views/bookmarks/bookmark_editor_view.h"
6
7 #include "base/basictypes.h"
8 #include "base/logging.h"
9 #include "base/string_util.h"
10 #include "base/utf_string_conversions.h"
11 #include "chrome/browser/bookmarks/bookmark_model.h"
12 #include "chrome/browser/bookmarks/bookmark_utils.h"
13 #include "chrome/browser/history/history.h"
14 #include "chrome/browser/net/url_fixer_upper.h"
15 #include "chrome/browser/prefs/pref_service.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/common/pref_names.h"
18 #include "googleurl/src/gurl.h"
19 #include "grit/chromium_strings.h"
20 #include "grit/generated_resources.h"
21 #include "grit/locale_settings.h"
22 #include "net/base/net_util.h"
23 #include "ui/base/l10n/l10n_util.h"
24 #include "views/background.h"
25 #include "views/controls/button/native_button.h"
26 #include "views/controls/label.h"
27 #include "views/controls/menu/menu_2.h"
28 #include "views/controls/textfield/textfield.h"
29 #include "views/focus/focus_manager.h"
30 #include "views/layout/grid_layout.h"
31 #include "views/layout/layout_constants.h"
32 #include "views/widget/widget.h"
33 #include "views/window/window.h"
34
35 using views::Button;
36 using views::ColumnSet;
37 using views::GridLayout;
38 using views::Label;
39 using views::NativeButton;
40 using views::Textfield;
41
42 // Background color of text field when URL is invalid.
43 static const SkColor kErrorColor = SkColorSetRGB(0xFF, 0xBC, 0xBC);
44
45 // Preferred width of the tree.
46 static const int kTreeWidth = 300;
47
48 // ID for various children.
49 static const int kNewFolderButtonID = 1002;
50
51 // static
Show(HWND parent_hwnd,Profile * profile,const BookmarkNode * parent,const EditDetails & details,Configuration configuration)52 void BookmarkEditor::Show(HWND parent_hwnd,
53 Profile* profile,
54 const BookmarkNode* parent,
55 const EditDetails& details,
56 Configuration configuration) {
57 DCHECK(profile);
58 BookmarkEditorView* editor =
59 new BookmarkEditorView(profile, parent, details, configuration);
60 editor->Show(parent_hwnd);
61 }
62
BookmarkEditorView(Profile * profile,const BookmarkNode * parent,const EditDetails & details,BookmarkEditor::Configuration configuration)63 BookmarkEditorView::BookmarkEditorView(
64 Profile* profile,
65 const BookmarkNode* parent,
66 const EditDetails& details,
67 BookmarkEditor::Configuration configuration)
68 : profile_(profile),
69 tree_view_(NULL),
70 new_folder_button_(NULL),
71 url_label_(NULL),
72 title_label_(NULL),
73 parent_(parent),
74 details_(details),
75 running_menu_for_root_(false),
76 show_tree_(configuration == SHOW_TREE) {
77 DCHECK(profile);
78 Init();
79 }
80
~BookmarkEditorView()81 BookmarkEditorView::~BookmarkEditorView() {
82 // The tree model is deleted before the view. Reset the model otherwise the
83 // tree will reference a deleted model.
84 if (tree_view_)
85 tree_view_->SetModel(NULL);
86 bb_model_->RemoveObserver(this);
87 }
88
IsDialogButtonEnabled(MessageBoxFlags::DialogButton button) const89 bool BookmarkEditorView::IsDialogButtonEnabled(
90 MessageBoxFlags::DialogButton button) const {
91 if (button == MessageBoxFlags::DIALOGBUTTON_OK) {
92 if (details_.type == EditDetails::NEW_FOLDER)
93 return !title_tf_.text().empty();
94
95 const GURL url(GetInputURL());
96 return bb_model_->IsLoaded() && url.is_valid();
97 }
98 return true;
99 }
100
IsModal() const101 bool BookmarkEditorView::IsModal() const {
102 return true;
103 }
104
CanResize() const105 bool BookmarkEditorView::CanResize() const {
106 return true;
107 }
108
GetWindowTitle() const109 std::wstring BookmarkEditorView::GetWindowTitle() const {
110 return UTF16ToWide(l10n_util::GetStringUTF16(IDS_BOOMARK_EDITOR_TITLE));
111 }
112
Accept()113 bool BookmarkEditorView::Accept() {
114 if (!IsDialogButtonEnabled(MessageBoxFlags::DIALOGBUTTON_OK)) {
115 // The url is invalid, focus the url field.
116 url_tf_.SelectAll();
117 url_tf_.RequestFocus();
118 return false;
119 }
120 // Otherwise save changes and close the dialog box.
121 ApplyEdits();
122 return true;
123 }
124
AreAcceleratorsEnabled(MessageBoxFlags::DialogButton button)125 bool BookmarkEditorView::AreAcceleratorsEnabled(
126 MessageBoxFlags::DialogButton button) {
127 return !show_tree_ || !tree_view_->GetEditingNode();
128 }
129
GetContentsView()130 views::View* BookmarkEditorView::GetContentsView() {
131 return this;
132 }
133
Layout()134 void BookmarkEditorView::Layout() {
135 // Let the grid layout manager lay out most of the dialog...
136 GetLayoutManager()->Layout(this);
137
138 if (!show_tree_)
139 return;
140
141 // Manually lay out the New Folder button in the same row as the OK/Cancel
142 // buttons...
143 gfx::Rect parent_bounds = parent()->GetContentsBounds();
144 gfx::Size prefsize = new_folder_button_->GetPreferredSize();
145 int button_y =
146 parent_bounds.bottom() - prefsize.height() - views::kButtonVEdgeMargin;
147 new_folder_button_->SetBounds(
148 views::kPanelHorizMargin, button_y, prefsize.width(), prefsize.height());
149 }
150
GetPreferredSize()151 gfx::Size BookmarkEditorView::GetPreferredSize() {
152 if (!show_tree_)
153 return views::View::GetPreferredSize();
154
155 return gfx::Size(views::Window::GetLocalizedContentsSize(
156 IDS_EDITBOOKMARK_DIALOG_WIDTH_CHARS,
157 IDS_EDITBOOKMARK_DIALOG_HEIGHT_LINES));
158 }
159
ViewHierarchyChanged(bool is_add,views::View * parent,views::View * child)160 void BookmarkEditorView::ViewHierarchyChanged(bool is_add,
161 views::View* parent,
162 views::View* child) {
163 if (show_tree_ && child == this) {
164 // Add and remove the New Folder button from the ClientView's hierarchy.
165 if (is_add) {
166 parent->AddChildView(new_folder_button_.get());
167 } else {
168 parent->RemoveChildView(new_folder_button_.get());
169 }
170 }
171 }
172
OnTreeViewSelectionChanged(views::TreeView * tree_view)173 void BookmarkEditorView::OnTreeViewSelectionChanged(
174 views::TreeView* tree_view) {
175 }
176
CanEdit(views::TreeView * tree_view,ui::TreeModelNode * node)177 bool BookmarkEditorView::CanEdit(views::TreeView* tree_view,
178 ui::TreeModelNode* node) {
179 // Only allow editting of children of the bookmark bar node and other node.
180 EditorNode* bb_node = tree_model_->AsNode(node);
181 return (bb_node->parent() && bb_node->parent()->parent());
182 }
183
ContentsChanged(Textfield * sender,const std::wstring & new_contents)184 void BookmarkEditorView::ContentsChanged(Textfield* sender,
185 const std::wstring& new_contents) {
186 UserInputChanged();
187 }
188
ButtonPressed(Button * sender,const views::Event & event)189 void BookmarkEditorView::ButtonPressed(
190 Button* sender, const views::Event& event) {
191 DCHECK(sender);
192 switch (sender->GetID()) {
193 case kNewFolderButtonID:
194 NewFolder();
195 break;
196
197 default:
198 NOTREACHED();
199 }
200 }
201
IsCommandIdChecked(int command_id) const202 bool BookmarkEditorView::IsCommandIdChecked(int command_id) const {
203 return false;
204 }
205
IsCommandIdEnabled(int command_id) const206 bool BookmarkEditorView::IsCommandIdEnabled(int command_id) const {
207 return (command_id != IDS_EDIT || !running_menu_for_root_);
208 }
209
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator)210 bool BookmarkEditorView::GetAcceleratorForCommandId(
211 int command_id,
212 ui::Accelerator* accelerator) {
213 return GetWidget()->GetAccelerator(command_id, accelerator);
214 }
215
ExecuteCommand(int command_id)216 void BookmarkEditorView::ExecuteCommand(int command_id) {
217 DCHECK(tree_view_->GetSelectedNode());
218 if (command_id == IDS_EDIT) {
219 tree_view_->StartEditing(tree_view_->GetSelectedNode());
220 } else {
221 DCHECK(command_id == IDS_BOOMARK_EDITOR_NEW_FOLDER_MENU_ITEM);
222 NewFolder();
223 }
224 }
225
Show(HWND parent_hwnd)226 void BookmarkEditorView::Show(HWND parent_hwnd) {
227 views::Window::CreateChromeWindow(parent_hwnd, gfx::Rect(), this);
228 UserInputChanged();
229 if (show_tree_ && bb_model_->IsLoaded())
230 ExpandAndSelect();
231 window()->Show();
232 // Select all the text in the name Textfield.
233 title_tf_.SelectAll();
234 // Give focus to the name Textfield.
235 title_tf_.RequestFocus();
236 }
237
Close()238 void BookmarkEditorView::Close() {
239 DCHECK(window());
240 window()->CloseWindow();
241 }
242
ShowContextMenuForView(View * source,const gfx::Point & p,bool is_mouse_gesture)243 void BookmarkEditorView::ShowContextMenuForView(View* source,
244 const gfx::Point& p,
245 bool is_mouse_gesture) {
246 DCHECK(source == tree_view_);
247 if (!tree_view_->GetSelectedNode())
248 return;
249 running_menu_for_root_ =
250 (tree_model_->GetParent(tree_view_->GetSelectedNode()) ==
251 tree_model_->GetRoot());
252 if (!context_menu_contents_.get()) {
253 context_menu_contents_.reset(new ui::SimpleMenuModel(this));
254 context_menu_contents_->AddItemWithStringId(IDS_EDIT, IDS_EDIT);
255 context_menu_contents_->AddItemWithStringId(
256 IDS_BOOMARK_EDITOR_NEW_FOLDER_MENU_ITEM,
257 IDS_BOOMARK_EDITOR_NEW_FOLDER_MENU_ITEM);
258 context_menu_.reset(new views::Menu2(context_menu_contents_.get()));
259 }
260 context_menu_->RunContextMenuAt(p);
261 }
262
Init()263 void BookmarkEditorView::Init() {
264 bb_model_ = profile_->GetBookmarkModel();
265 DCHECK(bb_model_);
266 bb_model_->AddObserver(this);
267
268 url_tf_.set_parent_owned(false);
269 title_tf_.set_parent_owned(false);
270
271 std::wstring title;
272 if (details_.type == EditDetails::EXISTING_NODE)
273 title = details_.existing_node->GetTitle();
274 else if (details_.type == EditDetails::NEW_FOLDER)
275 title = UTF16ToWide(
276 l10n_util::GetStringUTF16(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME));
277 title_tf_.SetText(title);
278 title_tf_.SetController(this);
279
280 title_label_ = new views::Label(
281 UTF16ToWide(l10n_util::GetStringUTF16(IDS_BOOMARK_EDITOR_NAME_LABEL)));
282 title_tf_.SetAccessibleName(WideToUTF16Hack(title_label_->GetText()));
283
284 string16 url_text;
285 if (details_.type == EditDetails::EXISTING_NODE) {
286 std::string languages = profile_
287 ? profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)
288 : std::string();
289 // Because this gets parsed by FixupURL(), it's safe to omit the scheme or
290 // trailing slash, and unescape most characters, but we need to not drop any
291 // username/password, or unescape anything that changes the meaning.
292 url_text = net::FormatUrl(details_.existing_node->GetURL(), languages,
293 net::kFormatUrlOmitAll & ~net::kFormatUrlOmitUsernamePassword,
294 UnescapeRule::SPACES, NULL, NULL, NULL);
295 }
296 url_tf_.SetText(UTF16ToWide(url_text));
297 url_tf_.SetController(this);
298
299 url_label_ = new views::Label(
300 UTF16ToWide(l10n_util::GetStringUTF16(IDS_BOOMARK_EDITOR_URL_LABEL)));
301 url_tf_.SetAccessibleName(WideToUTF16Hack(url_label_->GetText()));
302
303 if (show_tree_) {
304 tree_view_ = new views::TreeView();
305 new_folder_button_.reset(new views::NativeButton(
306 this,
307 UTF16ToWide(l10n_util::GetStringUTF16(
308 IDS_BOOMARK_EDITOR_NEW_FOLDER_BUTTON))));
309 new_folder_button_->set_parent_owned(false);
310 tree_view_->SetContextMenuController(this);
311
312 tree_view_->SetRootShown(false);
313 new_folder_button_->SetEnabled(false);
314 new_folder_button_->SetID(kNewFolderButtonID);
315 }
316
317 // Yummy layout code.
318 GridLayout* layout = GridLayout::CreatePanel(this);
319 SetLayoutManager(layout);
320
321 const int labels_column_set_id = 0;
322 const int single_column_view_set_id = 1;
323 const int buttons_column_set_id = 2;
324
325 ColumnSet* column_set = layout->AddColumnSet(labels_column_set_id);
326 column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
327 GridLayout::USE_PREF, 0, 0);
328 column_set->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
329 column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
330 GridLayout::USE_PREF, 0, 0);
331
332 column_set = layout->AddColumnSet(single_column_view_set_id);
333 column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
334 GridLayout::FIXED, kTreeWidth, 0);
335
336 column_set = layout->AddColumnSet(buttons_column_set_id);
337 column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0,
338 GridLayout::USE_PREF, 0, 0);
339 column_set->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing);
340 column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0,
341 GridLayout::USE_PREF, 0, 0);
342 column_set->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
343 column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0,
344 GridLayout::USE_PREF, 0, 0);
345 column_set->LinkColumnSizes(0, 2, 4, -1);
346
347 layout->StartRow(0, labels_column_set_id);
348
349 layout->AddView(title_label_);
350 layout->AddView(&title_tf_);
351
352 if (details_.type != EditDetails::NEW_FOLDER) {
353 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
354
355 layout->StartRow(0, labels_column_set_id);
356 layout->AddView(url_label_);
357 layout->AddView(&url_tf_);
358 }
359
360 if (show_tree_) {
361 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
362 layout->StartRow(1, single_column_view_set_id);
363 layout->AddView(tree_view_);
364 }
365
366 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
367
368 if (!show_tree_ || bb_model_->IsLoaded())
369 Reset();
370 }
371
BookmarkNodeMoved(BookmarkModel * model,const BookmarkNode * old_parent,int old_index,const BookmarkNode * new_parent,int new_index)372 void BookmarkEditorView::BookmarkNodeMoved(BookmarkModel* model,
373 const BookmarkNode* old_parent,
374 int old_index,
375 const BookmarkNode* new_parent,
376 int new_index) {
377 Reset();
378 }
379
BookmarkNodeAdded(BookmarkModel * model,const BookmarkNode * parent,int index)380 void BookmarkEditorView::BookmarkNodeAdded(BookmarkModel* model,
381 const BookmarkNode* parent,
382 int index) {
383 Reset();
384 }
385
BookmarkNodeRemoved(BookmarkModel * model,const BookmarkNode * parent,int index,const BookmarkNode * node)386 void BookmarkEditorView::BookmarkNodeRemoved(BookmarkModel* model,
387 const BookmarkNode* parent,
388 int index,
389 const BookmarkNode* node) {
390 if ((details_.type == EditDetails::EXISTING_NODE &&
391 details_.existing_node->HasAncestor(node)) ||
392 (parent_ && parent_->HasAncestor(node))) {
393 // The node, or its parent was removed. Close the dialog.
394 window()->CloseWindow();
395 } else {
396 Reset();
397 }
398 }
399
BookmarkNodeChildrenReordered(BookmarkModel * model,const BookmarkNode * node)400 void BookmarkEditorView::BookmarkNodeChildrenReordered(
401 BookmarkModel* model, const BookmarkNode* node) {
402 Reset();
403 }
404
Reset()405 void BookmarkEditorView::Reset() {
406 if (!show_tree_) {
407 if (parent())
408 UserInputChanged();
409 return;
410 }
411
412 new_folder_button_->SetEnabled(true);
413
414 // Do this first, otherwise when we invoke SetModel with the real one
415 // tree_view will try to invoke something on the model we just deleted.
416 tree_view_->SetModel(NULL);
417
418 EditorNode* root_node = CreateRootNode();
419 tree_model_.reset(new EditorTreeModel(root_node));
420
421 tree_view_->SetModel(tree_model_.get());
422 tree_view_->SetController(this);
423
424 context_menu_.reset();
425
426 if (parent())
427 ExpandAndSelect();
428 }
GetInputURL() const429 GURL BookmarkEditorView::GetInputURL() const {
430 return URLFixerUpper::FixupURL(UTF16ToUTF8(url_tf_.text()), std::string());
431 }
432
GetInputTitle() const433 std::wstring BookmarkEditorView::GetInputTitle() const {
434 return title_tf_.text();
435 }
436
UserInputChanged()437 void BookmarkEditorView::UserInputChanged() {
438 const GURL url(GetInputURL());
439 if (!url.is_valid())
440 url_tf_.SetBackgroundColor(kErrorColor);
441 else
442 url_tf_.UseDefaultBackgroundColor();
443 GetDialogClientView()->UpdateDialogButtons();
444 }
445
NewFolder()446 void BookmarkEditorView::NewFolder() {
447 // Create a new entry parented to the selected item, or the bookmark
448 // bar if nothing is selected.
449 EditorNode* parent = tree_model_->AsNode(tree_view_->GetSelectedNode());
450 if (!parent) {
451 NOTREACHED();
452 return;
453 }
454
455 tree_view_->StartEditing(AddNewFolder(parent));
456 }
457
AddNewFolder(EditorNode * parent)458 BookmarkEditorView::EditorNode* BookmarkEditorView::AddNewFolder(
459 EditorNode* parent) {
460 EditorNode* new_node = new EditorNode();
461 new_node->set_title(
462 l10n_util::GetStringUTF16(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME));
463 new_node->value = 0;
464 // new_node is now owned by parent.
465 tree_model_->Add(parent, new_node, parent->child_count());
466 return new_node;
467 }
468
ExpandAndSelect()469 void BookmarkEditorView::ExpandAndSelect() {
470 tree_view_->ExpandAll();
471
472 const BookmarkNode* to_select = parent_;
473 if (details_.type == EditDetails::EXISTING_NODE)
474 to_select = details_.existing_node->parent();
475 int64 folder_id_to_select = to_select->id();
476 EditorNode* b_node =
477 FindNodeWithID(tree_model_->GetRoot(), folder_id_to_select);
478 if (!b_node)
479 b_node = tree_model_->GetRoot()->GetChild(0); // Bookmark bar node.
480
481 tree_view_->SetSelectedNode(b_node);
482 }
483
CreateRootNode()484 BookmarkEditorView::EditorNode* BookmarkEditorView::CreateRootNode() {
485 EditorNode* root_node = new EditorNode(std::wstring(), 0);
486 const BookmarkNode* bb_root_node = bb_model_->root_node();
487 CreateNodes(bb_root_node, root_node);
488 DCHECK(root_node->child_count() == 2);
489 DCHECK(bb_root_node->GetChild(0)->type() == BookmarkNode::BOOKMARK_BAR);
490 DCHECK(bb_root_node->GetChild(1)->type() == BookmarkNode::OTHER_NODE);
491 return root_node;
492 }
493
CreateNodes(const BookmarkNode * bb_node,BookmarkEditorView::EditorNode * b_node)494 void BookmarkEditorView::CreateNodes(const BookmarkNode* bb_node,
495 BookmarkEditorView::EditorNode* b_node) {
496 for (int i = 0; i < bb_node->child_count(); ++i) {
497 const BookmarkNode* child_bb_node = bb_node->GetChild(i);
498 if (child_bb_node->is_folder()) {
499 EditorNode* new_b_node =
500 new EditorNode(WideToUTF16(child_bb_node->GetTitle()),
501 child_bb_node->id());
502 b_node->Add(new_b_node, b_node->child_count());
503 CreateNodes(child_bb_node, new_b_node);
504 }
505 }
506 }
507
FindNodeWithID(BookmarkEditorView::EditorNode * node,int64 id)508 BookmarkEditorView::EditorNode* BookmarkEditorView::FindNodeWithID(
509 BookmarkEditorView::EditorNode* node,
510 int64 id) {
511 if (node->value == id)
512 return node;
513 for (int i = 0; i < node->child_count(); ++i) {
514 EditorNode* result = FindNodeWithID(node->GetChild(i), id);
515 if (result)
516 return result;
517 }
518 return NULL;
519 }
520
ApplyEdits()521 void BookmarkEditorView::ApplyEdits() {
522 DCHECK(bb_model_->IsLoaded());
523
524 EditorNode* parent = show_tree_ ?
525 tree_model_->AsNode(tree_view_->GetSelectedNode()) : NULL;
526 if (show_tree_ && !parent) {
527 NOTREACHED();
528 return;
529 }
530 ApplyEdits(parent);
531 }
532
ApplyEdits(EditorNode * parent)533 void BookmarkEditorView::ApplyEdits(EditorNode* parent) {
534 DCHECK(!show_tree_ || parent);
535
536 // We're going to apply edits to the bookmark bar model, which will call us
537 // back. Normally when a structural edit occurs we reset the tree model.
538 // We don't want to do that here, so we remove ourselves as an observer.
539 bb_model_->RemoveObserver(this);
540
541 GURL new_url(GetInputURL());
542 string16 new_title(WideToUTF16Hack(GetInputTitle()));
543
544 if (!show_tree_) {
545 bookmark_utils::ApplyEditsWithNoFolderChange(
546 bb_model_, parent_, details_, new_title, new_url);
547 return;
548 }
549
550 // Create the new folders and update the titles.
551 const BookmarkNode* new_parent = NULL;
552 ApplyNameChangesAndCreateNewFolders(
553 bb_model_->root_node(), tree_model_->GetRoot(), parent, &new_parent);
554
555 bookmark_utils::ApplyEditsWithPossibleFolderChange(
556 bb_model_, new_parent, details_, new_title, new_url);
557 }
558
ApplyNameChangesAndCreateNewFolders(const BookmarkNode * bb_node,BookmarkEditorView::EditorNode * b_node,BookmarkEditorView::EditorNode * parent_b_node,const BookmarkNode ** parent_bb_node)559 void BookmarkEditorView::ApplyNameChangesAndCreateNewFolders(
560 const BookmarkNode* bb_node,
561 BookmarkEditorView::EditorNode* b_node,
562 BookmarkEditorView::EditorNode* parent_b_node,
563 const BookmarkNode** parent_bb_node) {
564 if (parent_b_node == b_node)
565 *parent_bb_node = bb_node;
566 for (int i = 0; i < b_node->child_count(); ++i) {
567 EditorNode* child_b_node = b_node->GetChild(i);
568 const BookmarkNode* child_bb_node = NULL;
569 if (child_b_node->value == 0) {
570 // New folder.
571 child_bb_node = bb_model_->AddFolder(bb_node,
572 bb_node->child_count(), child_b_node->GetTitle());
573 } else {
574 // Existing node, reset the title (BookmarkModel ignores changes if the
575 // title is the same).
576 for (int j = 0; j < bb_node->child_count(); ++j) {
577 const BookmarkNode* node = bb_node->GetChild(j);
578 if (node->is_folder() && node->id() == child_b_node->value) {
579 child_bb_node = node;
580 break;
581 }
582 }
583 DCHECK(child_bb_node);
584 bb_model_->SetTitle(child_bb_node, child_b_node->GetTitle());
585 }
586 ApplyNameChangesAndCreateNewFolders(child_bb_node, child_b_node,
587 parent_b_node, parent_bb_node);
588 }
589 }
590