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.
4
5#include "chrome/browser/ui/gtk/tab_contents/render_view_context_menu_gtk.h"
6
7#include <gtk/gtk.h>
8
9#include "base/strings/string_util.h"
10#include "base/strings/utf_string_conversions.h"
11#include "chrome/app/chrome_command_ids.h"
12#include "chrome/browser/ui/gtk/gtk_util.h"
13#include "content/public/browser/render_widget_host_view.h"
14#include "content/public/browser/web_contents.h"
15#include "content/public/common/context_menu_params.h"
16#include "grit/generated_resources.h"
17#include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
18#include "ui/base/l10n/l10n_util.h"
19
20using content::WebContents;
21
22namespace {
23
24// A callback function for gtk_container_foreach(). This callback just checks
25// the menu ID and set the given user data if it is same as the specified ID.
26struct GtkWidgetAtParam {
27  int index;
28  GtkWidget* widget;
29};
30
31void GtkWidgetAt(GtkWidget* widget, gpointer user_data) {
32  GtkWidgetAtParam* param = reinterpret_cast<GtkWidgetAtParam*>(user_data);
33
34  gpointer data = g_object_get_data(G_OBJECT(widget), "menu-id");
35  if (data && (GPOINTER_TO_INT(data) - 1) == param->index &&
36      GTK_IS_MENU_ITEM(widget)) {
37    param->widget = widget;
38  }
39}
40
41// Retrieves a GtkWidget which has the specified command_id. This function
42// traverses the given |model| in the depth-first order. When this function
43// finds an item whose command_id is the same as the given |command_id|, it
44// returns the GtkWidget associated with the item. This function emulates
45// views::MenuItemViews::GetMenuItemByID() for GTK.
46GtkWidget* GetMenuItemByID(ui::MenuModel* model,
47                           GtkWidget* menu,
48                           int command_id) {
49  if (!menu)
50    return NULL;
51
52  for (int i = 0; i < model->GetItemCount(); ++i) {
53    if (model->GetCommandIdAt(i) == command_id) {
54      GtkWidgetAtParam param;
55      param.index = i;
56      param.widget = NULL;
57      gtk_container_foreach(GTK_CONTAINER(menu), &GtkWidgetAt, &param);
58      return param.widget;
59    }
60
61    ui::MenuModel* submenu = model->GetSubmenuModelAt(i);
62    if (submenu) {
63      GtkWidget* subitem = GetMenuItemByID(
64          submenu,
65          gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu)),
66          command_id);
67      if (subitem)
68        return subitem;
69    }
70  }
71  return NULL;
72}
73
74}  // namespace
75
76RenderViewContextMenuGtk::RenderViewContextMenuGtk(
77    WebContents* web_contents,
78    const content::ContextMenuParams& params,
79    content::RenderWidgetHostView* view)
80    : RenderViewContextMenu(web_contents, params) {
81  GdkEventButton* event = view->GetLastMouseDown();
82  triggering_event_time_ = event ? event->time : GDK_CURRENT_TIME;
83}
84
85RenderViewContextMenuGtk::~RenderViewContextMenuGtk() {
86}
87
88void RenderViewContextMenuGtk::PlatformInit() {
89  menu_gtk_.reset(new MenuGtk(this, &menu_model_));
90
91  if (params_.is_editable) {
92    content::RenderWidgetHostView* rwhv =
93        source_web_contents_->GetRenderWidgetHostView();
94    if (rwhv) {
95      MenuGtk* menu = menu_gtk_.get();
96      gboolean show_input_method_menu = TRUE;
97
98      g_object_get(
99          gtk_widget_get_settings(GTK_WIDGET(rwhv->GetNativeView())),
100          "gtk-show-input-method-menu", &show_input_method_menu, NULL);
101      if (!show_input_method_menu)
102        return;
103
104      std::string label = ui::ConvertAcceleratorsFromWindowsStyle(
105          l10n_util::GetStringUTF8(IDS_CONTENT_CONTEXT_INPUT_METHODS_MENU));
106      GtkWidget* menuitem = gtk_menu_item_new_with_mnemonic(label.c_str());
107      GtkWidget* submenu = rwhv->BuildInputMethodsGtkMenu();
108      gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
109      menu->AppendSeparator();
110      menu->AppendMenuItem(IDC_INPUT_METHODS_MENU, menuitem);
111    }
112  }
113}
114
115void RenderViewContextMenuGtk::PlatformCancel() {
116  menu_gtk_->Cancel();
117}
118
119bool RenderViewContextMenuGtk::GetAcceleratorForCommandId(
120    int command_id,
121    ui::Accelerator* accelerator) {
122  return false;
123}
124
125void RenderViewContextMenuGtk::Popup(const gfx::Point& point) {
126  menu_gtk_->PopupAsContext(point, triggering_event_time_);
127}
128
129bool RenderViewContextMenuGtk::AlwaysShowIconForCmd(int command_id) const {
130  return command_id >= IDC_EXTENSIONS_CONTEXT_CUSTOM_FIRST &&
131      command_id <= IDC_EXTENSIONS_CONTEXT_CUSTOM_LAST;
132}
133
134void RenderViewContextMenuGtk::UpdateMenuItem(int command_id,
135                                              bool enabled,
136                                              bool hidden,
137                                              const string16& title) {
138  GtkWidget* item = GetMenuItemByID(&menu_model_, menu_gtk_->widget(),
139                                    command_id);
140  if (!item || !GTK_IS_MENU_ITEM(item))
141    return;
142
143  // Enable (or disable) the menu item and updates its text.
144  gtk_widget_set_sensitive(item, enabled);
145  if (hidden)
146    gtk_widget_hide(item);
147  else
148    gtk_widget_show(item);
149  gtk_menu_item_set_label(GTK_MENU_ITEM(item), UTF16ToUTF8(title).c_str());
150}
151