Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / bookmarks / bookmark_editor_view.cc
1 // Copyright (c) 2012 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 <string>
8
9 #include "base/basictypes.h"
10 #include "base/logging.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
15 #include "chrome/browser/history/history_service.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
18 #include "chrome/browser/ui/views/constrained_window_views.h"
19 #include "chrome/grit/chromium_strings.h"
20 #include "chrome/grit/generated_resources.h"
21 #include "chrome/grit/locale_settings.h"
22 #include "components/bookmarks/browser/bookmark_model.h"
23 #include "components/bookmarks/browser/bookmark_utils.h"
24 #include "components/url_fixer/url_fixer.h"
25 #include "components/user_prefs/user_prefs.h"
26 #include "ui/accessibility/ax_view_state.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/events/event.h"
29 #include "ui/views/background.h"
30 #include "ui/views/controls/button/label_button.h"
31 #include "ui/views/controls/label.h"
32 #include "ui/views/controls/menu/menu_runner.h"
33 #include "ui/views/controls/textfield/textfield.h"
34 #include "ui/views/controls/tree/tree_view.h"
35 #include "ui/views/focus/focus_manager.h"
36 #include "ui/views/layout/grid_layout.h"
37 #include "ui/views/layout/layout_constants.h"
38 #include "ui/views/widget/widget.h"
39 #include "ui/views/window/dialog_client_view.h"
40 #include "url/gurl.h"
41
42 using bookmarks::BookmarkExpandedStateTracker;
43 using views::GridLayout;
44
45 namespace {
46
47 // Background color of text field when URL is invalid.
48 const SkColor kErrorColor = SkColorSetRGB(0xFF, 0xBC, 0xBC);
49
50 }  // namespace
51
52 // static
53 void BookmarkEditor::Show(gfx::NativeWindow parent_window,
54                           Profile* profile,
55                           const EditDetails& details,
56                           Configuration configuration) {
57   DCHECK(profile);
58   BookmarkEditorView* editor = new BookmarkEditorView(profile,
59       details.parent_node, details, configuration);
60   editor->Show(parent_window);
61 }
62
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       url_label_(NULL),
71       url_tf_(NULL),
72       title_label_(NULL),
73       title_tf_(NULL),
74       parent_(parent),
75       details_(details),
76       bb_model_(BookmarkModelFactory::GetForProfile(profile)),
77       running_menu_for_root_(false),
78       show_tree_(configuration == SHOW_TREE) {
79   DCHECK(profile);
80   DCHECK(bb_model_);
81   DCHECK(bb_model_->client()->CanBeEditedByUser(parent));
82   Init();
83 }
84
85 BookmarkEditorView::~BookmarkEditorView() {
86   // The tree model is deleted before the view. Reset the model otherwise the
87   // tree will reference a deleted model.
88   if (tree_view_)
89     tree_view_->SetModel(NULL);
90   bb_model_->RemoveObserver(this);
91 }
92
93 base::string16 BookmarkEditorView::GetDialogButtonLabel(
94     ui::DialogButton button) const {
95   if (button == ui::DIALOG_BUTTON_OK)
96     return l10n_util::GetStringUTF16(IDS_SAVE);
97   return views::DialogDelegateView::GetDialogButtonLabel(button);
98 }
99
100 bool BookmarkEditorView::IsDialogButtonEnabled(ui::DialogButton button) const {
101   if (button == ui::DIALOG_BUTTON_OK) {
102     if (!bb_model_->loaded())
103       return false;
104
105     if (details_.GetNodeType() != BookmarkNode::FOLDER)
106       return GetInputURL().is_valid();
107   }
108   return true;
109 }
110
111 views::View* BookmarkEditorView::CreateExtraView() {
112   return new_folder_button_.get();
113 }
114
115 ui::ModalType BookmarkEditorView::GetModalType() const {
116   return ui::MODAL_TYPE_WINDOW;
117 }
118
119 bool BookmarkEditorView::CanResize() const {
120   return true;
121 }
122
123 base::string16 BookmarkEditorView::GetWindowTitle() const {
124   return l10n_util::GetStringUTF16(details_.GetWindowTitleId());
125 }
126
127 bool BookmarkEditorView::Accept() {
128   if (!IsDialogButtonEnabled(ui::DIALOG_BUTTON_OK)) {
129     if (details_.GetNodeType() != BookmarkNode::FOLDER) {
130       // The url is invalid, focus the url field.
131       url_tf_->SelectAll(true);
132       url_tf_->RequestFocus();
133     }
134     return false;
135   }
136   // Otherwise save changes and close the dialog box.
137   ApplyEdits();
138   return true;
139 }
140
141 gfx::Size BookmarkEditorView::GetPreferredSize() const {
142   if (!show_tree_)
143     return views::View::GetPreferredSize();
144
145   return gfx::Size(views::Widget::GetLocalizedContentsSize(
146       IDS_EDITBOOKMARK_DIALOG_WIDTH_CHARS,
147       IDS_EDITBOOKMARK_DIALOG_HEIGHT_LINES));
148 }
149
150 void BookmarkEditorView::OnTreeViewSelectionChanged(
151     views::TreeView* tree_view) {
152 }
153
154 bool BookmarkEditorView::CanEdit(views::TreeView* tree_view,
155                                  ui::TreeModelNode* node) {
156   // Only allow editting of children of the bookmark bar node and other node.
157   EditorNode* bb_node = tree_model_->AsNode(node);
158   return (bb_node->parent() && bb_node->parent()->parent());
159 }
160
161 void BookmarkEditorView::ContentsChanged(views::Textfield* sender,
162                                          const base::string16& new_contents) {
163   UserInputChanged();
164 }
165
166 bool BookmarkEditorView::HandleKeyEvent(views::Textfield* sender,
167                                         const ui::KeyEvent& key_event) {
168     return false;
169 }
170
171 void BookmarkEditorView::GetAccessibleState(ui::AXViewState* state) {
172   state->name =
173       l10n_util::GetStringUTF16(IDS_BOOKMARK_EDITOR_TITLE);
174   state->role = ui::AX_ROLE_DIALOG;
175 }
176
177 void BookmarkEditorView::ButtonPressed(views::Button* sender,
178                                        const ui::Event& event) {
179   DCHECK_EQ(new_folder_button_.get(), sender);
180   NewFolder();
181 }
182
183 bool BookmarkEditorView::IsCommandIdChecked(int command_id) const {
184   return false;
185 }
186
187 bool BookmarkEditorView::IsCommandIdEnabled(int command_id) const {
188   switch (command_id) {
189     case IDS_EDIT:
190     case IDS_DELETE:
191       return !running_menu_for_root_;
192     case IDS_BOOKMARK_EDITOR_NEW_FOLDER_MENU_ITEM:
193       return true;
194     default:
195       NOTREACHED();
196       return false;
197   }
198 }
199
200 bool BookmarkEditorView::GetAcceleratorForCommandId(
201     int command_id,
202     ui::Accelerator* accelerator) {
203   return GetWidget()->GetAccelerator(command_id, accelerator);
204 }
205
206 void BookmarkEditorView::ExecuteCommand(int command_id, int event_flags) {
207   DCHECK(tree_view_->GetSelectedNode());
208   if (command_id == IDS_EDIT) {
209     tree_view_->StartEditing(tree_view_->GetSelectedNode());
210   } else if (command_id == IDS_DELETE) {
211     EditorNode* node = tree_model_->AsNode(tree_view_->GetSelectedNode());
212     if (!node)
213       return;
214     if (node->value != 0) {
215       const BookmarkNode* b_node =
216           bookmarks::GetBookmarkNodeByID(bb_model_, node->value);
217       if (!b_node->empty() &&
218           !chrome::ConfirmDeleteBookmarkNode(b_node,
219             GetWidget()->GetNativeWindow())) {
220         // The folder is not empty and the user didn't confirm.
221         return;
222       }
223       deletes_.push_back(node->value);
224     }
225     tree_model_->Remove(node->parent(), node);
226   } else {
227     DCHECK_EQ(IDS_BOOKMARK_EDITOR_NEW_FOLDER_MENU_ITEM, command_id);
228     NewFolder();
229   }
230 }
231
232 void BookmarkEditorView::Show(gfx::NativeWindow parent) {
233   CreateBrowserModalDialogViews(this, parent);
234   UserInputChanged();
235   if (show_tree_ && bb_model_->loaded())
236     ExpandAndSelect();
237   GetWidget()->Show();
238   // Select all the text in the name Textfield.
239   title_tf_->SelectAll(true);
240   // Give focus to the name Textfield.
241   title_tf_->RequestFocus();
242 }
243
244 void BookmarkEditorView::ShowContextMenuForView(
245     views::View* source,
246     const gfx::Point& point,
247     ui::MenuSourceType source_type) {
248   DCHECK_EQ(tree_view_, source);
249   if (!tree_view_->GetSelectedNode())
250     return;
251   running_menu_for_root_ =
252       (tree_model_->GetParent(tree_view_->GetSelectedNode()) ==
253        tree_model_->GetRoot());
254
255   context_menu_runner_.reset(new views::MenuRunner(
256       GetMenuModel(),
257       views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU));
258
259   if (context_menu_runner_->RunMenuAt(source->GetWidget()->GetTopLevelWidget(),
260                                       NULL,
261                                       gfx::Rect(point, gfx::Size()),
262                                       views::MENU_ANCHOR_TOPRIGHT,
263                                       source_type) ==
264       views::MenuRunner::MENU_DELETED) {
265     return;
266   }
267 }
268
269 void BookmarkEditorView::Init() {
270   bb_model_->AddObserver(this);
271
272   title_label_ = new views::Label(
273       l10n_util::GetStringUTF16(IDS_BOOKMARK_EDITOR_NAME_LABEL));
274
275   base::string16 title;
276   GURL url;
277   if (details_.type == EditDetails::EXISTING_NODE) {
278     title = details_.existing_node->GetTitle();
279     url = details_.existing_node->url();
280   } else if (details_.type == EditDetails::NEW_FOLDER) {
281     title = l10n_util::GetStringUTF16(IDS_BOOKMARK_EDITOR_NEW_FOLDER_NAME);
282   } else if (details_.type == EditDetails::NEW_URL) {
283     url = details_.url;
284     title = details_.title;
285   }
286   title_tf_ = new views::Textfield;
287   title_tf_->SetAccessibleName(
288       l10n_util::GetStringUTF16(IDS_BOOKMARK_AX_EDITOR_NAME_LABEL));
289   title_tf_->SetText(title);
290   title_tf_->set_controller(this);
291
292   if (show_tree_) {
293     tree_view_ = new views::TreeView;
294     tree_view_->SetRootShown(false);
295     tree_view_->set_context_menu_controller(this);
296
297     new_folder_button_.reset(new views::LabelButton(this,
298         l10n_util::GetStringUTF16(IDS_BOOKMARK_EDITOR_NEW_FOLDER_BUTTON)));
299     new_folder_button_->SetStyle(views::Button::STYLE_BUTTON);
300     new_folder_button_->set_owned_by_client();
301     new_folder_button_->SetEnabled(false);
302   }
303
304   GridLayout* layout = GridLayout::CreatePanel(this);
305   SetLayoutManager(layout);
306
307   const int labels_column_set_id = 0;
308   const int single_column_view_set_id = 1;
309   const int buttons_column_set_id = 2;
310
311   views::ColumnSet* column_set = layout->AddColumnSet(labels_column_set_id);
312   column_set->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0,
313                         GridLayout::USE_PREF, 0, 0);
314   column_set->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
315   column_set->AddColumn(GridLayout::FILL, GridLayout::CENTER, 1,
316                         GridLayout::USE_PREF, 0, 0);
317
318   column_set = layout->AddColumnSet(single_column_view_set_id);
319   column_set->AddColumn(GridLayout::FILL, GridLayout::FILL, 1,
320                         GridLayout::USE_PREF, 0, 0);
321
322   column_set = layout->AddColumnSet(buttons_column_set_id);
323   column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0,
324                         GridLayout::USE_PREF, 0, 0);
325   column_set->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing);
326   column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0,
327                         GridLayout::USE_PREF, 0, 0);
328   column_set->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
329   column_set->AddColumn(GridLayout::FILL, GridLayout::LEADING, 0,
330                         GridLayout::USE_PREF, 0, 0);
331   column_set->LinkColumnSizes(0, 2, 4, -1);
332
333   layout->StartRow(0, labels_column_set_id);
334   layout->AddView(title_label_);
335   layout->AddView(title_tf_);
336
337   if (details_.GetNodeType() != BookmarkNode::FOLDER) {
338     url_label_ = new views::Label(
339         l10n_util::GetStringUTF16(IDS_BOOKMARK_EDITOR_URL_LABEL));
340
341     url_tf_ = new views::Textfield;
342     PrefService* prefs =
343         profile_ ? user_prefs::UserPrefs::Get(profile_) : NULL;
344     url_tf_->SetText(chrome::FormatBookmarkURLForDisplay(url, prefs));
345     url_tf_->set_controller(this);
346     url_tf_->SetAccessibleName(
347         l10n_util::GetStringUTF16(IDS_BOOKMARK_AX_EDITOR_URL_LABEL));
348
349     layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
350
351     layout->StartRow(0, labels_column_set_id);
352     layout->AddView(url_label_);
353     layout->AddView(url_tf_);
354   }
355
356   if (show_tree_) {
357     layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
358     layout->StartRow(1, single_column_view_set_id);
359     layout->AddView(tree_view_->CreateParentIfNecessary());
360   }
361
362   layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
363
364   if (!show_tree_ || bb_model_->loaded())
365     Reset();
366 }
367
368 void BookmarkEditorView::BookmarkNodeMoved(BookmarkModel* model,
369                                            const BookmarkNode* old_parent,
370                                            int old_index,
371                                            const BookmarkNode* new_parent,
372                                            int new_index) {
373   Reset();
374 }
375
376 void BookmarkEditorView::BookmarkNodeAdded(BookmarkModel* model,
377                                            const BookmarkNode* parent,
378                                            int index) {
379   Reset();
380 }
381
382 void BookmarkEditorView::BookmarkNodeRemoved(
383     BookmarkModel* model,
384     const BookmarkNode* parent,
385     int index,
386     const BookmarkNode* node,
387     const std::set<GURL>& removed_urls) {
388   if ((details_.type == EditDetails::EXISTING_NODE &&
389        details_.existing_node->HasAncestor(node)) ||
390       (parent_ && parent_->HasAncestor(node))) {
391     // The node, or its parent was removed. Close the dialog.
392     GetWidget()->Close();
393   } else {
394     Reset();
395   }
396 }
397
398 void BookmarkEditorView::BookmarkAllUserNodesRemoved(
399     BookmarkModel* model,
400     const std::set<GURL>& removed_urls) {
401   Reset();
402 }
403
404 void BookmarkEditorView::BookmarkNodeChildrenReordered(
405     BookmarkModel* model,
406     const BookmarkNode* node) {
407   Reset();
408 }
409
410 void BookmarkEditorView::Reset() {
411   if (!show_tree_) {
412     if (parent())
413       UserInputChanged();
414     return;
415   }
416
417   new_folder_button_->SetEnabled(true);
418
419   // Do this first, otherwise when we invoke SetModel with the real one
420   // tree_view will try to invoke something on the model we just deleted.
421   tree_view_->SetModel(NULL);
422
423   EditorNode* root_node = CreateRootNode();
424   tree_model_.reset(new EditorTreeModel(root_node));
425
426   tree_view_->SetModel(tree_model_.get());
427   tree_view_->SetController(this);
428
429   context_menu_runner_.reset();
430
431   if (parent())
432     ExpandAndSelect();
433 }
434
435 GURL BookmarkEditorView::GetInputURL() const {
436   if (details_.GetNodeType() == BookmarkNode::FOLDER)
437     return GURL();
438   return url_fixer::FixupURL(base::UTF16ToUTF8(url_tf_->text()), std::string());
439 }
440
441 void BookmarkEditorView::UserInputChanged() {
442   if (details_.GetNodeType() != BookmarkNode::FOLDER) {
443     const GURL url(GetInputURL());
444     if (!url.is_valid())
445       url_tf_->SetBackgroundColor(kErrorColor);
446     else
447       url_tf_->UseDefaultBackgroundColor();
448   }
449   GetDialogClientView()->UpdateDialogButtons();
450 }
451
452 void BookmarkEditorView::NewFolder() {
453   // Create a new entry parented to the selected item, or the bookmark
454   // bar if nothing is selected.
455   EditorNode* parent = tree_model_->AsNode(tree_view_->GetSelectedNode());
456   if (!parent) {
457     NOTREACHED();
458     return;
459   }
460
461   tree_view_->StartEditing(AddNewFolder(parent));
462 }
463
464 BookmarkEditorView::EditorNode* BookmarkEditorView::AddNewFolder(
465     EditorNode* parent) {
466   EditorNode* new_node = new EditorNode(
467       l10n_util::GetStringUTF16(IDS_BOOKMARK_EDITOR_NEW_FOLDER_NAME), 0);
468   // |new_node| is now owned by |parent|.
469   tree_model_->Add(parent, new_node, parent->child_count());
470   return new_node;
471 }
472
473 void BookmarkEditorView::ExpandAndSelect() {
474   BookmarkExpandedStateTracker::Nodes expanded_nodes =
475       bb_model_->expanded_state_tracker()->GetExpandedNodes();
476   for (BookmarkExpandedStateTracker::Nodes::const_iterator i(
477        expanded_nodes.begin()); i != expanded_nodes.end(); ++i) {
478     EditorNode* editor_node =
479         FindNodeWithID(tree_model_->GetRoot(), (*i)->id());
480     if (editor_node)
481       tree_view_->Expand(editor_node);
482   }
483
484   const BookmarkNode* to_select = parent_;
485   if (details_.type == EditDetails::EXISTING_NODE)
486     to_select = details_.existing_node->parent();
487   int64 folder_id_to_select = to_select->id();
488   EditorNode* b_node =
489       FindNodeWithID(tree_model_->GetRoot(), folder_id_to_select);
490   if (!b_node)
491     b_node = tree_model_->GetRoot()->GetChild(0);  // Bookmark bar node.
492
493   tree_view_->SetSelectedNode(b_node);
494 }
495
496 BookmarkEditorView::EditorNode* BookmarkEditorView::CreateRootNode() {
497   EditorNode* root_node = new EditorNode(base::string16(), 0);
498   const BookmarkNode* bb_root_node = bb_model_->root_node();
499   CreateNodes(bb_root_node, root_node);
500   DCHECK(root_node->child_count() >= 2 && root_node->child_count() <= 4);
501   DCHECK_EQ(BookmarkNode::BOOKMARK_BAR, bb_root_node->GetChild(0)->type());
502   DCHECK_EQ(BookmarkNode::OTHER_NODE, bb_root_node->GetChild(1)->type());
503   if (root_node->child_count() >= 3)
504     DCHECK_EQ(BookmarkNode::MOBILE, bb_root_node->GetChild(2)->type());
505   return root_node;
506 }
507
508 void BookmarkEditorView::CreateNodes(const BookmarkNode* bb_node,
509                                      BookmarkEditorView::EditorNode* b_node) {
510   for (int i = 0; i < bb_node->child_count(); ++i) {
511     const BookmarkNode* child_bb_node = bb_node->GetChild(i);
512     if (child_bb_node->IsVisible() && child_bb_node->is_folder() &&
513         bb_model_->client()->CanBeEditedByUser(child_bb_node)) {
514       EditorNode* new_b_node = new EditorNode(child_bb_node->GetTitle(),
515                                               child_bb_node->id());
516       b_node->Add(new_b_node, b_node->child_count());
517       CreateNodes(child_bb_node, new_b_node);
518     }
519   }
520 }
521
522 BookmarkEditorView::EditorNode* BookmarkEditorView::FindNodeWithID(
523     BookmarkEditorView::EditorNode* node,
524     int64 id) {
525   if (node->value == id)
526     return node;
527   for (int i = 0; i < node->child_count(); ++i) {
528     EditorNode* result = FindNodeWithID(node->GetChild(i), id);
529     if (result)
530       return result;
531   }
532   return NULL;
533 }
534
535 void BookmarkEditorView::ApplyEdits() {
536   DCHECK(bb_model_->loaded());
537
538   if (tree_view_)
539     tree_view_->CommitEdit();
540
541   EditorNode* parent = show_tree_ ?
542       tree_model_->AsNode(tree_view_->GetSelectedNode()) : NULL;
543   if (show_tree_ && !parent) {
544     NOTREACHED();
545     return;
546   }
547   ApplyEdits(parent);
548 }
549
550 void BookmarkEditorView::ApplyEdits(EditorNode* parent) {
551   DCHECK(!show_tree_ || parent);
552
553   // We're going to apply edits to the bookmark bar model, which will call us
554   // back. Normally when a structural edit occurs we reset the tree model.
555   // We don't want to do that here, so we remove ourselves as an observer.
556   bb_model_->RemoveObserver(this);
557
558   GURL new_url(GetInputURL());
559   base::string16 new_title(title_tf_->text());
560
561   if (!show_tree_) {
562     BookmarkEditor::ApplyEditsWithNoFolderChange(
563         bb_model_, parent_, details_, new_title, new_url);
564     return;
565   }
566
567   // Create the new folders and update the titles.
568   const BookmarkNode* new_parent = NULL;
569   ApplyNameChangesAndCreateNewFolders(
570       bb_model_->root_node(), tree_model_->GetRoot(), parent, &new_parent);
571
572   BookmarkEditor::ApplyEditsWithPossibleFolderChange(
573       bb_model_, new_parent, details_, new_title, new_url);
574
575   BookmarkExpandedStateTracker::Nodes expanded_nodes;
576   UpdateExpandedNodes(tree_model_->GetRoot(), &expanded_nodes);
577   bb_model_->expanded_state_tracker()->SetExpandedNodes(expanded_nodes);
578
579   // Remove the folders that were removed. This has to be done after all the
580   // other changes have been committed.
581   bookmarks::DeleteBookmarkFolders(bb_model_, deletes_);
582 }
583
584 void BookmarkEditorView::ApplyNameChangesAndCreateNewFolders(
585     const BookmarkNode* bb_node,
586     BookmarkEditorView::EditorNode* b_node,
587     BookmarkEditorView::EditorNode* parent_b_node,
588     const BookmarkNode** parent_bb_node) {
589   if (parent_b_node == b_node)
590     *parent_bb_node = bb_node;
591   for (int i = 0; i < b_node->child_count(); ++i) {
592     EditorNode* child_b_node = b_node->GetChild(i);
593     const BookmarkNode* child_bb_node = NULL;
594     if (child_b_node->value == 0) {
595       // New folder.
596       child_bb_node = bb_model_->AddFolder(bb_node,
597           bb_node->child_count(), child_b_node->GetTitle());
598       child_b_node->value = child_bb_node->id();
599     } else {
600       // Existing node, reset the title (BookmarkModel ignores changes if the
601       // title is the same).
602       for (int j = 0; j < bb_node->child_count(); ++j) {
603         const BookmarkNode* node = bb_node->GetChild(j);
604         if (node->is_folder() && node->id() == child_b_node->value) {
605           child_bb_node = node;
606           break;
607         }
608       }
609       DCHECK(child_bb_node);
610       bb_model_->SetTitle(child_bb_node, child_b_node->GetTitle());
611     }
612     ApplyNameChangesAndCreateNewFolders(child_bb_node, child_b_node,
613                                         parent_b_node, parent_bb_node);
614   }
615 }
616
617 void BookmarkEditorView::UpdateExpandedNodes(
618     EditorNode* editor_node,
619     BookmarkExpandedStateTracker::Nodes* expanded_nodes) {
620   if (!tree_view_->IsExpanded(editor_node))
621     return;
622
623   // The root is 0.
624   if (editor_node->value != 0) {
625     expanded_nodes->insert(
626         bookmarks::GetBookmarkNodeByID(bb_model_, editor_node->value));
627   }
628
629   for (int i = 0; i < editor_node->child_count(); ++i)
630     UpdateExpandedNodes(editor_node->GetChild(i), expanded_nodes);
631 }
632
633 ui::SimpleMenuModel* BookmarkEditorView::GetMenuModel() {
634   if (!context_menu_model_.get()) {
635     context_menu_model_.reset(new ui::SimpleMenuModel(this));
636     context_menu_model_->AddItemWithStringId(IDS_EDIT, IDS_EDIT);
637     context_menu_model_->AddItemWithStringId(IDS_DELETE, IDS_DELETE);
638     context_menu_model_->AddItemWithStringId(
639         IDS_BOOKMARK_EDITOR_NEW_FOLDER_MENU_ITEM,
640         IDS_BOOKMARK_EDITOR_NEW_FOLDER_MENU_ITEM);
641   }
642   return context_menu_model_.get();
643 }
644
645 void BookmarkEditorView::EditorTreeModel::SetTitle(
646     ui::TreeModelNode* node,
647     const base::string16& title) {
648   if (!title.empty())
649     ui::TreeNodeModel<EditorNode>::SetTitle(node, title);
650 }