Index: chrome/browser/ui/gtk/menu_gtk.cc |
diff --git a/chrome/browser/ui/gtk/menu_gtk.cc b/chrome/browser/ui/gtk/menu_gtk.cc |
deleted file mode 100644 |
index b9c850c5d8181577932c8eeef831e5758ba95b42..0000000000000000000000000000000000000000 |
--- a/chrome/browser/ui/gtk/menu_gtk.cc |
+++ /dev/null |
@@ -1,983 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "chrome/browser/ui/gtk/menu_gtk.h" |
- |
-#include <map> |
- |
-#include "base/bind.h" |
-#include "base/i18n/rtl.h" |
-#include "base/logging.h" |
-#include "base/message_loop/message_loop.h" |
-#include "base/stl_util.h" |
-#include "base/strings/utf_string_conversions.h" |
-#include "chrome/app/chrome_command_ids.h" |
-#include "chrome/browser/ui/gtk/event_utils.h" |
-#include "chrome/browser/ui/gtk/gtk_custom_menu.h" |
-#include "chrome/browser/ui/gtk/gtk_custom_menu_item.h" |
-#include "chrome/browser/ui/gtk/gtk_util.h" |
-#include "third_party/skia/include/core/SkBitmap.h" |
-#include "ui/base/accelerators/menu_label_accelerator_util_linux.h" |
-#include "ui/base/accelerators/platform_accelerator_gtk.h" |
-#include "ui/base/models/button_menu_item_model.h" |
-#include "ui/base/models/menu_model.h" |
-#include "ui/base/window_open_disposition.h" |
-#include "ui/gfx/gtk_util.h" |
-#include "ui/gfx/image/image.h" |
- |
-bool MenuGtk::block_activation_ = false; |
- |
-namespace { |
- |
-// Sets the ID of a menu item. |
-void SetMenuItemID(GtkWidget* menu_item, int menu_id) { |
- DCHECK_GE(menu_id, 0); |
- |
- // Add 1 to the menu_id to avoid setting zero (null) to "menu-id". |
- g_object_set_data(G_OBJECT(menu_item), "menu-id", |
- GINT_TO_POINTER(menu_id + 1)); |
-} |
- |
-// Gets the ID of a menu item. |
-// Returns true if the menu item has an ID. |
-bool GetMenuItemID(GtkWidget* menu_item, int* menu_id) { |
- gpointer id_ptr = g_object_get_data(G_OBJECT(menu_item), "menu-id"); |
- if (id_ptr != NULL) { |
- *menu_id = GPOINTER_TO_INT(id_ptr) - 1; |
- return true; |
- } |
- |
- return false; |
-} |
- |
-ui::MenuModel* ModelForMenuItem(GtkMenuItem* menu_item) { |
- return reinterpret_cast<ui::MenuModel*>( |
- g_object_get_data(G_OBJECT(menu_item), "model")); |
-} |
- |
-void SetUpButtonShowHandler(GtkWidget* button, |
- ui::ButtonMenuItemModel* model, |
- int index) { |
- g_object_set_data(G_OBJECT(button), "button-model", |
- model); |
- g_object_set_data(G_OBJECT(button), "button-model-id", |
- GINT_TO_POINTER(index)); |
-} |
- |
-void OnSubmenuShowButtonImage(GtkWidget* widget, GtkButton* button) { |
- MenuGtk::Delegate* delegate = reinterpret_cast<MenuGtk::Delegate*>( |
- g_object_get_data(G_OBJECT(button), "menu-gtk-delegate")); |
- int icon_idr = GPOINTER_TO_INT(g_object_get_data( |
- G_OBJECT(button), "button-image-idr")); |
- |
- GtkIconSet* icon_set = delegate->GetIconSetForId(icon_idr); |
- if (icon_set) { |
- gtk_button_set_image( |
- button, gtk_image_new_from_icon_set(icon_set, |
- GTK_ICON_SIZE_MENU)); |
- } |
-} |
- |
-void SetupImageIcon(GtkWidget* button, |
- GtkWidget* menu, |
- int icon_idr, |
- MenuGtk::Delegate* menu_gtk_delegate) { |
- g_object_set_data(G_OBJECT(button), "button-image-idr", |
- GINT_TO_POINTER(icon_idr)); |
- g_object_set_data(G_OBJECT(button), "menu-gtk-delegate", |
- menu_gtk_delegate); |
- |
- g_signal_connect(menu, "show", G_CALLBACK(OnSubmenuShowButtonImage), button); |
-} |
- |
-// Popup menus may get squished if they open up too close to the bottom of the |
-// screen. This function takes the size of the screen, the size of the menu, |
-// an optional widget, the Y position of the mouse click, and adjusts the popup |
-// menu's Y position to make it fit if it's possible to do so. |
-// Returns the new Y position of the popup menu. |
-int CalculateMenuYPosition(const GdkRectangle* screen_rect, |
- const GtkRequisition* menu_req, |
- GtkWidget* widget, const int y) { |
- CHECK(screen_rect); |
- CHECK(menu_req); |
- // If the menu would run off the bottom of the screen, and there is enough |
- // screen space upwards to accommodate the menu, then pop upwards. If there |
- // is a widget, then also move the anchor point to the top of the widget |
- // rather than the bottom. |
- const int screen_top = screen_rect->y; |
- const int screen_bottom = screen_rect->y + screen_rect->height; |
- const int menu_bottom = y + menu_req->height; |
- int alternate_y = y - menu_req->height; |
- if (widget) { |
- GtkAllocation allocation; |
- gtk_widget_get_allocation(widget, &allocation); |
- alternate_y -= allocation.height; |
- } |
- if (menu_bottom >= screen_bottom && alternate_y >= screen_top) |
- return alternate_y; |
- return y; |
-} |
- |
-} // namespace |
- |
-bool MenuGtk::Delegate::AlwaysShowIconForCmd(int command_id) const { |
- return false; |
-} |
- |
-GtkIconSet* MenuGtk::Delegate::GetIconSetForId(int idr) { return NULL; } |
- |
-GtkWidget* MenuGtk::Delegate::GetDefaultImageForCommandId(int command_id) { |
- const char* stock; |
- switch (command_id) { |
- case IDC_NEW_TAB: |
- case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB: |
- case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE: |
- case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB: |
- case IDC_CONTENT_CONTEXT_OPENAVNEWTAB: |
- stock = GTK_STOCK_NEW; |
- break; |
- |
- case IDC_CLOSE_TAB: |
- stock = GTK_STOCK_CLOSE; |
- break; |
- |
- case IDC_CONTENT_CONTEXT_SAVEIMAGEAS: |
- case IDC_CONTENT_CONTEXT_SAVEAVAS: |
- case IDC_CONTENT_CONTEXT_SAVELINKAS: |
- stock = GTK_STOCK_SAVE_AS; |
- break; |
- |
- case IDC_SAVE_PAGE: |
- stock = GTK_STOCK_SAVE; |
- break; |
- |
- case IDC_COPY: |
- case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION: |
- case IDC_CONTENT_CONTEXT_COPYLINKLOCATION: |
- case IDC_CONTENT_CONTEXT_COPYAVLOCATION: |
- case IDC_CONTENT_CONTEXT_COPYEMAILADDRESS: |
- case IDC_CONTENT_CONTEXT_COPY: |
- stock = GTK_STOCK_COPY; |
- break; |
- |
- case IDC_CUT: |
- case IDC_CONTENT_CONTEXT_CUT: |
- stock = GTK_STOCK_CUT; |
- break; |
- |
- case IDC_PASTE: |
- case IDC_CONTENT_CONTEXT_PASTE: |
- stock = GTK_STOCK_PASTE; |
- break; |
- |
- case IDC_CONTENT_CONTEXT_DELETE: |
- case IDC_BOOKMARK_BAR_REMOVE: |
- stock = GTK_STOCK_DELETE; |
- break; |
- |
- case IDC_CONTENT_CONTEXT_UNDO: |
- stock = GTK_STOCK_UNDO; |
- break; |
- |
- case IDC_CONTENT_CONTEXT_REDO: |
- stock = GTK_STOCK_REDO; |
- break; |
- |
- case IDC_SEARCH: |
- case IDC_FIND: |
- case IDC_CONTENT_CONTEXT_SEARCHWEBFOR: |
- stock = GTK_STOCK_FIND; |
- break; |
- |
- case IDC_CONTENT_CONTEXT_SELECTALL: |
- stock = GTK_STOCK_SELECT_ALL; |
- break; |
- |
- case IDC_CLEAR_BROWSING_DATA: |
- stock = GTK_STOCK_CLEAR; |
- break; |
- |
- case IDC_BACK: |
- stock = GTK_STOCK_GO_BACK; |
- break; |
- |
- case IDC_RELOAD: |
- stock = GTK_STOCK_REFRESH; |
- break; |
- |
- case IDC_FORWARD: |
- stock = GTK_STOCK_GO_FORWARD; |
- break; |
- |
- case IDC_PRINT: |
- stock = GTK_STOCK_PRINT; |
- break; |
- |
- case IDC_CONTENT_CONTEXT_VIEWPAGEINFO: |
- stock = GTK_STOCK_INFO; |
- break; |
- |
- case IDC_SPELLCHECK_MENU: |
- stock = GTK_STOCK_SPELL_CHECK; |
- break; |
- |
- case IDC_RESTORE_TAB: |
- stock = GTK_STOCK_UNDELETE; |
- break; |
- |
- case IDC_HOME: |
- stock = GTK_STOCK_HOME; |
- break; |
- |
- case IDC_STOP: |
- stock = GTK_STOCK_STOP; |
- break; |
- |
- case IDC_ABOUT: |
- stock = GTK_STOCK_ABOUT; |
- break; |
- |
- case IDC_EXIT: |
- stock = GTK_STOCK_QUIT; |
- break; |
- |
- case IDC_HELP_PAGE_VIA_MENU: |
- stock = GTK_STOCK_HELP; |
- break; |
- |
- case IDC_OPTIONS: |
- stock = GTK_STOCK_PREFERENCES; |
- break; |
- |
- case IDC_CONTENT_CONTEXT_GOTOURL: |
- stock = GTK_STOCK_JUMP_TO; |
- break; |
- |
- case IDC_DEV_TOOLS_INSPECT: |
- case IDC_CONTENT_CONTEXT_INSPECTELEMENT: |
- stock = GTK_STOCK_PROPERTIES; |
- break; |
- |
- case IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK: |
- stock = GTK_STOCK_ADD; |
- break; |
- |
- case IDC_BOOKMARK_BAR_RENAME_FOLDER: |
- case IDC_BOOKMARK_BAR_EDIT: |
- stock = GTK_STOCK_EDIT; |
- break; |
- |
- case IDC_BOOKMARK_BAR_NEW_FOLDER: |
- stock = GTK_STOCK_DIRECTORY; |
- break; |
- |
- case IDC_BOOKMARK_BAR_OPEN_ALL: |
- stock = GTK_STOCK_OPEN; |
- break; |
- |
- default: |
- stock = NULL; |
- } |
- |
- return stock ? gtk_image_new_from_stock(stock, GTK_ICON_SIZE_MENU) : NULL; |
-} |
- |
-GtkWidget* MenuGtk::Delegate::GetImageForCommandId(int command_id) const { |
- return GetDefaultImageForCommandId(command_id); |
-} |
- |
-MenuGtk::MenuGtk(MenuGtk::Delegate* delegate, |
- ui::MenuModel* model) |
- : delegate_(delegate), |
- model_(model), |
- dummy_accel_group_(gtk_accel_group_new()), |
- menu_(gtk_custom_menu_new()), |
- weak_factory_(this) { |
- DCHECK(model); |
- g_object_ref_sink(menu_); |
- ConnectSignalHandlers(); |
- BuildMenuFromModel(); |
-} |
- |
-MenuGtk::~MenuGtk() { |
- Cancel(); |
- |
- gtk_widget_destroy(menu_); |
- g_object_unref(menu_); |
- |
- g_object_unref(dummy_accel_group_); |
-} |
- |
-void MenuGtk::ConnectSignalHandlers() { |
- // We connect afterwards because OnMenuShow calls SetMenuItemInfo, which may |
- // take a long time or even start a nested message loop. |
- g_signal_connect(menu_, "show", G_CALLBACK(OnMenuShowThunk), this); |
- g_signal_connect(menu_, "hide", G_CALLBACK(OnMenuHiddenThunk), this); |
- GtkWidget* toplevel_window = gtk_widget_get_toplevel(menu_); |
- signal_.Connect(toplevel_window, "focus-out-event", |
- G_CALLBACK(OnMenuFocusOutThunk), this); |
-} |
- |
-GtkWidget* MenuGtk::AppendMenuItemWithLabel(int command_id, |
- const std::string& label) { |
- std::string converted_label = ui::ConvertAcceleratorsFromWindowsStyle(label); |
- GtkWidget* menu_item = BuildMenuItemWithLabel(converted_label, command_id); |
- return AppendMenuItem(command_id, menu_item); |
-} |
- |
-GtkWidget* MenuGtk::AppendMenuItemWithIcon(int command_id, |
- const std::string& label, |
- const gfx::Image& icon) { |
- std::string converted_label = ui::ConvertAcceleratorsFromWindowsStyle(label); |
- GtkWidget* menu_item = BuildMenuItemWithImage(converted_label, icon); |
- return AppendMenuItem(command_id, menu_item); |
-} |
- |
-GtkWidget* MenuGtk::AppendCheckMenuItemWithLabel(int command_id, |
- const std::string& label) { |
- std::string converted_label = ui::ConvertAcceleratorsFromWindowsStyle(label); |
- GtkWidget* menu_item = |
- gtk_check_menu_item_new_with_mnemonic(converted_label.c_str()); |
- return AppendMenuItem(command_id, menu_item); |
-} |
- |
-GtkWidget* MenuGtk::AppendSeparator() { |
- GtkWidget* menu_item = gtk_separator_menu_item_new(); |
- gtk_widget_show(menu_item); |
- gtk_menu_shell_append(GTK_MENU_SHELL(menu_), menu_item); |
- return menu_item; |
-} |
- |
-GtkWidget* MenuGtk::InsertSeparator(int position) { |
- GtkWidget* menu_item = gtk_separator_menu_item_new(); |
- gtk_widget_show(menu_item); |
- gtk_menu_shell_insert(GTK_MENU_SHELL(menu_), menu_item, position); |
- return menu_item; |
-} |
- |
-GtkWidget* MenuGtk::AppendMenuItem(int command_id, GtkWidget* menu_item) { |
- if (delegate_ && delegate_->AlwaysShowIconForCmd(command_id) && |
- GTK_IS_IMAGE_MENU_ITEM(menu_item)) |
- gtk_util::SetAlwaysShowImage(menu_item); |
- |
- return AppendMenuItemToMenu(command_id, NULL, menu_item, menu_, true); |
-} |
- |
-GtkWidget* MenuGtk::InsertMenuItem(int command_id, GtkWidget* menu_item, |
- int position) { |
- if (delegate_ && delegate_->AlwaysShowIconForCmd(command_id) && |
- GTK_IS_IMAGE_MENU_ITEM(menu_item)) |
- gtk_util::SetAlwaysShowImage(menu_item); |
- |
- return InsertMenuItemToMenu(command_id, NULL, menu_item, menu_, position, |
- true); |
-} |
- |
-GtkWidget* MenuGtk::AppendMenuItemToMenu(int index, |
- ui::MenuModel* model, |
- GtkWidget* menu_item, |
- GtkWidget* menu, |
- bool connect_to_activate) { |
- int children_count = g_list_length(GTK_MENU_SHELL(menu)->children); |
- return InsertMenuItemToMenu(index, model, menu_item, menu, |
- children_count, connect_to_activate); |
-} |
- |
-GtkWidget* MenuGtk::InsertMenuItemToMenu(int index, |
- ui::MenuModel* model, |
- GtkWidget* menu_item, |
- GtkWidget* menu, |
- int position, |
- bool connect_to_activate) { |
- SetMenuItemID(menu_item, index); |
- |
- // Native menu items do their own thing, so only selectively listen for the |
- // activate signal. |
- if (connect_to_activate) { |
- g_signal_connect(menu_item, "activate", |
- G_CALLBACK(OnMenuItemActivatedThunk), this); |
- } |
- |
- // AppendMenuItemToMenu is used both internally when we control menu creation |
- // from a model (where the model can choose to hide certain menu items), and |
- // with immediate commands which don't provide the option. |
- if (model) { |
- if (model->IsVisibleAt(index)) |
- gtk_widget_show(menu_item); |
- } else { |
- gtk_widget_show(menu_item); |
- } |
- gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menu_item, position); |
- return menu_item; |
-} |
- |
-void MenuGtk::PopupForWidget(GtkWidget* widget, int button, |
- guint32 event_time) { |
- gtk_menu_popup(GTK_MENU(menu_), NULL, NULL, |
- WidgetMenuPositionFunc, |
- widget, |
- button, event_time); |
-} |
- |
-void MenuGtk::PopupAsContext(const gfx::Point& point, guint32 event_time) { |
- // gtk_menu_popup doesn't like the "const" qualifier on point. |
- gfx::Point nonconst_point(point); |
- gtk_menu_popup(GTK_MENU(menu_), NULL, NULL, |
- PointMenuPositionFunc, &nonconst_point, |
- 3, event_time); |
-} |
- |
-void MenuGtk::PopupAsContextForStatusIcon(guint32 event_time, guint32 button, |
- GtkStatusIcon* icon) { |
- gtk_menu_popup(GTK_MENU(menu_), NULL, NULL, gtk_status_icon_position_menu, |
- icon, button, event_time); |
-} |
- |
-void MenuGtk::PopupAsFromKeyEvent(GtkWidget* widget) { |
- PopupForWidget(widget, 0, gtk_get_current_event_time()); |
- gtk_menu_shell_select_first(GTK_MENU_SHELL(menu_), FALSE); |
-} |
- |
-void MenuGtk::Cancel() { |
- gtk_menu_popdown(GTK_MENU(menu_)); |
-} |
- |
-void MenuGtk::UpdateMenu() { |
- gtk_container_foreach(GTK_CONTAINER(menu_), SetMenuItemInfo, this); |
-} |
- |
-GtkWidget* MenuGtk::BuildMenuItemWithImage(const std::string& label, |
- GtkWidget* image) { |
- GtkWidget* menu_item = |
- gtk_image_menu_item_new_with_mnemonic(label.c_str()); |
- gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image); |
- return menu_item; |
-} |
- |
-GtkWidget* MenuGtk::BuildMenuItemWithImage(const std::string& label, |
- const gfx::Image& icon) { |
- GtkWidget* menu_item = BuildMenuItemWithImage(label, |
- gtk_image_new_from_pixbuf(icon.ToGdkPixbuf())); |
- return menu_item; |
-} |
- |
-GtkWidget* MenuGtk::BuildMenuItemWithLabel(const std::string& label, |
- int command_id) { |
- GtkWidget* img = |
- delegate_ ? delegate_->GetImageForCommandId(command_id) : |
- MenuGtk::Delegate::GetDefaultImageForCommandId(command_id); |
- return img ? BuildMenuItemWithImage(label, img) : |
- gtk_menu_item_new_with_mnemonic(label.c_str()); |
-} |
- |
-void MenuGtk::BuildMenuFromModel() { |
- BuildSubmenuFromModel(model_, menu_); |
-} |
- |
-void MenuGtk::BuildSubmenuFromModel(ui::MenuModel* model, GtkWidget* menu) { |
- std::map<int, GtkWidget*> radio_groups; |
- GtkWidget* menu_item = NULL; |
- for (int i = 0; i < model->GetItemCount(); ++i) { |
- gfx::Image icon; |
- std::string label = ui::ConvertAcceleratorsFromWindowsStyle( |
- base::UTF16ToUTF8(model->GetLabelAt(i))); |
- bool connect_to_activate = true; |
- |
- switch (model->GetTypeAt(i)) { |
- case ui::MenuModel::TYPE_SEPARATOR: |
- menu_item = gtk_separator_menu_item_new(); |
- break; |
- |
- case ui::MenuModel::TYPE_CHECK: |
- menu_item = gtk_check_menu_item_new_with_mnemonic(label.c_str()); |
- break; |
- |
- case ui::MenuModel::TYPE_RADIO: { |
- std::map<int, GtkWidget*>::iterator iter = |
- radio_groups.find(model->GetGroupIdAt(i)); |
- |
- if (iter == radio_groups.end()) { |
- menu_item = gtk_radio_menu_item_new_with_mnemonic( |
- NULL, label.c_str()); |
- radio_groups[model->GetGroupIdAt(i)] = menu_item; |
- } else { |
- menu_item = gtk_radio_menu_item_new_with_mnemonic_from_widget( |
- GTK_RADIO_MENU_ITEM(iter->second), label.c_str()); |
- } |
- break; |
- } |
- case ui::MenuModel::TYPE_BUTTON_ITEM: { |
- ui::ButtonMenuItemModel* button_menu_item_model = |
- model->GetButtonMenuItemAt(i); |
- menu_item = BuildButtonMenuItem(button_menu_item_model, menu); |
- connect_to_activate = false; |
- break; |
- } |
- case ui::MenuModel::TYPE_SUBMENU: |
- case ui::MenuModel::TYPE_COMMAND: { |
- int command_id = model->GetCommandIdAt(i); |
- if (model->GetIconAt(i, &icon)) |
- menu_item = BuildMenuItemWithImage(label, icon); |
- else |
- menu_item = BuildMenuItemWithLabel(label, command_id); |
- if (delegate_ && delegate_->AlwaysShowIconForCmd(command_id) && |
- GTK_IS_IMAGE_MENU_ITEM(menu_item)) { |
- gtk_util::SetAlwaysShowImage(menu_item); |
- } |
- break; |
- } |
- |
- default: |
- NOTREACHED(); |
- } |
- |
- if (model->GetTypeAt(i) == ui::MenuModel::TYPE_SUBMENU) { |
- GtkWidget* submenu = gtk_menu_new(); |
- g_object_set_data(G_OBJECT(submenu), "menu-item", menu_item); |
- ui::MenuModel* submenu_model = model->GetSubmenuModelAt(i); |
- g_object_set_data(G_OBJECT(menu_item), "submenu-model", submenu_model); |
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu); |
- // We will populate the submenu on demand when shown. |
- g_signal_connect(submenu, "show", G_CALLBACK(OnSubMenuShowThunk), this); |
- g_signal_connect(submenu, "hide", G_CALLBACK(OnSubMenuHiddenThunk), this); |
- connect_to_activate = false; |
- } |
- |
- ui::Accelerator accelerator; |
- if (model->GetAcceleratorAt(i, &accelerator)) { |
- gtk_widget_add_accelerator(menu_item, |
- "activate", |
- dummy_accel_group_, |
- ui::GetGdkKeyCodeForAccelerator(accelerator), |
- ui::GetGdkModifierForAccelerator(accelerator), |
- GTK_ACCEL_VISIBLE); |
- } |
- |
- g_object_set_data(G_OBJECT(menu_item), "model", model); |
- AppendMenuItemToMenu(i, model, menu_item, menu, connect_to_activate); |
- |
- menu_item = NULL; |
- } |
-} |
- |
-GtkWidget* MenuGtk::BuildButtonMenuItem(ui::ButtonMenuItemModel* model, |
- GtkWidget* menu) { |
- GtkWidget* menu_item = gtk_custom_menu_item_new( |
- ui::RemoveWindowsStyleAccelerators( |
- base::UTF16ToUTF8(model->label())).c_str()); |
- |
- // Set up the callback to the model for when it is clicked. |
- g_object_set_data(G_OBJECT(menu_item), "button-model", model); |
- g_signal_connect(menu_item, "button-pushed", |
- G_CALLBACK(OnMenuButtonPressedThunk), this); |
- g_signal_connect(menu_item, "try-button-pushed", |
- G_CALLBACK(OnMenuTryButtonPressedThunk), this); |
- |
- GtkSizeGroup* group = NULL; |
- for (int i = 0; i < model->GetItemCount(); ++i) { |
- GtkWidget* button = NULL; |
- |
- switch (model->GetTypeAt(i)) { |
- case ui::ButtonMenuItemModel::TYPE_SPACE: { |
- gtk_custom_menu_item_add_space(GTK_CUSTOM_MENU_ITEM(menu_item)); |
- break; |
- } |
- case ui::ButtonMenuItemModel::TYPE_BUTTON: { |
- button = gtk_custom_menu_item_add_button( |
- GTK_CUSTOM_MENU_ITEM(menu_item), |
- model->GetCommandIdAt(i)); |
- |
- int icon_idr; |
- if (model->GetIconAt(i, &icon_idr)) { |
- SetupImageIcon(button, menu, icon_idr, delegate_); |
- } else { |
- gtk_button_set_label( |
- GTK_BUTTON(button), |
- ui::RemoveWindowsStyleAccelerators( |
- base::UTF16ToUTF8(model->GetLabelAt(i))).c_str()); |
- } |
- |
- SetUpButtonShowHandler(button, model, i); |
- break; |
- } |
- case ui::ButtonMenuItemModel::TYPE_BUTTON_LABEL: { |
- button = gtk_custom_menu_item_add_button_label( |
- GTK_CUSTOM_MENU_ITEM(menu_item), |
- model->GetCommandIdAt(i)); |
- gtk_button_set_label( |
- GTK_BUTTON(button), |
- ui::RemoveWindowsStyleAccelerators( |
- base::UTF16ToUTF8(model->GetLabelAt(i))).c_str()); |
- SetUpButtonShowHandler(button, model, i); |
- break; |
- } |
- } |
- |
- if (button && model->PartOfGroup(i)) { |
- if (!group) |
- group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); |
- |
- gtk_size_group_add_widget(group, button); |
- } |
- } |
- |
- if (group) |
- g_object_unref(group); |
- |
- return menu_item; |
-} |
- |
-void MenuGtk::OnMenuItemActivated(GtkWidget* menu_item) { |
- if (block_activation_) |
- return; |
- |
- ui::MenuModel* model = ModelForMenuItem(GTK_MENU_ITEM(menu_item)); |
- |
- if (!model) { |
- // There won't be a model for "native" submenus like the "Input Methods" |
- // context menu. We don't need to handle activation messages for submenus |
- // anyway, so we can just return here. |
- DCHECK(gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item))); |
- return; |
- } |
- |
- // The activate signal is sent to radio items as they get deselected; |
- // ignore it in this case. |
- if (GTK_IS_RADIO_MENU_ITEM(menu_item) && |
- !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_item))) { |
- return; |
- } |
- |
- int id; |
- if (!GetMenuItemID(menu_item, &id)) |
- return; |
- |
- // The menu item can still be activated by hotkeys even if it is disabled. |
- if (model->IsEnabledAt(id)) |
- ExecuteCommand(model, id); |
-} |
- |
-void MenuGtk::OnMenuButtonPressed(GtkWidget* menu_item, int command_id) { |
- ui::ButtonMenuItemModel* model = |
- reinterpret_cast<ui::ButtonMenuItemModel*>( |
- g_object_get_data(G_OBJECT(menu_item), "button-model")); |
- if (model && model->IsCommandIdEnabled(command_id)) { |
- if (delegate_) |
- delegate_->CommandWillBeExecuted(); |
- |
- model->ActivatedCommand(command_id); |
- } |
-} |
- |
-gboolean MenuGtk::OnMenuTryButtonPressed(GtkWidget* menu_item, |
- int command_id) { |
- gboolean pressed = FALSE; |
- ui::ButtonMenuItemModel* model = |
- reinterpret_cast<ui::ButtonMenuItemModel*>( |
- g_object_get_data(G_OBJECT(menu_item), "button-model")); |
- if (model && |
- model->IsCommandIdEnabled(command_id) && |
- !model->DoesCommandIdDismissMenu(command_id)) { |
- if (delegate_) |
- delegate_->CommandWillBeExecuted(); |
- |
- model->ActivatedCommand(command_id); |
- pressed = TRUE; |
- } |
- |
- return pressed; |
-} |
- |
-// static |
-void MenuGtk::WidgetMenuPositionFunc(GtkMenu* menu, |
- int* x, |
- int* y, |
- gboolean* push_in, |
- void* void_widget) { |
- GtkWidget* widget = GTK_WIDGET(void_widget); |
- GtkRequisition menu_req; |
- |
- gtk_widget_size_request(GTK_WIDGET(menu), &menu_req); |
- |
- gdk_window_get_origin(gtk_widget_get_window(widget), x, y); |
- GdkScreen *screen = gtk_widget_get_screen(widget); |
- gint monitor = gdk_screen_get_monitor_at_point(screen, *x, *y); |
- |
- GdkRectangle screen_rect; |
- gdk_screen_get_monitor_geometry(screen, monitor, |
- &screen_rect); |
- |
- GtkAllocation allocation; |
- gtk_widget_get_allocation(widget, &allocation); |
- |
- if (!gtk_widget_get_has_window(widget)) { |
- *x += allocation.x; |
- *y += allocation.y; |
- } |
- *y += allocation.height; |
- |
- bool start_align = |
- !!g_object_get_data(G_OBJECT(widget), "left-align-popup"); |
- if (base::i18n::IsRTL()) |
- start_align = !start_align; |
- |
- if (!start_align) |
- *x += allocation.width - menu_req.width; |
- |
- *y = CalculateMenuYPosition(&screen_rect, &menu_req, widget, *y); |
- |
- *push_in = FALSE; |
-} |
- |
-// static |
-void MenuGtk::PointMenuPositionFunc(GtkMenu* menu, |
- int* x, |
- int* y, |
- gboolean* push_in, |
- gpointer userdata) { |
- *push_in = TRUE; |
- |
- gfx::Point* point = reinterpret_cast<gfx::Point*>(userdata); |
- *x = point->x(); |
- *y = point->y(); |
- |
- GtkRequisition menu_req; |
- gtk_widget_size_request(GTK_WIDGET(menu), &menu_req); |
- GdkScreen* screen; |
- gdk_display_get_pointer(gdk_display_get_default(), &screen, NULL, NULL, NULL); |
- gint monitor = gdk_screen_get_monitor_at_point(screen, *x, *y); |
- |
- GdkRectangle screen_rect; |
- gdk_screen_get_monitor_geometry(screen, monitor, &screen_rect); |
- |
- *y = CalculateMenuYPosition(&screen_rect, &menu_req, NULL, *y); |
-} |
- |
-void MenuGtk::ExecuteCommand(ui::MenuModel* model, int id) { |
- if (delegate_) |
- delegate_->CommandWillBeExecuted(); |
- |
- GdkEvent* event = gtk_get_current_event(); |
- int event_flags = 0; |
- |
- if (event && event->type == GDK_BUTTON_RELEASE) |
- event_flags = event_utils::EventFlagsFromGdkState(event->button.state); |
- model->ActivatedAt(id, event_flags); |
- |
- if (event) |
- gdk_event_free(event); |
-} |
- |
-void MenuGtk::OnMenuShow(GtkWidget* widget) { |
- model_->MenuWillShow(); |
- base::MessageLoop::current()->PostTask( |
- FROM_HERE, base::Bind(&MenuGtk::UpdateMenu, weak_factory_.GetWeakPtr())); |
-} |
- |
-void MenuGtk::OnMenuHidden(GtkWidget* widget) { |
- if (delegate_) |
- delegate_->StoppedShowing(); |
- model_->MenuClosed(); |
-} |
- |
-gboolean MenuGtk::OnMenuFocusOut(GtkWidget* widget, GdkEventFocus* event) { |
- gtk_widget_hide(menu_); |
- return TRUE; |
-} |
- |
-void MenuGtk::OnSubMenuShow(GtkWidget* submenu) { |
- GtkWidget* menu_item = static_cast<GtkWidget*>( |
- g_object_get_data(G_OBJECT(submenu), "menu-item")); |
- // TODO(mdm): Figure out why this can sometimes be NULL. See bug 131974. |
- CHECK(menu_item); |
- // Notify the submenu model that the menu will be shown. |
- ui::MenuModel* submenu_model = static_cast<ui::MenuModel*>( |
- g_object_get_data(G_OBJECT(menu_item), "submenu-model")); |
- // We're extra cautious here, and bail out if the submenu model is NULL. In |
- // some cases we clear it out from a parent menu; we shouldn't ever show the |
- // menu after that, but we play it safe since we're dealing with wacky |
- // injected libraries that toy with our menus. (See comments below.) |
- if (!submenu_model) |
- return; |
- |
- // If the submenu is already built, then return right away. This means we |
- // recently showed this submenu, and have not yet processed the fact that it |
- // was hidden before being shown again. |
- if (g_object_get_data(G_OBJECT(submenu), "submenu-built")) |
- return; |
- g_object_set_data(G_OBJECT(submenu), "submenu-built", GINT_TO_POINTER(1)); |
- |
- submenu_model->MenuWillShow(); |
- |
- // Actually build the submenu and attach it to the parent menu item. |
- BuildSubmenuFromModel(submenu_model, submenu); |
- gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu); |
- |
- // Update all the menu item info in the newly-generated menu. |
- gtk_container_foreach(GTK_CONTAINER(submenu), SetMenuItemInfo, this); |
-} |
- |
-void MenuGtk::OnSubMenuHidden(GtkWidget* submenu) { |
- // Increase the reference count of the old submenu, and schedule it to be |
- // deleted later. We get this hide notification before we've processed menu |
- // activations, so if we were to delete the submenu now, we might lose the |
- // activation. This also lets us reuse the menu if it is shown again before |
- // it gets deleted; in that case, OnSubMenuHiddenCallback() just decrements |
- // the reference count again. Note that the delay is just an optimization; we |
- // could use PostTask() and this would still work correctly. |
- g_object_ref(G_OBJECT(submenu)); |
- base::MessageLoop::current()->PostDelayedTask( |
- FROM_HERE, |
- base::Bind(&MenuGtk::OnSubMenuHiddenCallback, submenu), |
- base::TimeDelta::FromSeconds(2)); |
-} |
- |
-namespace { |
- |
-// Remove all descendant submenu-model data pointers. |
-void RemoveSubMenuModels(GtkWidget* menu_item, void* unused) { |
- if (!GTK_IS_MENU_ITEM(menu_item)) |
- return; |
- g_object_steal_data(G_OBJECT(menu_item), "submenu-model"); |
- GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item)); |
- if (submenu) |
- gtk_container_foreach(GTK_CONTAINER(submenu), RemoveSubMenuModels, NULL); |
-} |
- |
-} // namespace |
- |
-// static |
-void MenuGtk::OnSubMenuHiddenCallback(GtkWidget* submenu) { |
- if (!gtk_widget_get_visible(submenu)) { |
- // Remove all the children of this menu, clearing out their submenu-model |
- // pointers in case they have pending calls to OnSubMenuHiddenCallback(). |
- // (Normally that won't happen: we'd have hidden them first, and so they'd |
- // have already been deleted. But in some cases [e.g. on Ubuntu 12.04], |
- // GTK menu operations may be hooked to allow external applications to |
- // mirror the menu structure, and the hooks may show and hide menus in |
- // order to trigger exactly the kind of dynamic menu building we're doing. |
- // The result is that we see show and hide events in strange orders.) |
- GList* children = gtk_container_get_children(GTK_CONTAINER(submenu)); |
- for (GList* child = children; child; child = g_list_next(child)) { |
- RemoveSubMenuModels(GTK_WIDGET(child->data), NULL); |
- gtk_container_remove(GTK_CONTAINER(submenu), GTK_WIDGET(child->data)); |
- } |
- g_list_free(children); |
- |
- // Clear out the bit that says the menu is built. |
- // We'll rebuild it next time it is shown. |
- g_object_steal_data(G_OBJECT(submenu), "submenu-built"); |
- |
- // Notify the submenu model that the menu has been hidden. This may cause |
- // it to delete descendant submenu models, which is why we cleared those |
- // pointers out above. |
- GtkWidget* menu_item = static_cast<GtkWidget*>( |
- g_object_get_data(G_OBJECT(submenu), "menu-item")); |
- // TODO(mdm): Figure out why this can sometimes be NULL. See bug 124110. |
- CHECK(menu_item); |
- ui::MenuModel* submenu_model = static_cast<ui::MenuModel*>( |
- g_object_get_data(G_OBJECT(menu_item), "submenu-model")); |
- if (submenu_model) |
- submenu_model->MenuClosed(); |
- } |
- |
- // Remove the reference we grabbed in OnSubMenuHidden() above. |
- g_object_unref(G_OBJECT(submenu)); |
-} |
- |
-// static |
-void MenuGtk::SetButtonItemInfo(GtkWidget* button, gpointer userdata) { |
- ui::ButtonMenuItemModel* model = |
- reinterpret_cast<ui::ButtonMenuItemModel*>( |
- g_object_get_data(G_OBJECT(button), "button-model")); |
- int index = GPOINTER_TO_INT(g_object_get_data( |
- G_OBJECT(button), "button-model-id")); |
- |
- if (model->IsItemDynamicAt(index)) { |
- std::string label = ui::ConvertAcceleratorsFromWindowsStyle( |
- base::UTF16ToUTF8(model->GetLabelAt(index))); |
- gtk_button_set_label(GTK_BUTTON(button), label.c_str()); |
- } |
- |
- gtk_widget_set_sensitive(GTK_WIDGET(button), model->IsEnabledAt(index)); |
-} |
- |
-// static |
-void MenuGtk::SetMenuItemInfo(GtkWidget* widget, gpointer userdata) { |
- if (GTK_IS_SEPARATOR_MENU_ITEM(widget)) { |
- // We need to explicitly handle this case because otherwise we'll ask the |
- // menu delegate about something with an invalid id. |
- return; |
- } |
- |
- int id; |
- if (!GetMenuItemID(widget, &id)) |
- return; |
- |
- ui::MenuModel* model = ModelForMenuItem(GTK_MENU_ITEM(widget)); |
- if (!model) { |
- // If we're not providing the sub menu, then there's no model. For |
- // example, the IME submenu doesn't have a model. |
- return; |
- } |
- |
- if (GTK_IS_CHECK_MENU_ITEM(widget)) { |
- GtkCheckMenuItem* item = GTK_CHECK_MENU_ITEM(widget); |
- |
- // gtk_check_menu_item_set_active() will send the activate signal. Touching |
- // the underlying "active" property will also call the "activate" handler |
- // for this menu item. So we prevent the "activate" handler from |
- // being called while we set the checkbox. |
- // Why not use one of the glib signal-blocking functions? Because when we |
- // toggle a radio button, it will deactivate one of the other radio buttons, |
- // which we don't have a pointer to. |
- // Wny not make this a member variable? Because "menu" is a pointer to the |
- // root of the MenuGtk and we want to disable *all* MenuGtks, including |
- // submenus. |
- block_activation_ = true; |
- gtk_check_menu_item_set_active(item, model->IsItemCheckedAt(id)); |
- block_activation_ = false; |
- } |
- |
- if (GTK_IS_CUSTOM_MENU_ITEM(widget)) { |
- // Iterate across all the buttons to update their visible properties. |
- gtk_custom_menu_item_foreach_button(GTK_CUSTOM_MENU_ITEM(widget), |
- SetButtonItemInfo, |
- userdata); |
- } |
- |
- if (GTK_IS_MENU_ITEM(widget)) { |
- gtk_widget_set_sensitive(widget, model->IsEnabledAt(id)); |
- |
- if (model->IsVisibleAt(id)) { |
- // Update the menu item label if it is dynamic. |
- if (model->IsItemDynamicAt(id)) { |
- std::string label = ui::ConvertAcceleratorsFromWindowsStyle( |
- base::UTF16ToUTF8(model->GetLabelAt(id))); |
- |
- gtk_menu_item_set_label(GTK_MENU_ITEM(widget), label.c_str()); |
- if (GTK_IS_IMAGE_MENU_ITEM(widget)) { |
- gfx::Image icon; |
- if (model->GetIconAt(id, &icon)) { |
- gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), |
- gtk_image_new_from_pixbuf( |
- icon.ToGdkPixbuf())); |
- } else { |
- gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), NULL); |
- } |
- } |
- } |
- |
- gtk_widget_show(widget); |
- } else { |
- gtk_widget_hide(widget); |
- } |
- |
- GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(widget)); |
- if (submenu) { |
- gtk_container_foreach(GTK_CONTAINER(submenu), &SetMenuItemInfo, |
- userdata); |
- } |
- } |
-} |