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.
5 #include "chrome/browser/ui/views/extensions/browser_action_overflow_menu_controller.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/extensions/extension_action.h"
10 #include "chrome/browser/extensions/extension_action_manager.h"
11 #include "chrome/browser/extensions/extension_context_menu_model.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/ui/browser_list.h"
14 #include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
15 #include "chrome/browser/ui/views/toolbar/browser_action_view.h"
16 #include "chrome/browser/ui/views/toolbar/browser_actions_container.h"
17 #include "extensions/common/extension.h"
18 #include "ui/gfx/canvas.h"
19 #include "ui/views/controls/menu/menu_item_view.h"
20 #include "ui/views/controls/menu/menu_runner.h"
21 #include "ui/views/controls/menu/submenu_view.h"
22 #include "ui/views/widget/widget.h"
24 BrowserActionOverflowMenuController::BrowserActionOverflowMenuController(
25 BrowserActionsContainer
* owner
,
27 views::MenuButton
* menu_button
,
28 const std::vector
<BrowserActionView
*>& views
,
33 menu_button_(menu_button
),
36 start_index_(start_index
),
38 menu_
= new views::MenuItemView(this);
39 menu_runner_
.reset(new views::MenuRunner(menu_
));
40 menu_
->set_has_icons(true);
42 size_t command_id
= 1; // Menu id 0 is reserved, start with 1.
43 for (size_t i
= start_index
; i
< views_
->size(); ++i
) {
44 BrowserActionView
* view
= (*views_
)[i
];
45 menu_
->AppendMenuItemWithIcon(
47 base::UTF8ToUTF16(view
->button()->extension()->name()),
48 view
->GetIconWithBadge());
50 // Set the tooltip for this item.
51 base::string16 tooltip
= base::UTF8ToUTF16(
52 extensions::ExtensionActionManager::Get(owner_
->profile())->
53 GetBrowserAction(*view
->button()->extension())->
54 GetTitle(owner_
->GetCurrentTabId()));
55 menu_
->SetTooltip(tooltip
, command_id
);
61 BrowserActionOverflowMenuController::~BrowserActionOverflowMenuController() {
63 observer_
->NotifyMenuDeleted(this);
66 bool BrowserActionOverflowMenuController::RunMenu(views::Widget
* window
,
70 gfx::Rect bounds
= menu_button_
->bounds();
71 gfx::Point screen_loc
;
72 views::View::ConvertPointToScreen(menu_button_
, &screen_loc
);
73 bounds
.set_x(screen_loc
.x());
74 bounds
.set_y(screen_loc
.y());
76 views::MenuItemView::AnchorPosition anchor
= views::MenuItemView::TOPRIGHT
;
77 // As we maintain our own lifetime we can safely ignore the result.
78 ignore_result(menu_runner_
->RunMenuAt(window
, menu_button_
, bounds
, anchor
,
79 ui::MENU_SOURCE_NONE
, for_drop_
? views::MenuRunner::FOR_DROP
: 0));
81 // Give the context menu (if any) a chance to execute the user-selected
83 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
88 void BrowserActionOverflowMenuController::CancelMenu() {
92 bool BrowserActionOverflowMenuController::IsCommandEnabled(int id
) const {
93 BrowserActionView
* view
= (*views_
)[start_index_
+ id
- 1];
94 return view
->button()->IsEnabled(owner_
->GetCurrentTabId());
97 void BrowserActionOverflowMenuController::ExecuteCommand(int id
) {
98 BrowserActionView
* view
= (*views_
)[start_index_
+ id
- 1];
99 owner_
->OnBrowserActionExecuted(view
->button());
102 bool BrowserActionOverflowMenuController::ShowContextMenu(
103 views::MenuItemView
* source
,
106 ui::MenuSourceType source_type
) {
107 const extensions::Extension
* extension
=
108 (*views_
)[start_index_
+ id
- 1]->button()->extension();
109 if (!extension
->ShowConfigureContextMenus())
112 scoped_refptr
<ExtensionContextMenuModel
> context_menu_contents
=
113 new ExtensionContextMenuModel(extension
, browser_
, owner_
);
114 views::MenuRunner
context_menu_runner(context_menu_contents
.get());
116 // We can ignore the result as we delete ourself.
117 // This blocks until the user choses something or dismisses the menu.
118 ignore_result(context_menu_runner
.RunMenuAt(menu_button_
->GetWidget(),
119 NULL
, gfx::Rect(p
, gfx::Size()), views::MenuItemView::TOPLEFT
,
121 views::MenuRunner::HAS_MNEMONICS
| views::MenuRunner::IS_NESTED
|
122 views::MenuRunner::CONTEXT_MENU
));
124 // The user is done with the context menu, so we can close the underlying
131 void BrowserActionOverflowMenuController::DropMenuClosed(
132 views::MenuItemView
* menu
) {
136 bool BrowserActionOverflowMenuController::GetDropFormats(
137 views::MenuItemView
* menu
,
139 std::set
<OSExchangeData::CustomFormat
>* custom_formats
) {
140 custom_formats
->insert(BrowserActionDragData::GetBrowserActionCustomFormat());
144 bool BrowserActionOverflowMenuController::AreDropTypesRequired(
145 views::MenuItemView
* menu
) {
149 bool BrowserActionOverflowMenuController::CanDrop(
150 views::MenuItemView
* menu
, const OSExchangeData
& data
) {
151 BrowserActionDragData drop_data
;
152 if (!drop_data
.Read(data
))
154 return drop_data
.IsFromProfile(owner_
->profile());
157 int BrowserActionOverflowMenuController::GetDropOperation(
158 views::MenuItemView
* item
,
159 const ui::DropTargetEvent
& event
,
160 DropPosition
* position
) {
161 // Don't allow dropping from the BrowserActionContainer into slot 0 of the
162 // overflow menu since once the move has taken place the item you are dragging
163 // falls right out of the menu again once the user releases the button
164 // (because we don't shrink the BrowserActionContainer when you do this).
165 if ((item
->GetCommand() == 0) && (*position
== DROP_BEFORE
)) {
166 BrowserActionDragData drop_data
;
167 if (!drop_data
.Read(event
.data()))
168 return ui::DragDropTypes::DRAG_NONE
;
170 if (drop_data
.index() < owner_
->VisibleBrowserActions())
171 return ui::DragDropTypes::DRAG_NONE
;
174 return ui::DragDropTypes::DRAG_MOVE
;
177 int BrowserActionOverflowMenuController::OnPerformDrop(
178 views::MenuItemView
* menu
,
179 DropPosition position
,
180 const ui::DropTargetEvent
& event
) {
181 BrowserActionDragData drop_data
;
182 if (!drop_data
.Read(event
.data()))
183 return ui::DragDropTypes::DRAG_NONE
;
186 ViewForId(menu
->GetCommand(), &drop_index
);
188 // When not dragging within the overflow menu (dragging an icon into the menu)
189 // subtract one to get the right index.
190 if (position
== DROP_BEFORE
&&
191 drop_data
.index() < owner_
->VisibleBrowserActions())
194 owner_
->MoveBrowserAction(drop_data
.id(), drop_index
);
198 return ui::DragDropTypes::DRAG_MOVE
;
201 bool BrowserActionOverflowMenuController::CanDrag(views::MenuItemView
* menu
) {
205 void BrowserActionOverflowMenuController::WriteDragData(
206 views::MenuItemView
* sender
, OSExchangeData
* data
) {
208 BrowserActionView
* view
= ViewForId(sender
->GetCommand(), &drag_index
);
209 std::string id
= view
->button()->extension()->id();
211 BrowserActionDragData
drag_data(id
, drag_index
);
212 drag_data
.Write(owner_
->profile(), data
);
215 int BrowserActionOverflowMenuController::GetDragOperations(
216 views::MenuItemView
* sender
) {
217 return ui::DragDropTypes::DRAG_MOVE
;
220 BrowserActionView
* BrowserActionOverflowMenuController::ViewForId(
221 int id
, size_t* index
) {
222 // The index of the view being dragged (GetCommand gives a 1-based index into
223 // the overflow menu).
224 size_t view_index
= owner_
->VisibleBrowserActions() + id
- 1;
227 return owner_
->GetBrowserActionViewAt(view_index
);