Home | History | Annotate | Download | only in extensions
      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