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