bookmark_editor_view.cc revision 558790d6acca3451cf3a6b497803a5f07d0bec58
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/profiles/profile.h" 19#include "chrome/browser/ui/bookmarks/bookmark_utils.h" 20#include "chrome/browser/ui/views/constrained_window_views.h" 21#include "chrome/common/net/url_fixer_upper.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/base/events/event.h" 27#include "ui/base/l10n/l10n_util.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 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