Chromium Code Reviews| Index: chrome/browser/gtk/menu_gtk.cc |
| diff --git a/chrome/browser/gtk/menu_gtk.cc b/chrome/browser/gtk/menu_gtk.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..7eaab60a460bae26a31934f9ff182cb7157610ef |
| --- /dev/null |
| +++ b/chrome/browser/gtk/menu_gtk.cc |
| @@ -0,0 +1,151 @@ |
| +// Copyright (c) 2009 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/gtk/menu_gtk.h" |
| + |
| +#include "base/logging.h" |
| +#include "base/string_util.h" |
| +#include "chrome/common/l10n_util.h" |
| + |
| +MenuGtk::MenuGtk(MenuGtk::Delegate* delegate, |
| + const MenuCreateMaterial* menu_data) |
| + : delegate_(delegate), |
| + menu_(gtk_menu_new()) { |
| + BuildMenuIn(menu_, menu_data); |
| +} |
| + |
| +MenuGtk::~MenuGtk() { |
| + g_object_unref(menu_); |
| +} |
| + |
| +void MenuGtk::Popup(GtkWidget* widget, GdkEvent* event) { |
| + DCHECK(event->type == GDK_BUTTON_PRESS) |
| + << "Non-button press event sent to RunMenuAt"; |
| + |
| + gtk_container_foreach(GTK_CONTAINER(menu_), SetMenuItemInfo, this); |
| + |
| + GdkEventButton* event_button = reinterpret_cast<GdkEventButton*>(event); |
| + gtk_menu_popup(GTK_MENU(menu_), NULL, NULL, |
| + MenuPositionFunc, |
| + widget, |
| + event_button->button, event_button->time); |
| +} |
| + |
| +void MenuGtk::BuildMenuIn(GtkWidget* menu, |
| + const MenuCreateMaterial* menu_data) { |
| + for (; menu_data->type != MENU_END; menu_data++) { |
| + GtkWidget* menu_item = NULL; |
| + |
| + std::wstring label; |
| + if (menu_data->label_argument) { |
| + label = l10n_util::GetStringF( |
| + menu_data->label_id, |
| + l10n_util::GetString(menu_data->label_argument)); |
| + } else if (menu_data->label_id) { |
| + label = l10n_util::GetString(menu_data->label_id); |
| + } else { |
| + DCHECK(menu_data->type == MENU_SEPARATOR) << "Menu definition broken"; |
| + } |
| + |
| + switch (menu_data->type) { |
| + case MENU_CHECKBOX: |
| + menu_item = gtk_check_menu_item_new_with_label( |
| + WideToUTF8(label).c_str()); |
| + break; |
| + case MENU_SEPARATOR: |
| + menu_item = gtk_separator_menu_item_new(); |
| + break; |
| + case MENU_NORMAL: |
| + default: |
| + menu_item = gtk_menu_item_new_with_label(WideToUTF8(label).c_str()); |
| + break; |
| + } |
| + |
| + if (menu_data->submenu) { |
| + GtkWidget* submenu = gtk_menu_new(); |
| + BuildMenuIn(submenu, menu_data->submenu); |
| + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu); |
| + } |
| + |
| + g_object_set_data(G_OBJECT(menu_item), "menu-data", |
| + const_cast<MenuCreateMaterial*>(menu_data)); |
| + |
| + g_signal_connect(G_OBJECT(menu_item), "activate", |
| + G_CALLBACK(OnMenuItemActivated), this); |
| + |
| + gtk_widget_show(menu_item); |
| + gtk_menu_append(menu, menu_item); |
| + } |
| +} |
| + |
| +/* static */ |
| +void MenuGtk::OnMenuItemActivated(GtkMenuItem* menuitem, MenuGtk* menu) { |
| + // We receive activation messages when highlighting a menu that has a |
| + // submenu. Ignore them. |
| + if (!gtk_menu_item_get_submenu(menuitem)) { |
| + const MenuCreateMaterial* data = |
| + reinterpret_cast<const MenuCreateMaterial*>( |
| + g_object_get_data(G_OBJECT(menuitem), "menu-data")); |
| + menu->delegate_->ExecuteCommand(data->id); |
| + } |
| +} |
| + |
| +/* static */ |
|
Dean McNamee
2009/02/18 12:35:17
I've seen this in a few places now. I don't think
|
| +void MenuGtk::MenuPositionFunc(GtkMenu* menu, |
| + int* x, |
| + int* y, |
| + gboolean* push_in, |
| + void* void_widget) { |
| + GtkWidget* widget = GTK_WIDGET(void_widget); |
| + GtkRequisition menu_req; |
| + GdkRectangle monitor; |
| + |
| + gtk_widget_size_request(GTK_WIDGET(menu), &menu_req); |
| + |
| + GdkScreen* screen = gtk_widget_get_screen(GTK_WIDGET(menu)); |
| + gint monitor_num = gdk_screen_get_monitor_at_window(screen, widget->window); |
| + if (monitor_num < 0) |
| + monitor_num = 0; |
| + gdk_screen_get_monitor_geometry(screen, monitor_num, &monitor); |
| + |
| + gdk_window_get_origin(widget->window, x, y); |
| + *x += widget->allocation.x + widget->allocation.width; |
| + *y += widget->allocation.y + widget->allocation.height; |
| + |
| + *x -= menu_req.width; |
| + |
| + // TODO(erg): Deal with this scrolling off the bottom of the screen. |
| + |
| + // Regretfully, we can't rely on push_in to alter the coordinates above to |
| + // always make the menu fit on screen. It'd make the above calculations just |
| + // work though... |
| + *push_in = FALSE; |
| +} |
| + |
| +/* static */ |
| +void MenuGtk::SetMenuItemInfo(GtkWidget* widget, void* raw_menu) { |
| + MenuGtk* menu = static_cast<MenuGtk*>(raw_menu); |
| + const MenuCreateMaterial* data = |
| + reinterpret_cast<const MenuCreateMaterial*>( |
| + g_object_get_data(G_OBJECT(widget), "menu-data")); |
| + |
| + if (data) { |
| + if (GTK_IS_CHECK_MENU_ITEM(widget)) { |
| + GtkCheckMenuItem* item = GTK_CHECK_MENU_ITEM(widget); |
| + gtk_check_menu_item_set_active( |
| + item, menu->delegate_->IsItemChecked(data->id)); |
| + } |
| + |
| + if (GTK_IS_MENU_ITEM(widget)) { |
| + gtk_widget_set_sensitive( |
| + widget, menu->delegate_->IsCommandEnabled(data->id)); |
| + |
| + GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(widget)); |
| + if (submenu) { |
| + gtk_container_foreach(GTK_CONTAINER(submenu), &MenuGtk::SetMenuItemInfo, |
| + raw_menu); |
| + } |
| + } |
| + } |
| +} |