1 // Copyright (c) 2011 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/extensions/browser_action_overflow_menu_controller.h" 6 7 #include "base/utf_string_conversions.h" 8 #include "chrome/browser/extensions/extension_context_menu_model.h" 9 #include "chrome/browser/profiles/profile.h" 10 #include "chrome/browser/ui/browser_list.h" 11 #include "chrome/browser/ui/views/browser_actions_container.h" 12 #include "chrome/browser/ui/views/extensions/browser_action_drag_data.h" 13 #include "chrome/common/extensions/extension.h" 14 #include "chrome/common/extensions/extension_action.h" 15 #include "ui/gfx/canvas_skia.h" 16 #include "views/controls/menu/menu_2.h" 17 #include "views/controls/menu/menu_item_view.h" 18 19 BrowserActionOverflowMenuController::BrowserActionOverflowMenuController( 20 BrowserActionsContainer* owner, 21 views::MenuButton* menu_button, 22 const std::vector<BrowserActionView*>& views, 23 int start_index) 24 : owner_(owner), 25 observer_(NULL), 26 menu_button_(menu_button), 27 views_(&views), 28 start_index_(start_index), 29 for_drop_(false) { 30 menu_.reset(new views::MenuItemView(this)); 31 menu_->set_has_icons(true); 32 33 size_t command_id = 1; // Menu id 0 is reserved, start with 1. 34 for (size_t i = start_index; i < views_->size(); ++i) { 35 BrowserActionView* view = (*views_)[i]; 36 scoped_ptr<gfx::Canvas> canvas(view->GetIconWithBadge()); 37 menu_->AppendMenuItemWithIcon( 38 command_id, 39 UTF8ToWide(view->button()->extension()->name()), 40 canvas->AsCanvasSkia()->ExtractBitmap()); 41 42 // Set the tooltip for this item. 43 std::wstring tooltip = UTF8ToWide( 44 view->button()->extension()->browser_action()->GetTitle( 45 owner_->GetCurrentTabId())); 46 menu_->SetTooltip(tooltip, command_id); 47 48 ++command_id; 49 } 50 } 51 52 BrowserActionOverflowMenuController::~BrowserActionOverflowMenuController() { 53 if (observer_) 54 observer_->NotifyMenuDeleted(this); 55 } 56 57 bool BrowserActionOverflowMenuController::RunMenu(gfx::NativeWindow window, 58 bool for_drop) { 59 for_drop_ = for_drop; 60 61 gfx::Rect bounds = menu_button_->bounds(); 62 gfx::Point screen_loc; 63 views::View::ConvertPointToScreen(menu_button_, &screen_loc); 64 bounds.set_x(screen_loc.x()); 65 bounds.set_y(screen_loc.y()); 66 67 views::MenuItemView::AnchorPosition anchor = base::i18n::IsRTL() ? 68 views::MenuItemView::TOPLEFT : views::MenuItemView::TOPRIGHT; 69 if (for_drop) { 70 menu_->RunMenuForDropAt(window, bounds, anchor); 71 } else { 72 menu_->RunMenuAt(window, menu_button_, bounds, anchor, false); 73 // Give the context menu (if any) a chance to execute the user-selected 74 // command. 75 MessageLoop::current()->DeleteSoon(FROM_HERE, this); 76 } 77 return true; 78 } 79 80 void BrowserActionOverflowMenuController::CancelMenu() { 81 menu_->Cancel(); 82 } 83 84 void BrowserActionOverflowMenuController::ExecuteCommand(int id) { 85 BrowserActionView* view = (*views_)[start_index_ + id - 1]; 86 owner_->OnBrowserActionExecuted(view->button(), 87 false); // inspect_with_devtools 88 } 89 90 bool BrowserActionOverflowMenuController::ShowContextMenu( 91 views::MenuItemView* source, 92 int id, 93 const gfx::Point& p, 94 bool is_mouse_gesture) { 95 const Extension* extension = 96 (*views_)[start_index_ + id - 1]->button()->extension(); 97 if (!extension->ShowConfigureContextMenus()) 98 return false; 99 100 context_menu_contents_ = new ExtensionContextMenuModel( 101 extension, 102 owner_->browser(), 103 owner_); 104 context_menu_menu_.reset(new views::Menu2(context_menu_contents_.get())); 105 // This blocks until the user choses something or dismisses the menu. 106 context_menu_menu_->RunContextMenuAt(p); 107 108 // The user is done with the context menu, so we can close the underlying 109 // menu. 110 menu_->Cancel(); 111 112 return true; 113 } 114 115 void BrowserActionOverflowMenuController::DropMenuClosed( 116 views::MenuItemView* menu) { 117 delete this; 118 } 119 120 bool BrowserActionOverflowMenuController::GetDropFormats( 121 views::MenuItemView* menu, 122 int* formats, 123 std::set<OSExchangeData::CustomFormat>* custom_formats) { 124 custom_formats->insert(BrowserActionDragData::GetBrowserActionCustomFormat()); 125 return true; 126 } 127 128 bool BrowserActionOverflowMenuController::AreDropTypesRequired( 129 views::MenuItemView* menu) { 130 return true; 131 } 132 133 bool BrowserActionOverflowMenuController::CanDrop( 134 views::MenuItemView* menu, const OSExchangeData& data) { 135 BrowserActionDragData drop_data; 136 if (!drop_data.Read(data)) 137 return false; 138 return drop_data.IsFromProfile(owner_->profile()); 139 } 140 141 int BrowserActionOverflowMenuController::GetDropOperation( 142 views::MenuItemView* item, 143 const views::DropTargetEvent& event, 144 DropPosition* position) { 145 // Don't allow dropping from the BrowserActionContainer into slot 0 of the 146 // overflow menu since once the move has taken place the item you are dragging 147 // falls right out of the menu again once the user releases the button 148 // (because we don't shrink the BrowserActionContainer when you do this). 149 if ((item->GetCommand() == 0) && (*position == DROP_BEFORE)) { 150 BrowserActionDragData drop_data; 151 if (!drop_data.Read(event.data())) 152 return ui::DragDropTypes::DRAG_NONE; 153 154 if (drop_data.index() < owner_->VisibleBrowserActions()) 155 return ui::DragDropTypes::DRAG_NONE; 156 } 157 158 return ui::DragDropTypes::DRAG_MOVE; 159 } 160 161 int BrowserActionOverflowMenuController::OnPerformDrop( 162 views::MenuItemView* menu, 163 DropPosition position, 164 const views::DropTargetEvent& event) { 165 BrowserActionDragData drop_data; 166 if (!drop_data.Read(event.data())) 167 return ui::DragDropTypes::DRAG_NONE; 168 169 size_t drop_index; 170 ViewForId(menu->GetCommand(), &drop_index); 171 172 // When not dragging within the overflow menu (dragging an icon into the menu) 173 // subtract one to get the right index. 174 if (position == DROP_BEFORE && 175 drop_data.index() < owner_->VisibleBrowserActions()) 176 --drop_index; 177 178 owner_->MoveBrowserAction(drop_data.id(), drop_index); 179 180 if (for_drop_) 181 delete this; 182 return ui::DragDropTypes::DRAG_MOVE; 183 } 184 185 bool BrowserActionOverflowMenuController::CanDrag(views::MenuItemView* menu) { 186 return true; 187 } 188 189 void BrowserActionOverflowMenuController::WriteDragData( 190 views::MenuItemView* sender, OSExchangeData* data) { 191 size_t drag_index; 192 BrowserActionView* view = ViewForId(sender->GetCommand(), &drag_index); 193 std::string id = view->button()->extension()->id(); 194 195 BrowserActionDragData drag_data(id, drag_index); 196 drag_data.Write(owner_->profile(), data); 197 } 198 199 int BrowserActionOverflowMenuController::GetDragOperations( 200 views::MenuItemView* sender) { 201 return ui::DragDropTypes::DRAG_MOVE; 202 } 203 204 BrowserActionView* BrowserActionOverflowMenuController::ViewForId( 205 int id, size_t* index) { 206 // The index of the view being dragged (GetCommand gives a 1-based index into 207 // the overflow menu). 208 size_t view_index = owner_->VisibleBrowserActions() + id - 1; 209 if (index) 210 *index = view_index; 211 return owner_->GetBrowserActionViewAt(view_index); 212 } 213