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