Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(215)

Unified Diff: views/controls/menu/native_menu_gtk.cc

Issue 119237: A new menu system. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 11 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « views/controls/menu/native_menu_gtk.h ('k') | views/controls/menu/native_menu_win.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: views/controls/menu/native_menu_gtk.cc
===================================================================
--- views/controls/menu/native_menu_gtk.cc (revision 0)
+++ views/controls/menu/native_menu_gtk.cc (revision 0)
@@ -0,0 +1,233 @@
+// 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 "views/controls/menu/native_menu_gtk.h"
+
+#include "base/string_util.h"
+#include "base/time.h"
+#include "views/accelerator.h"
+#include "views/controls/menu/menu_2.h"
+
+namespace {
+// Data passed to the UpdateStateCallback from gtk_container_foreach.
+struct UpdateStateData {
+ // The model to retrieve state from.
+ views::Menu2Model* model;
+ // The index within said model.
+ int index;
+};
+
+// Data passed to the MenuPositionFunc from gtk_menu_popup
+struct Position {
+ // The point to run the menu at.
+ gfx::Point point;
+ // The alignment of the menu at that point.
+ views::Menu2::Alignment alignment;
+};
+
+std::string ConvertAcceleratorsFromWindowsStyle(const std::string& label) {
+ std::string ret;
+ ret.reserve(label.length());
+ for (size_t i = 0; i < label.length(); ++i) {
+ if ('&' == label[i]) {
+ if (i + 1 < label.length() && '&' == label[i + 1]) {
+ ret.push_back(label[i]);
+ ++i;
+ } else {
+ ret.push_back('_');
+ }
+ } else {
+ ret.push_back(label[i]);
+ }
+ }
+
+ return ret;
+}
+
+// Returns true if the menu item type specified can be executed as a command.
+bool MenuTypeCanExecute(views::Menu2Model::ItemType type) {
+ return type == views::Menu2Model::TYPE_COMMAND ||
+ type == views::Menu2Model::TYPE_CHECK ||
+ type == views::Menu2Model::TYPE_RADIO;
+}
+
+} // namespace
+
+namespace views {
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeMenuGtk, public:
+
+NativeMenuGtk::NativeMenuGtk(Menu2Model* model,
+ Menu2Delegate* delegate)
+ : model_(model),
+ delegate_(delegate),
+ menu_(NULL) {
+}
+
+NativeMenuGtk::~NativeMenuGtk() {
+ gtk_widget_destroy(menu_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeMenuGtk, MenuWrapper implementation:
+
+void NativeMenuGtk::RunMenuAt(const gfx::Point& point, int alignment) {
+ Position position = { point, static_cast<Menu2::Alignment>(alignment) };
+ // TODO(beng): value of '1' will not work for context menus!
+ gtk_menu_popup(GTK_MENU(menu_), NULL, NULL, MenuPositionFunc, &position, 1,
+ gtk_get_current_event_time());
+}
+
+void NativeMenuGtk::Rebuild() {
+ ResetMenu();
+
+ GtkRadioMenuItem* last_radio_item = NULL;
+ for (int i = 0; i < model_->GetItemCount(); ++i) {
+ Menu2Model::ItemType type = model_->GetTypeAt(i);
+ if (type == Menu2Model::TYPE_SEPARATOR)
+ AddSeparatorAt(i);
+ else
+ AddMenuItemAt(i, &last_radio_item);
+ }
+}
+
+void NativeMenuGtk::UpdateStates() {
+ UpdateStateData data = { model_, 0 };
+ gtk_container_foreach(GTK_CONTAINER(menu_), &UpdateStateCallback, &data);
+}
+
+gfx::NativeMenu NativeMenuGtk::GetNativeMenu() const {
+ return menu_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeMenuGtk, private:
+
+void NativeMenuGtk::AddSeparatorAt(int index) {
+ GtkWidget* separator = gtk_separator_menu_item_new();
+ gtk_widget_show(separator);
+ gtk_menu_append(menu_, separator);
+}
+
+void NativeMenuGtk::AddMenuItemAt(int index,
+ GtkRadioMenuItem** last_radio_item) {
+ GtkWidget* menu_item = NULL;
+ std::string label = ConvertAcceleratorsFromWindowsStyle(WideToUTF8(
+ model_->GetLabelAt(index)));
+
+ Menu2Model::ItemType type = model_->GetTypeAt(index);
+ switch (type) {
+ case Menu2Model::TYPE_CHECK:
+ menu_item = gtk_check_menu_item_new_with_mnemonic(label.c_str());
+ break;
+ case Menu2Model::TYPE_RADIO:
+ if (*last_radio_item) {
+ menu_item = gtk_radio_menu_item_new_with_mnemonic_from_widget(
+ *last_radio_item, label.c_str());
+ } else {
+ menu_item = gtk_radio_menu_item_new_with_mnemonic(NULL, label.c_str());
+ }
+ break;
+ case Menu2Model::TYPE_SUBMENU:
+ case Menu2Model::TYPE_COMMAND:
+ menu_item = gtk_menu_item_new_with_mnemonic(label.c_str());
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ // TODO(beng): icons
+
+ if (type == Menu2Model::TYPE_SUBMENU) {
+ // TODO(beng): we're leaking these objects right now... consider some other
+ // arrangement.
+ Menu2* submenu = new Menu2(model_->GetSubmenuModelAt(index), delegate_);
+ g_object_set_data(G_OBJECT(menu_item), "submenu", submenu);
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item),
+ submenu->GetNativeMenu());
+ }
+
+ views::Accelerator accelerator(0, false, false, false);
+ if (model_->GetAcceleratorAt(index, &accelerator)) {
+ // TODO(beng): accelerators w/gtk_widget_add_accelerator.
+ }
+ g_object_set_data(G_OBJECT(menu_item), "position",
+ reinterpret_cast<void*>(index));
+ g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(CallActivate),
+ this);
+ gtk_widget_show(menu_item);
+ gtk_menu_append(menu_, menu_item);
+}
+
+// static
+void NativeMenuGtk::UpdateStateCallback(GtkWidget* menu_item, gpointer data) {
+ UpdateStateData* usd = reinterpret_cast<UpdateStateData*>(data);
+ gtk_widget_set_sensitive(menu_item, usd->model->IsEnabledAt(usd->index));
+ if (GTK_IS_CHECK_MENU_ITEM(menu_item)) {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_item),
+ usd->model->IsItemCheckedAt(usd->index));
+ }
+ // Recurse into submenus, too.
+ if (GTK_IS_MENU_ITEM(menu_item)) {
+ if (gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item))) {
+ Menu2* submenu =
+ reinterpret_cast<Menu2*>(g_object_get_data(G_OBJECT(menu_item),
+ "submenu"));
+ if (submenu)
+ submenu->UpdateStates();
+ }
+ }
+ ++usd->index;
+}
+
+void NativeMenuGtk::ResetMenu() {
+ if (menu_)
+ gtk_widget_destroy(menu_);
+ menu_ = gtk_menu_new();
+}
+
+// static
+void NativeMenuGtk::MenuPositionFunc(GtkMenu* menu,
+ int* x,
+ int* y,
+ gboolean* push_in,
+ void* data) {
+ Position* position = reinterpret_cast<Position*>(data);
+ // TODO(beng): RTL
+ *x = position->point.x();
+ *y = position->point.y();
+ if (position->alignment == Menu2::ALIGN_TOPRIGHT) {
+ GtkRequisition menu_req;
+ gtk_widget_size_request(GTK_WIDGET(menu), &menu_req);
+ *x -= menu_req.width;
+ }
+ *push_in = FALSE;
+}
+
+void NativeMenuGtk::OnActivate(GtkMenuItem* menu_item) {
+ int position = reinterpret_cast<int>(g_object_get_data(G_OBJECT(menu_item),
+ "position"));
+ if (model_->IsEnabledAt(position) &&
+ MenuTypeCanExecute(model_->GetTypeAt(position))) {
+ delegate_->ExecuteCommand(model_, model_->GetCommandIdAt(position));
+ }
+}
+
+// static
+void NativeMenuGtk::CallActivate(GtkMenuItem* menu_item,
+ NativeMenuGtk* native_menu) {
+ native_menu->OnActivate(menu_item);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// MenuWrapper, public:
+
+// static
+MenuWrapper* MenuWrapper::CreateWrapper(Menu2* menu) {
+ return new NativeMenuGtk(menu->model(), menu->delegate());
+}
+
+} // namespace views
« no previous file with comments | « views/controls/menu/native_menu_gtk.h ('k') | views/controls/menu/native_menu_win.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698