// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/bookmarks/bookmark_context_menu.h" #include "chrome/browser/bookmarks/bookmark_model.h" #include "chrome/browser/bookmarks/bookmark_utils.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/profile.h" #include "chrome/browser/tab_contents/page_navigator.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/user_metrics.h" #include "chrome/browser/views/bookmark_bar_view.h" #include "chrome/browser/views/bookmark_editor_view.h" #include "chrome/browser/views/bookmark_manager_view.h" #include "chrome/browser/views/input_window.h" #include "chrome/common/l10n_util.h" #include "chrome/common/pref_names.h" #include "chrome/common/pref_service.h" #include "chrome/views/window.h" #include "generated_resources.h" namespace { // Returns true if the specified node is of type URL, or has a descendant // of type URL. bool NodeHasURLs(BookmarkNode* node) { if (node->is_url()) return true; for (int i = 0; i < node->GetChildCount(); ++i) { if (NodeHasURLs(node->GetChild(i))) return true; } return false; } // EditFolderController ------------------------------------------------------- // EditFolderController manages the editing and/or creation of a folder. If the // user presses ok, the name change is committed to the model. // // EditFolderController deletes itself when the window is closed. class EditFolderController : public InputWindowDelegate, public BookmarkModelObserver { public: virtual ~EditFolderController() { if (model_) model_->RemoveObserver(this); } static void Show(Profile* profile, HWND hwnd, BookmarkNode* node, bool is_new, bool show_in_manager) { // EditFolderController deletes itself when done. EditFolderController* controller = new EditFolderController(profile, hwnd, node, is_new, show_in_manager); controller->Show(); } private: EditFolderController(Profile* profile, HWND hwnd, BookmarkNode* node, bool is_new, bool show_in_manager) : profile_(profile), model_(profile->GetBookmarkModel()), node_(node), is_new_(is_new), show_in_manager_(show_in_manager) { DCHECK(is_new_ || node); window_ = CreateInputWindow(hwnd, this); model_->AddObserver(this); } void Show() { window_->Show(); } // InputWindowDelegate methods. virtual std::wstring GetTextFieldLabel() { return l10n_util::GetString(IDS_BOOMARK_BAR_EDIT_FOLDER_LABEL); } virtual std::wstring GetTextFieldContents() { if (is_new_) return l10n_util::GetString(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME); return node_->GetTitle(); } virtual bool IsValid(const std::wstring& text) { return !text.empty(); } virtual void InputAccepted(const std::wstring& text) { if (is_new_) { BookmarkNode* node = model_->AddGroup(node_, node_->GetChildCount(), text); if (show_in_manager_) { BookmarkManagerView* manager = BookmarkManagerView::current(); if (manager && manager->profile() == profile_) manager->SelectInTree(node); } } else { model_->SetTitle(node_, text); } } virtual void InputCanceled() { } virtual void WindowClosing() { delete this; } virtual std::wstring GetWindowTitle() const { return is_new_ ? l10n_util::GetString(IDS_BOOMARK_FOLDER_EDITOR_WINDOW_TITLE_NEW) : l10n_util::GetString(IDS_BOOMARK_FOLDER_EDITOR_WINDOW_TITLE); } // BookmarkModelObserver methods, all invoke ModelChanged and close the // dialog. virtual void Loaded(BookmarkModel* model) {} virtual void BookmarkModelBeingDeleted(BookmarkModel* model) { model_->RemoveObserver(this); model_ = NULL; ModelChanged(); } virtual void BookmarkNodeMoved(BookmarkModel* model, BookmarkNode* old_parent, int old_index, BookmarkNode* new_parent, int new_index) { ModelChanged(); } virtual void BookmarkNodeAdded(BookmarkModel* model, BookmarkNode* parent, int index) { ModelChanged(); } virtual void BookmarkNodeRemoved(BookmarkModel* model, BookmarkNode* parent, int index, BookmarkNode* node) { ModelChanged(); } virtual void BookmarkNodeChanged(BookmarkModel* model, BookmarkNode* node) { ModelChanged(); } virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model, BookmarkNode* node) {} void ModelChanged() { window_->Close(); } Profile* profile_; BookmarkModel* model_; // If is_new is true, this is the parent to create the new node under. // Otherwise this is the node to change the title of. BookmarkNode* node_; bool is_new_; // If is_new_ is true and a new node is created, it is selected in the // bookmark manager. bool show_in_manager_; views::Window* window_; DISALLOW_COPY_AND_ASSIGN(EditFolderController); }; // SelectOnCreationHandler ---------------------------------------------------- // Used when adding a new bookmark. If a new bookmark is created it is selected // in the bookmark manager. class SelectOnCreationHandler : public BookmarkEditorView::Handler { public: explicit SelectOnCreationHandler(Profile* profile) : profile_(profile) { } virtual void NodeCreated(BookmarkNode* new_node) { BookmarkManagerView* manager = BookmarkManagerView::current(); if (!manager || manager->profile() != profile_) return; // Manager no longer showing, or showing a different profile. manager->SelectInTree(new_node); } private: Profile* profile_; DISALLOW_COPY_AND_ASSIGN(SelectOnCreationHandler); }; } // namespace // BookmarkContextMenu ------------------------------------------- BookmarkContextMenu::BookmarkContextMenu( HWND hwnd, Profile* profile, Browser* browser, PageNavigator* navigator, BookmarkNode* parent, const std::vector& selection, ConfigurationType configuration) : hwnd_(hwnd), profile_(profile), browser_(browser), navigator_(navigator), parent_(parent), selection_(selection), model_(profile->GetBookmarkModel()), configuration_(configuration) { DCHECK(profile_); DCHECK(model_->IsLoaded()); menu_.reset(new views::MenuItemView(this)); if (configuration != BOOKMARK_MANAGER_ORGANIZE_MENU) { if (selection.size() == 1 && selection[0]->is_url()) { menu_->AppendMenuItemWithLabel( IDS_BOOMARK_BAR_OPEN_ALL, l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_IN_NEW_TAB)); menu_->AppendMenuItemWithLabel( IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW, l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_IN_NEW_WINDOW)); menu_->AppendMenuItemWithLabel( IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO, l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_INCOGNITO)); } else { menu_->AppendMenuItemWithLabel( IDS_BOOMARK_BAR_OPEN_ALL, l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_ALL)); menu_->AppendMenuItemWithLabel( IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW, l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW)); menu_->AppendMenuItemWithLabel( IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO, l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO)); } menu_->AppendSeparator(); } if (selection.size() == 1 && selection[0]->is_folder()) { menu_->AppendMenuItemWithLabel(IDS_BOOKMARK_BAR_RENAME_FOLDER, l10n_util::GetString(IDS_BOOKMARK_BAR_RENAME_FOLDER)); } else { menu_->AppendMenuItemWithLabel(IDS_BOOKMARK_BAR_EDIT, l10n_util::GetString(IDS_BOOKMARK_BAR_EDIT)); } menu_->AppendMenuItemWithLabel( IDS_BOOKMARK_BAR_REMOVE, l10n_util::GetString(IDS_BOOKMARK_BAR_REMOVE)); if (configuration == BOOKMARK_MANAGER_TABLE || configuration == BOOKMARK_MANAGER_TABLE_OTHER || configuration == BOOKMARK_MANAGER_ORGANIZE_MENU || configuration == BOOKMARK_MANAGER_ORGANIZE_MENU_OTHER) { menu_->AppendMenuItemWithLabel( IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER, l10n_util::GetString(IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER)); } if (configuration == BOOKMARK_MANAGER_TABLE || configuration == BOOKMARK_MANAGER_TABLE_OTHER || configuration == BOOKMARK_MANAGER_TREE || configuration == BOOKMARK_MANAGER_ORGANIZE_MENU || configuration == BOOKMARK_MANAGER_ORGANIZE_MENU_OTHER) { menu_->AppendSeparator(); menu_->AppendMenuItemWithLabel( IDS_CUT, l10n_util::GetString(IDS_CUT)); menu_->AppendMenuItemWithLabel( IDS_COPY, l10n_util::GetString(IDS_COPY)); menu_->AppendMenuItemWithLabel( IDS_PASTE, l10n_util::GetString(IDS_PASTE)); } menu_->AppendSeparator(); menu_->AppendMenuItemWithLabel( IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK, l10n_util::GetString(IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK)); menu_->AppendMenuItemWithLabel( IDS_BOOMARK_BAR_NEW_FOLDER, l10n_util::GetString(IDS_BOOMARK_BAR_NEW_FOLDER)); if (configuration == BOOKMARK_BAR) { menu_->AppendSeparator(); menu_->AppendMenuItemWithLabel(IDS_BOOKMARK_MANAGER, l10n_util::GetString(IDS_BOOKMARK_MANAGER)); menu_->AppendMenuItem(IDS_BOOMARK_BAR_ALWAYS_SHOW, l10n_util::GetString(IDS_BOOMARK_BAR_ALWAYS_SHOW), views::MenuItemView::CHECKBOX); } model_->AddObserver(this); } BookmarkContextMenu::~BookmarkContextMenu() { if (model_) model_->RemoveObserver(this); } void BookmarkContextMenu::RunMenuAt(int x, int y) { if (!model_->IsLoaded()) { NOTREACHED(); return; } // width/height don't matter here. views::MenuItemView::AnchorPosition anchor = (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) ? views::MenuItemView::TOPRIGHT : views::MenuItemView::TOPLEFT; menu_->RunMenuAt(hwnd_, gfx::Rect(x, y, 0, 0), anchor, true); } void BookmarkContextMenu::ExecuteCommand(int id) { switch (id) { case IDS_BOOMARK_BAR_OPEN_ALL: case IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO: case IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW: { PageNavigator* navigator = browser_ ? browser_->GetSelectedTabContents() : navigator_; WindowOpenDisposition initial_disposition; if (id == IDS_BOOMARK_BAR_OPEN_ALL) { initial_disposition = NEW_FOREGROUND_TAB; UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_OpenAll", profile_); } else if (id == IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW) { initial_disposition = NEW_WINDOW; UserMetrics::RecordAction( L"BookmarkBar_ContextMenu_OpenAllInNewWindow", profile_); } else { initial_disposition = OFF_THE_RECORD; UserMetrics::RecordAction( L"BookmarkBar_ContextMenu_OpenAllIncognito", profile_); } bookmark_utils::OpenAll(hwnd_, profile_, navigator, selection_, initial_disposition); break; } case IDS_BOOKMARK_BAR_RENAME_FOLDER: case IDS_BOOKMARK_BAR_EDIT: UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_Edit", profile_); if (selection_.size() != 1) { NOTREACHED(); return; } if (selection_[0]->is_url()) { BookmarkEditorView::Configuration editor_config; if (configuration_ == BOOKMARK_BAR) editor_config = BookmarkEditorView::SHOW_TREE; else editor_config = BookmarkEditorView::NO_TREE; BookmarkEditorView::Show(hwnd_, profile_, NULL, selection_[0], editor_config, NULL); } else { EditFolderController::Show(profile_, hwnd_, selection_[0], false, false); } break; case IDS_BOOKMARK_BAR_REMOVE: { UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_Remove", profile_); BookmarkModel* model = RemoveModelObserver(); for (size_t i = 0; i < selection_.size(); ++i) { model->Remove(selection_[i]->GetParent(), selection_[i]->GetParent()->IndexOfChild(selection_[i])); } selection_.clear(); break; } case IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK: { UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_Add", profile_); BookmarkEditorView::Configuration editor_config; BookmarkEditorView::Handler* handler = NULL; if (configuration_ == BOOKMARK_BAR) { editor_config = BookmarkEditorView::SHOW_TREE; } else { editor_config = BookmarkEditorView::NO_TREE; // This is owned by the BookmarkEditorView. handler = new SelectOnCreationHandler(profile_); } BookmarkEditorView::Show(hwnd_, profile_, GetParentForNewNodes(), NULL, editor_config, handler); break; } case IDS_BOOMARK_BAR_NEW_FOLDER: { UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_NewFolder", profile_); EditFolderController::Show(profile_, hwnd_, GetParentForNewNodes(), true, (configuration_ != BOOKMARK_BAR)); break; } case IDS_BOOMARK_BAR_ALWAYS_SHOW: BookmarkBarView::ToggleWhenVisible(profile_); break; case IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER: UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_ShowInFolder", profile_); if (selection_.size() != 1) { NOTREACHED(); return; } if (BookmarkManagerView::current()) BookmarkManagerView::current()->SelectInTree(selection_[0]); break; case IDS_BOOKMARK_MANAGER: UserMetrics::RecordAction(L"ShowBookmarkManager", profile_); BookmarkManagerView::Show(profile_); break; case IDS_COPY: case IDS_CUT: bookmark_utils::CopyToClipboard(profile_->GetBookmarkModel(), selection_, id == IDS_CUT); break; case IDS_PASTE: { // Always paste to parent. if (!parent_) return; int index = (selection_.size() == 1) ? parent_->IndexOfChild(selection_[0]) : -1; if (index != -1) index++; bookmark_utils::PasteFromClipboard(profile_->GetBookmarkModel(), parent_, index); break; } default: NOTREACHED(); } } bool BookmarkContextMenu::IsItemChecked(int id) const { DCHECK(id == IDS_BOOMARK_BAR_ALWAYS_SHOW); return profile_->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar); } bool BookmarkContextMenu::IsCommandEnabled(int id) const { bool is_root_node = (selection_.size() == 1 && selection_[0]->GetParent() == model_->root_node()); switch (id) { case IDS_BOOMARK_BAR_OPEN_INCOGNITO: return !profile_->IsOffTheRecord(); case IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO: return HasURLs() && !profile_->IsOffTheRecord(); case IDS_BOOMARK_BAR_OPEN_ALL: case IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW: return HasURLs(); case IDS_BOOKMARK_BAR_RENAME_FOLDER: case IDS_BOOKMARK_BAR_EDIT: return selection_.size() == 1 && !is_root_node; case IDS_BOOKMARK_BAR_REMOVE: return !selection_.empty() && !is_root_node; case IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER: return (configuration_ == BOOKMARK_MANAGER_TABLE_OTHER || configuration_ == BOOKMARK_MANAGER_ORGANIZE_MENU_OTHER) && selection_.size() == 1; case IDS_BOOMARK_BAR_NEW_FOLDER: case IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK: return GetParentForNewNodes() != NULL; case IDS_COPY: case IDS_CUT: return selection_.size() > 0 && !is_root_node; case IDS_PASTE: // Always paste to parent. return bookmark_utils::CanPasteFromClipboard(parent_); } return true; } void BookmarkContextMenu::BookmarkModelBeingDeleted(BookmarkModel* model) { ModelChanged(); } void BookmarkContextMenu::BookmarkNodeMoved(BookmarkModel* model, BookmarkNode* old_parent, int old_index, BookmarkNode* new_parent, int new_index) { ModelChanged(); } void BookmarkContextMenu::BookmarkNodeAdded(BookmarkModel* model, BookmarkNode* parent, int index) { ModelChanged(); } void BookmarkContextMenu::BookmarkNodeRemoved(BookmarkModel* model, BookmarkNode* parent, int index, BookmarkNode* node) { ModelChanged(); } void BookmarkContextMenu::BookmarkNodeChanged(BookmarkModel* model, BookmarkNode* node) { ModelChanged(); } void BookmarkContextMenu::ModelChanged() { menu_->Cancel(); } BookmarkModel* BookmarkContextMenu::RemoveModelObserver() { BookmarkModel* model = model_; model_->RemoveObserver(this); model_ = NULL; return model; } bool BookmarkContextMenu::HasURLs() const { for (size_t i = 0; i < selection_.size(); ++i) { if (NodeHasURLs(selection_[i])) return true; } return false; } BookmarkNode* BookmarkContextMenu::GetParentForNewNodes() const { return (selection_.size() == 1 && selection_[0]->is_folder()) ? selection_[0] : parent_; }