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