bookmark_menu_delegate.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project// Copyright (c) 2012 The Chromium Authors. All rights reserved. 272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project// Use of this source code is governed by a BSD-style license that can be 372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project// found in the LICENSE file. 472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project 572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h" 672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project 772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "base/prefs/pref_service.h" 872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "base/strings/utf_string_conversions.h" 972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "chrome/browser/bookmarks/bookmark_model_factory.h" 1072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "chrome/browser/bookmarks/chrome_bookmark_client.h" 1172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "chrome/browser/bookmarks/chrome_bookmark_client_factory.h" 1272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "chrome/browser/profiles/profile.h" 1372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "chrome/browser/ui/bookmarks/bookmark_drag_drop.h" 1472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "chrome/browser/ui/bookmarks/bookmark_utils.h" 1572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "chrome/browser/ui/browser.h" 1672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h" 1772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.h" 1872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "chrome/browser/ui/views/event_utils.h" 1972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "chrome/common/pref_names.h" 2072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "components/bookmarks/browser/bookmark_model.h" 2172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "content/public/browser/page_navigator.h" 22798098f7dcc1b7f1fbed1727f1e9ee9a9902f24aAlex Yakavenka#include "content/public/browser/user_metrics.h" 23d6f77b1faf5a6a230026815e8b63d50f71c2361fFrederic Predon#include "grit/generated_resources.h" 2472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "grit/theme_resources.h" 2572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "grit/ui_resources.h" 2672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "ui/base/dragdrop/os_exchange_data.h" 2772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "ui/base/l10n/l10n_util.h" 2872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "ui/base/resource/resource_bundle.h" 2972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "ui/base/window_open_disposition.h" 3072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "ui/views/controls/button/menu_button.h" 3172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "ui/views/controls/menu/menu_item_view.h" 3272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "ui/views/controls/menu/submenu_view.h" 3372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project#include "ui/views/widget/widget.h" 3472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project 3572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectusing base::UserMetricsAction; 3672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectusing content::PageNavigator; 3772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectusing views::MenuItemView; 3872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project 3972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project// Max width of a menu. There does not appear to be an OS value for this, yet 4072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project// both IE and FF restrict the max width of a menu. 4172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectstatic const int kMaxMenuWidth = 400; 4272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project 4372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source ProjectBookmarkMenuDelegate::BookmarkMenuDelegate(Browser* browser, 4472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project PageNavigator* navigator, 4572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project views::Widget* parent, 4672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project int first_menu_id, 4772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project int max_menu_id) 4872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project : browser_(browser), 4972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project profile_(browser->profile()), 5072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project page_navigator_(navigator), 5172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project parent_(parent), 5272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project menu_(NULL), 5372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project parent_menu_item_(NULL), 5472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project next_menu_id_(first_menu_id), 5572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project min_menu_id_(first_menu_id), 5672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project max_menu_id_(max_menu_id), 5772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project real_delegate_(NULL), 5872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project is_mutating_model_(false), 5972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project location_(BOOKMARK_LAUNCH_LOCATION_NONE) {} 6072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project 6172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source ProjectBookmarkMenuDelegate::~BookmarkMenuDelegate() { 6272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project GetBookmarkModel()->RemoveObserver(this); 6372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project} 6472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project 6572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectvoid BookmarkMenuDelegate::Init(views::MenuDelegate* real_delegate, 6672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project MenuItemView* parent, 6772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project const BookmarkNode* node, 6872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project int start_child_index, 6972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project ShowOptions show_options, 7072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project BookmarkLaunchLocation location) { 7172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project GetBookmarkModel()->AddObserver(this); 7272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project real_delegate_ = real_delegate; 7372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project location_ = location; 7472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project if (parent) { 7572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project parent_menu_item_ = parent; 7672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project 7772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project // Add a separator if there are existing items in the menu, and if the 7872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project // current node has children. If |node| is the bookmark bar then the 7972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project // managed node is shown as its first child, if it's not empty. 8072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project BookmarkModel* model = GetBookmarkModel(); 8172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project ChromeBookmarkClient* client = GetChromeBookmarkClient(); 8272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project bool show_managed = show_options == SHOW_PERMANENT_FOLDERS && 8372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project node == model->bookmark_bar_node() && 8472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project !client->managed_node()->empty(); 8572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project bool has_children = 8672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project (start_child_index < node->child_count()) || show_managed; 8772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project int initial_count = parent->GetSubmenu() ? 8872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project parent->GetSubmenu()->GetMenuItemCount() : 0; 8972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project if (has_children && initial_count > 0) 9072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project parent->AppendSeparator(); 9172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project if (show_managed) 9272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project BuildMenuForManagedNode(parent, &next_menu_id_); 9372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project BuildMenu(node, start_child_index, parent, &next_menu_id_); 9472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project if (show_options == SHOW_PERMANENT_FOLDERS) 9572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project BuildMenusForPermanentNodes(parent, &next_menu_id_); 9672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project } else { 9772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project menu_ = CreateMenu(node, start_child_index, show_options); 9872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project } 9972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project} 10072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project 10172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectvoid BookmarkMenuDelegate::SetPageNavigator(PageNavigator* navigator) { 10272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project page_navigator_ = navigator; 10372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project if (context_menu_.get()) 10472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project context_menu_->SetPageNavigator(navigator); 10572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project} 10672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project 10772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source ProjectBookmarkModel* BookmarkMenuDelegate::GetBookmarkModel() { 10872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project return BookmarkModelFactory::GetForProfile(profile_); 10972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project} 11072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project 11172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source ProjectChromeBookmarkClient* BookmarkMenuDelegate::GetChromeBookmarkClient() { 11272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project return ChromeBookmarkClientFactory::GetForProfile(profile_); 11372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project} 11472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project 11572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectvoid BookmarkMenuDelegate::SetActiveMenu(const BookmarkNode* node, 11672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project int start_index) { 11772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project DCHECK(!parent_menu_item_); 11872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project if (!node_to_menu_map_[node]) 11972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project CreateMenu(node, start_index, HIDE_PERMANENT_FOLDERS); 12072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project menu_ = node_to_menu_map_[node]; 12172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project} 12272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project 12372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectbase::string16 BookmarkMenuDelegate::GetTooltipText( 12472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project int id, 12572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project const gfx::Point& screen_loc) const { 12672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project MenuIDToNodeMap::const_iterator i = menu_id_to_node_map_.find(id); 12772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project // When removing bookmarks it may be possible to end up here without a node. 12872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project if (i == menu_id_to_node_map_.end()) { 12972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project DCHECK(is_mutating_model_); 13072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project return base::string16(); 13172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project } 13272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project 13372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project const BookmarkNode* node = i->second; 13472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project if (node->is_url()) { 13572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project return BookmarkBarView::CreateToolTipForURLAndTitle( 13672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project parent_, screen_loc, node->url(), node->GetTitle(), profile_); 13772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project } 13872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project return base::string16(); 13972735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project} 14072735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project 14172735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Projectbool BookmarkMenuDelegate::IsTriggerableEvent(views::MenuItemView* menu, 14272735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project const ui::Event& e) { 14372735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project return e.type() == ui::ET_GESTURE_TAP || 14472735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project e.type() == ui::ET_GESTURE_TAP_DOWN || 14572735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project event_utils::IsPossibleDispositionEvent(e); 14672735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project} 14772735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project 14836cb4bcc16e6d395e03d365a066bcad78e9d25c4Tom Taylorvoid BookmarkMenuDelegate::ExecuteCommand(int id, int mouse_event_flags) { 14936cb4bcc16e6d395e03d365a066bcad78e9d25c4Tom Taylor DCHECK(menu_id_to_node_map_.find(id) != menu_id_to_node_map_.end()); 15036cb4bcc16e6d395e03d365a066bcad78e9d25c4Tom Taylor 15136cb4bcc16e6d395e03d365a066bcad78e9d25c4Tom Taylor const BookmarkNode* node = menu_id_to_node_map_[id]; 15236cb4bcc16e6d395e03d365a066bcad78e9d25c4Tom Taylor std::vector<const BookmarkNode*> selection; 153d6f77b1faf5a6a230026815e8b63d50f71c2361fFrederic Predon selection.push_back(node); 15436cb4bcc16e6d395e03d365a066bcad78e9d25c4Tom Taylor 15536cb4bcc16e6d395e03d365a066bcad78e9d25c4Tom Taylor chrome::OpenAll(parent_->GetNativeWindow(), page_navigator_, selection, 15636cb4bcc16e6d395e03d365a066bcad78e9d25c4Tom Taylor ui::DispositionFromEventFlags(mouse_event_flags), 15736cb4bcc16e6d395e03d365a066bcad78e9d25c4Tom Taylor profile_); 15872735c62aba8fd2a9420a0f9f83d22543e3c164fThe Android Open Source Project RecordBookmarkLaunch(node, location_); 159} 160 161bool BookmarkMenuDelegate::ShouldExecuteCommandWithoutClosingMenu( 162 int id, 163 const ui::Event& event) { 164 return (event.flags() & ui::EF_LEFT_MOUSE_BUTTON) && 165 ui::DispositionFromEventFlags(event.flags()) == NEW_BACKGROUND_TAB; 166} 167 168bool BookmarkMenuDelegate::GetDropFormats( 169 MenuItemView* menu, 170 int* formats, 171 std::set<ui::OSExchangeData::CustomFormat>* custom_formats) { 172 *formats = ui::OSExchangeData::URL; 173 custom_formats->insert(BookmarkNodeData::GetBookmarkCustomFormat()); 174 return true; 175} 176 177bool BookmarkMenuDelegate::AreDropTypesRequired(MenuItemView* menu) { 178 return true; 179} 180 181bool BookmarkMenuDelegate::CanDrop(MenuItemView* menu, 182 const ui::OSExchangeData& data) { 183 // Only accept drops of 1 node, which is the case for all data dragged from 184 // bookmark bar and menus. 185 186 if (!drop_data_.Read(data) || drop_data_.elements.size() != 1 || 187 !profile_->GetPrefs()->GetBoolean(prefs::kEditBookmarksEnabled)) 188 return false; 189 190 if (drop_data_.has_single_url()) 191 return true; 192 193 const BookmarkNode* drag_node = 194 drop_data_.GetFirstNode(GetBookmarkModel(), profile_->GetPath()); 195 if (!drag_node) { 196 // Dragging a folder from another profile, always accept. 197 return true; 198 } 199 200 // Drag originated from same profile and is not a URL. Only accept it if 201 // the dragged node is not a parent of the node menu represents. 202 if (menu_id_to_node_map_.find(menu->GetCommand()) == 203 menu_id_to_node_map_.end()) { 204 // If we don't know the menu assume its because we're embedded. We'll 205 // figure out the real operation when GetDropOperation is invoked. 206 return true; 207 } 208 const BookmarkNode* drop_node = menu_id_to_node_map_[menu->GetCommand()]; 209 DCHECK(drop_node); 210 while (drop_node && drop_node != drag_node) 211 drop_node = drop_node->parent(); 212 return (drop_node == NULL); 213} 214 215int BookmarkMenuDelegate::GetDropOperation( 216 MenuItemView* item, 217 const ui::DropTargetEvent& event, 218 views::MenuDelegate::DropPosition* position) { 219 // Should only get here if we have drop data. 220 DCHECK(drop_data_.is_valid()); 221 222 const BookmarkNode* node = menu_id_to_node_map_[item->GetCommand()]; 223 const BookmarkNode* drop_parent = node->parent(); 224 int index_to_drop_at = drop_parent->GetIndexOf(node); 225 BookmarkModel* model = GetBookmarkModel(); 226 switch (*position) { 227 case views::MenuDelegate::DROP_AFTER: 228 if (node == model->other_node() || node == model->mobile_node()) { 229 // Dropping after these nodes makes no sense. 230 *position = views::MenuDelegate::DROP_NONE; 231 } 232 index_to_drop_at++; 233 break; 234 235 case views::MenuDelegate::DROP_BEFORE: 236 if (node == model->mobile_node()) { 237 // Dropping before this node makes no sense. 238 *position = views::MenuDelegate::DROP_NONE; 239 } 240 break; 241 242 case views::MenuDelegate::DROP_ON: 243 drop_parent = node; 244 index_to_drop_at = node->child_count(); 245 break; 246 247 default: 248 break; 249 } 250 DCHECK(drop_parent); 251 return chrome::GetBookmarkDropOperation( 252 profile_, event, drop_data_, drop_parent, index_to_drop_at); 253} 254 255int BookmarkMenuDelegate::OnPerformDrop( 256 MenuItemView* menu, 257 views::MenuDelegate::DropPosition position, 258 const ui::DropTargetEvent& event) { 259 const BookmarkNode* drop_node = menu_id_to_node_map_[menu->GetCommand()]; 260 DCHECK(drop_node); 261 BookmarkModel* model = GetBookmarkModel(); 262 DCHECK(model); 263 const BookmarkNode* drop_parent = drop_node->parent(); 264 DCHECK(drop_parent); 265 int index_to_drop_at = drop_parent->GetIndexOf(drop_node); 266 switch (position) { 267 case views::MenuDelegate::DROP_AFTER: 268 index_to_drop_at++; 269 break; 270 271 case views::MenuDelegate::DROP_ON: 272 DCHECK(drop_node->is_folder()); 273 drop_parent = drop_node; 274 index_to_drop_at = drop_node->child_count(); 275 break; 276 277 case views::MenuDelegate::DROP_BEFORE: 278 if (drop_node == model->other_node() || 279 drop_node == model->mobile_node()) { 280 // This can happen with SHOW_PERMANENT_FOLDERS. 281 drop_parent = model->bookmark_bar_node(); 282 index_to_drop_at = drop_parent->child_count(); 283 } 284 break; 285 286 default: 287 break; 288 } 289 290 bool copy = event.source_operations() == ui::DragDropTypes::DRAG_COPY; 291 return chrome::DropBookmarks(profile_, drop_data_, 292 drop_parent, index_to_drop_at, copy); 293} 294 295bool BookmarkMenuDelegate::ShowContextMenu(MenuItemView* source, 296 int id, 297 const gfx::Point& p, 298 ui::MenuSourceType source_type) { 299 DCHECK(menu_id_to_node_map_.find(id) != menu_id_to_node_map_.end()); 300 std::vector<const BookmarkNode*> nodes; 301 nodes.push_back(menu_id_to_node_map_[id]); 302 bool close_on_delete = !parent_menu_item_ && 303 (nodes[0]->parent() == GetBookmarkModel()->other_node() && 304 nodes[0]->parent()->child_count() == 1); 305 context_menu_.reset( 306 new BookmarkContextMenu( 307 parent_, 308 browser_, 309 profile_, 310 page_navigator_, 311 nodes[0]->parent(), 312 nodes, 313 close_on_delete)); 314 context_menu_->set_observer(this); 315 context_menu_->RunMenuAt(p, source_type); 316 context_menu_.reset(NULL); 317 return true; 318} 319 320bool BookmarkMenuDelegate::CanDrag(MenuItemView* menu) { 321 const BookmarkNode* node = menu_id_to_node_map_[menu->GetCommand()]; 322 // Don't let users drag the other folder. 323 return node->parent() != GetBookmarkModel()->root_node(); 324} 325 326void BookmarkMenuDelegate::WriteDragData(MenuItemView* sender, 327 ui::OSExchangeData* data) { 328 DCHECK(sender && data); 329 330 content::RecordAction(UserMetricsAction("BookmarkBar_DragFromFolder")); 331 332 BookmarkNodeData drag_data(menu_id_to_node_map_[sender->GetCommand()]); 333 drag_data.Write(profile_->GetPath(), data); 334} 335 336int BookmarkMenuDelegate::GetDragOperations(MenuItemView* sender) { 337 return chrome::GetBookmarkDragOperation( 338 profile_, menu_id_to_node_map_[sender->GetCommand()]); 339} 340 341int BookmarkMenuDelegate::GetMaxWidthForMenu(MenuItemView* menu) { 342 return kMaxMenuWidth; 343} 344 345void BookmarkMenuDelegate::BookmarkModelChanged() { 346} 347 348void BookmarkMenuDelegate::BookmarkNodeFaviconChanged( 349 BookmarkModel* model, 350 const BookmarkNode* node) { 351 NodeToMenuMap::iterator menu_pair = node_to_menu_map_.find(node); 352 if (menu_pair == node_to_menu_map_.end()) 353 return; // We're not showing a menu item for the node. 354 355 menu_pair->second->SetIcon(model->GetFavicon(node).AsImageSkia()); 356} 357 358void BookmarkMenuDelegate::WillRemoveBookmarks( 359 const std::vector<const BookmarkNode*>& bookmarks) { 360 DCHECK(!is_mutating_model_); 361 is_mutating_model_ = true; // Set to false in DidRemoveBookmarks(). 362 363 // Remove the observer so that when the remove happens we don't prematurely 364 // cancel the menu. The observer is added back in DidRemoveBookmarks(). 365 GetBookmarkModel()->RemoveObserver(this); 366 367 // Remove the menu items. 368 std::set<MenuItemView*> changed_parent_menus; 369 for (std::vector<const BookmarkNode*>::const_iterator i(bookmarks.begin()); 370 i != bookmarks.end(); ++i) { 371 NodeToMenuMap::iterator node_to_menu = node_to_menu_map_.find(*i); 372 if (node_to_menu != node_to_menu_map_.end()) { 373 MenuItemView* menu = node_to_menu->second; 374 MenuItemView* parent = menu->GetParentMenuItem(); 375 // |parent| is NULL when removing a root. This happens when right clicking 376 // to delete an empty folder. 377 if (parent) { 378 changed_parent_menus.insert(parent); 379 parent->RemoveMenuItemAt(menu->parent()->GetIndexOf(menu)); 380 } 381 node_to_menu_map_.erase(node_to_menu); 382 menu_id_to_node_map_.erase(menu->GetCommand()); 383 } 384 } 385 386 // All the bookmarks in |bookmarks| should have the same parent. It's possible 387 // to support different parents, but this would need to prune any nodes whose 388 // parent has been removed. As all nodes currently have the same parent, there 389 // is the DCHECK. 390 DCHECK(changed_parent_menus.size() <= 1); 391 392 // Remove any descendants of the removed nodes in |node_to_menu_map_|. 393 for (NodeToMenuMap::iterator i(node_to_menu_map_.begin()); 394 i != node_to_menu_map_.end(); ) { 395 bool ancestor_removed = false; 396 for (std::vector<const BookmarkNode*>::const_iterator j(bookmarks.begin()); 397 j != bookmarks.end(); ++j) { 398 if (i->first->HasAncestor(*j)) { 399 ancestor_removed = true; 400 break; 401 } 402 } 403 if (ancestor_removed) { 404 menu_id_to_node_map_.erase(i->second->GetCommand()); 405 node_to_menu_map_.erase(i++); 406 } else { 407 ++i; 408 } 409 } 410 411 for (std::set<MenuItemView*>::const_iterator i(changed_parent_menus.begin()); 412 i != changed_parent_menus.end(); ++i) 413 (*i)->ChildrenChanged(); 414} 415 416void BookmarkMenuDelegate::DidRemoveBookmarks() { 417 // Balances remove in WillRemoveBookmarksImpl. 418 GetBookmarkModel()->AddObserver(this); 419 DCHECK(is_mutating_model_); 420 is_mutating_model_ = false; 421} 422 423MenuItemView* BookmarkMenuDelegate::CreateMenu(const BookmarkNode* parent, 424 int start_child_index, 425 ShowOptions show_options) { 426 MenuItemView* menu = new MenuItemView(real_delegate_); 427 menu->SetCommand(next_menu_id_++); 428 menu_id_to_node_map_[menu->GetCommand()] = parent; 429 menu->set_has_icons(true); 430 bool show_permanent = show_options == SHOW_PERMANENT_FOLDERS; 431 if (show_permanent && parent == GetBookmarkModel()->bookmark_bar_node()) 432 BuildMenuForManagedNode(menu, &next_menu_id_); 433 BuildMenu(parent, start_child_index, menu, &next_menu_id_); 434 if (show_permanent) 435 BuildMenusForPermanentNodes(menu, &next_menu_id_); 436 return menu; 437} 438 439void BookmarkMenuDelegate::BuildMenusForPermanentNodes( 440 views::MenuItemView* menu, 441 int* next_menu_id) { 442 BookmarkModel* model = GetBookmarkModel(); 443 bool added_separator = false; 444 BuildMenuForPermanentNode(model->other_node(), menu, next_menu_id, 445 &added_separator); 446 BuildMenuForPermanentNode(model->mobile_node(), menu, next_menu_id, 447 &added_separator); 448} 449 450void BookmarkMenuDelegate::BuildMenuForPermanentNode( 451 const BookmarkNode* node, 452 MenuItemView* menu, 453 int* next_menu_id, 454 bool* added_separator) { 455 if (!node->IsVisible() || node->GetTotalNodeCount() == 1) 456 return; // No children, don't create a menu. 457 458 int id = *next_menu_id; 459 // Don't create the submenu if its menu ID will be outside the range allowed. 460 if (IsOutsideMenuIdRange(id)) 461 return; 462 (*next_menu_id)++; 463 464 if (!*added_separator) { 465 *added_separator = true; 466 menu->AppendSeparator(); 467 } 468 469 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 470 gfx::ImageSkia* folder_icon = rb->GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER); 471 MenuItemView* submenu = menu->AppendSubMenuWithIcon( 472 id, node->GetTitle(), *folder_icon); 473 BuildMenu(node, 0, submenu, next_menu_id); 474 menu_id_to_node_map_[id] = node; 475} 476 477void BookmarkMenuDelegate::BuildMenuForManagedNode( 478 MenuItemView* menu, 479 int* next_menu_id) { 480 // Don't add a separator for this menu. 481 bool added_separator = true; 482 const BookmarkNode* node = GetChromeBookmarkClient()->managed_node(); 483 // TODO(joaodasilva): use the "managed bookmark folder" icon here. 484 // http://crbug.com/49598 485 BuildMenuForPermanentNode(node, menu, next_menu_id, &added_separator); 486} 487 488void BookmarkMenuDelegate::BuildMenu(const BookmarkNode* parent, 489 int start_child_index, 490 MenuItemView* menu, 491 int* next_menu_id) { 492 node_to_menu_map_[parent] = menu; 493 DCHECK(parent->empty() || start_child_index < parent->child_count()); 494 ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); 495 for (int i = start_child_index; i < parent->child_count(); ++i) { 496 const BookmarkNode* node = parent->GetChild(i); 497 const int id = *next_menu_id; 498 // Don't create the item if its menu ID will be outside the range allowed. 499 if (IsOutsideMenuIdRange(id)) 500 break; 501 502 (*next_menu_id)++; 503 504 menu_id_to_node_map_[id] = node; 505 if (node->is_url()) { 506 const gfx::Image& image = GetBookmarkModel()->GetFavicon(node); 507 const gfx::ImageSkia* icon = image.IsEmpty() ? 508 rb->GetImageSkiaNamed(IDR_DEFAULT_FAVICON) : image.ToImageSkia(); 509 node_to_menu_map_[node] = 510 menu->AppendMenuItemWithIcon(id, node->GetTitle(), *icon); 511 } else if (node->is_folder()) { 512 gfx::ImageSkia* folder_icon = 513 rb->GetImageSkiaNamed(IDR_BOOKMARK_BAR_FOLDER); 514 MenuItemView* submenu = menu->AppendSubMenuWithIcon( 515 id, node->GetTitle(), *folder_icon); 516 BuildMenu(node, 0, submenu, next_menu_id); 517 } else { 518 NOTREACHED(); 519 } 520 } 521} 522 523bool BookmarkMenuDelegate::IsOutsideMenuIdRange(int menu_id) const { 524 return menu_id < min_menu_id_ || menu_id > max_menu_id_; 525} 526