summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/app_base.gypi2
-rw-r--r--app/menus/button_menu_item_model.cc65
-rw-r--r--app/menus/button_menu_item_model.h103
-rw-r--r--app/menus/menu_model.h5
-rw-r--r--app/menus/simple_menu_model.cc33
-rw-r--r--app/menus/simple_menu_model.h7
-rw-r--r--chrome/app/generated_resources.grd9
-rw-r--r--chrome/browser/back_forward_menu_model.cc5
-rw-r--r--chrome/browser/back_forward_menu_model.h1
-rw-r--r--chrome/browser/chromeos/status/clock_menu_button.h3
-rw-r--r--chrome/browser/chromeos/status/feedback_menu_button.h3
-rw-r--r--chrome/browser/chromeos/status/language_menu_button.cc5
-rw-r--r--chrome/browser/chromeos/status/language_menu_button.h1
-rw-r--r--chrome/browser/chromeos/status/network_menu_button.h3
-rw-r--r--chrome/browser/chromeos/status/power_menu_button.h3
-rw-r--r--chrome/browser/gtk/gtk_custom_menu.cc144
-rw-r--r--chrome/browser/gtk/gtk_custom_menu.h53
-rw-r--r--chrome/browser/gtk/gtk_custom_menu_item.cc282
-rw-r--r--chrome/browser/gtk/gtk_custom_menu_item.h118
-rw-r--r--chrome/browser/gtk/gtk_util.cc19
-rw-r--r--chrome/browser/gtk/gtk_util.h3
-rw-r--r--chrome/browser/gtk/menu_gtk.cc91
-rw-r--r--chrome/browser/gtk/menu_gtk.h10
-rw-r--r--chrome/browser/gtk/notifications/notification_options_menu_model.cc5
-rw-r--r--chrome/browser/gtk/notifications/notification_options_menu_model.h1
-rw-r--r--chrome/browser/wrench_menu_model.cc86
-rw-r--r--chrome/browser/wrench_menu_model.h52
-rw-r--r--chrome/chrome_browser.gypi8
28 files changed, 1088 insertions, 32 deletions
diff --git a/app/app_base.gypi b/app/app_base.gypi
index facbd4c..4cd5e66 100644
--- a/app/app_base.gypi
+++ b/app/app_base.gypi
@@ -159,6 +159,8 @@
'menus/accelerator.h',
'menus/accelerator_gtk.h',
'menus/accelerator_cocoa.h',
+ 'menus/button_menu_item_model.cc',
+ 'menus/button_menu_item_model.h',
'menus/menu_model.cc',
'menus/menu_model.h',
'menus/simple_menu_model.cc',
diff --git a/app/menus/button_menu_item_model.cc b/app/menus/button_menu_item_model.cc
new file mode 100644
index 0000000..c5064fb
--- /dev/null
+++ b/app/menus/button_menu_item_model.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2010 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 "app/menus/button_menu_item_model.h"
+
+#include "app/l10n_util.h"
+
+namespace menus {
+
+ButtonMenuItemModel::ButtonMenuItemModel(
+ int string_id,
+ ButtonMenuItemModel::Delegate* delegate)
+ : item_label_(l10n_util::GetStringUTF16(string_id)),
+ delegate_(delegate) {
+}
+
+void ButtonMenuItemModel::AddItemWithStringId(int command_id, int string_id) {
+ Item item = { command_id, TYPE_BUTTON, l10n_util::GetStringUTF16(string_id),
+ SIDE_BOTH, -1 };
+ items_.push_back(item);
+}
+
+void ButtonMenuItemModel::AddItemWithImage(int command_id,
+ int icon_idr) {
+ Item item = { command_id, TYPE_BUTTON, string16(), SIDE_BOTH, icon_idr };
+ items_.push_back(item);
+}
+
+void ButtonMenuItemModel::AddSpace() {
+ Item item = { 0, TYPE_SPACE, string16(), SIDE_NONE, -1 };
+ items_.push_back(item);
+}
+
+int ButtonMenuItemModel::GetItemCount() const {
+ return static_cast<int>(items_.size());
+}
+
+ButtonMenuItemModel::ButtonType ButtonMenuItemModel::GetTypeAt(
+ int index) const {
+ return items_[index].type;
+}
+
+int ButtonMenuItemModel::GetCommandIdAt(int index) const {
+ return items_[index].command_id;
+}
+
+const string16& ButtonMenuItemModel::GetLabelAt(int index) const {
+ return items_[index].label;
+}
+
+bool ButtonMenuItemModel::GetIconAt(int index, int* icon_idr) const {
+ if (items_[index].icon_idr == -1)
+ return false;
+
+ *icon_idr = items_[index].icon_idr;
+ return true;
+}
+
+void ButtonMenuItemModel::ActivatedCommand(int command_id) {
+ if (delegate_)
+ delegate_->ExecuteCommand(command_id);
+}
+
+} // namespace menus
diff --git a/app/menus/button_menu_item_model.h b/app/menus/button_menu_item_model.h
new file mode 100644
index 0000000..b8418d1
--- /dev/null
+++ b/app/menus/button_menu_item_model.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2010 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.
+
+#ifndef APP_MENUS_BUTTON_MENU_ITEM_MODEL_H_
+#define APP_MENUS_BUTTON_MENU_ITEM_MODEL_H_
+
+#include <vector>
+
+#include "base/string16.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+namespace menus {
+
+// A model representing the rows of buttons that should be inserted in a button
+// containing menu item.
+//
+// TODO(erg): There are still two major pieces missing from this model. It
+// needs to be able to group buttons together so they all have the same
+// width. ButtonSides needs to be used to communicate how buttons are squashed
+// together.
+class ButtonMenuItemModel {
+ public:
+ // Types of buttons.
+ enum ButtonType {
+ TYPE_SPACE,
+ TYPE_BUTTON
+ };
+
+ // Which sides of the button are visible.
+ enum ButtonSides {
+ SIDE_NONE = 0,
+ SIDE_LEFT = 1 << 0,
+ SIDE_RIGHT = 1 << 1,
+ SIDE_BOTH = SIDE_LEFT | SIDE_RIGHT
+ };
+
+ class Delegate {
+ public:
+ // Some command ids have labels that change over time.
+ virtual bool IsLabelForCommandIdDynamic(int command_id) const {
+ return false;
+ }
+ virtual string16 GetLabelForCommandId(int command_id) const {
+ return string16();
+ }
+
+ // Performs the action associated with the specified command id.
+ virtual void ExecuteCommand(int command_id) = 0;
+ };
+
+ ButtonMenuItemModel(int string_id, ButtonMenuItemModel::Delegate* delegate);
+
+ // Adds a button that will emit |command_id|.
+ void AddItemWithStringId(int command_id, int string_id);
+
+ // Adds a button that has an icon instead of a label.
+ void AddItemWithImage(int command_id, int icon_idr);
+
+ // Adds a small horizontal space.
+ void AddSpace();
+
+ // Returns the number of items for iteration.
+ int GetItemCount() const;
+
+ // Returns what kind of item is at |index|.
+ ButtonType GetTypeAt(int index) const;
+
+ // Changes a position into a command ID.
+ int GetCommandIdAt(int index) const;
+
+ const string16& GetLabelAt(int index) const;
+
+ // If the button at |index| should have an icon instead, returns true and
+ // sets the IDR |icon|.
+ bool GetIconAt(int index, int* icon) const;
+
+ // Called from implementations.
+ void ActivatedCommand(int command_id);
+
+ const string16& label() const { return item_label_; }
+
+ private:
+ // The non-clickable label to the left of the buttons.
+ string16 item_label_;
+
+ struct Item {
+ int command_id;
+ ButtonType type;
+ string16 label;
+ int sides;
+ int icon_idr;
+ };
+ std::vector<Item> items_;
+
+ Delegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(ButtonMenuItemModel);
+};
+
+} // namespace menus
+
+#endif // APP_MENUS_BUTTON_MENU_ITEM_MODEL_H_
diff --git a/app/menus/menu_model.h b/app/menus/menu_model.h
index 50f2e54..f14c8b5 100644
--- a/app/menus/menu_model.h
+++ b/app/menus/menu_model.h
@@ -20,6 +20,7 @@ class Font;
namespace menus {
class Accelerator;
+class ButtonMenuItemModel;
// An interface implemented by an object that provides the content of a menu.
class MenuModel {
@@ -32,6 +33,7 @@ class MenuModel {
TYPE_CHECK,
TYPE_RADIO,
TYPE_SEPARATOR,
+ TYPE_BUTTON_ITEM,
TYPE_SUBMENU
};
@@ -85,6 +87,9 @@ class MenuModel {
// is an icon, false otherwise.
virtual bool GetIconAt(int index, SkBitmap* icon) const = 0;
+ // Returns the model for a menu item with a line of buttons at |index|.
+ virtual ButtonMenuItemModel* GetButtonMenuItemAt(int index) const = 0;
+
// Returns the enabled state of the item at the specified index.
virtual bool IsEnabledAt(int index) const = 0;
diff --git a/app/menus/simple_menu_model.cc b/app/menus/simple_menu_model.cc
index 8631666..3e51986 100644
--- a/app/menus/simple_menu_model.cc
+++ b/app/menus/simple_menu_model.cc
@@ -20,7 +20,7 @@ SimpleMenuModel::~SimpleMenuModel() {
}
void SimpleMenuModel::AddItem(int command_id, const string16& label) {
- Item item = { command_id, label, SkBitmap(), TYPE_COMMAND, -1, NULL };
+ Item item = { command_id, label, SkBitmap(), TYPE_COMMAND, -1, NULL, NULL };
AppendItem(item);
}
@@ -30,7 +30,7 @@ void SimpleMenuModel::AddItemWithStringId(int command_id, int string_id) {
void SimpleMenuModel::AddSeparator() {
Item item = { kSeparatorId, string16(), SkBitmap(), TYPE_SEPARATOR, -1,
- NULL };
+ NULL, NULL };
AppendItem(item);
}
@@ -45,7 +45,8 @@ void SimpleMenuModel::AddCheckItemWithStringId(int command_id, int string_id) {
void SimpleMenuModel::AddRadioItem(int command_id, const string16& label,
int group_id) {
- Item item = { command_id, label, SkBitmap(), TYPE_RADIO, group_id, NULL };
+ Item item = { command_id, label, SkBitmap(), TYPE_RADIO, group_id, NULL,
+ NULL };
AppendItem(item);
}
@@ -54,9 +55,15 @@ void SimpleMenuModel::AddRadioItemWithStringId(int command_id, int string_id,
AddRadioItem(command_id, l10n_util::GetStringUTF16(string_id), group_id);
}
+void SimpleMenuModel::AddButtonItem(int command_id,
+ ButtonMenuItemModel* model) {
+ Item item = { 0, string16(), SkBitmap(), TYPE_BUTTON_ITEM, -1, NULL, model };
+ AppendItem(item);
+}
+
void SimpleMenuModel::AddSubMenu(int command_id, const string16& label,
MenuModel* model) {
- Item item = { command_id, label, SkBitmap(), TYPE_SUBMENU, -1, model };
+ Item item = { command_id, label, SkBitmap(), TYPE_SUBMENU, -1, model, NULL };
AppendItem(item);
}
@@ -67,7 +74,7 @@ void SimpleMenuModel::AddSubMenuWithStringId(int command_id,
void SimpleMenuModel::InsertItemAt(
int index, int command_id, const string16& label) {
- Item item = { command_id, label, SkBitmap(), TYPE_COMMAND, -1, NULL };
+ Item item = { command_id, label, SkBitmap(), TYPE_COMMAND, -1, NULL, NULL };
InsertItemAtIndex(item, index);
}
@@ -78,13 +85,13 @@ void SimpleMenuModel::InsertItemWithStringIdAt(
void SimpleMenuModel::InsertSeparatorAt(int index) {
Item item = { kSeparatorId, string16(), SkBitmap(), TYPE_SEPARATOR, -1,
- NULL };
+ NULL, NULL };
InsertItemAtIndex(item, index);
}
void SimpleMenuModel::InsertCheckItemAt(
int index, int command_id, const string16& label) {
- Item item = { command_id, label, SkBitmap(), TYPE_CHECK, -1, NULL };
+ Item item = { command_id, label, SkBitmap(), TYPE_CHECK, -1, NULL, NULL };
InsertItemAtIndex(item, index);
}
@@ -96,7 +103,8 @@ void SimpleMenuModel::InsertCheckItemWithStringIdAt(
void SimpleMenuModel::InsertRadioItemAt(
int index, int command_id, const string16& label, int group_id) {
- Item item = { command_id, label, SkBitmap(), TYPE_RADIO, group_id, NULL };
+ Item item = { command_id, label, SkBitmap(), TYPE_RADIO, group_id, NULL,
+ NULL };
InsertItemAtIndex(item, index);
}
@@ -108,7 +116,7 @@ void SimpleMenuModel::InsertRadioItemWithStringIdAt(
void SimpleMenuModel::InsertSubMenuAt(
int index, int command_id, const string16& label, MenuModel* model) {
- Item item = { command_id, label, SkBitmap(), TYPE_SUBMENU, -1, model };
+ Item item = { command_id, label, SkBitmap(), TYPE_SUBMENU, -1, model, NULL };
InsertItemAtIndex(item, index);
}
@@ -199,9 +207,14 @@ bool SimpleMenuModel::GetIconAt(int index, SkBitmap* icon) const {
return true;
}
+ButtonMenuItemModel* SimpleMenuModel::GetButtonMenuItemAt(int index) const {
+ return items_.at(FlipIndex(index)).button_model;
+}
+
bool SimpleMenuModel::IsEnabledAt(int index) const {
int command_id = GetCommandIdAt(index);
- if (!delegate_ || command_id == kSeparatorId)
+ if (!delegate_ || command_id == kSeparatorId ||
+ items_.at(FlipIndex(index)).button_model)
return true;
return delegate_->IsCommandIdEnabled(command_id);
}
diff --git a/app/menus/simple_menu_model.h b/app/menus/simple_menu_model.h
index 8dd6c64..b6ae852 100644
--- a/app/menus/simple_menu_model.h
+++ b/app/menus/simple_menu_model.h
@@ -8,6 +8,7 @@
#include <vector>
#include "base/string16.h"
+#include "app/menus/button_menu_item_model.h"
#include "app/menus/menu_model.h"
#include "third_party/skia/include/core/SkBitmap.h"
@@ -60,6 +61,10 @@ class SimpleMenuModel : public MenuModel {
void AddCheckItemWithStringId(int command_id, int string_id);
void AddRadioItem(int command_id, const string16& label, int group_id);
void AddRadioItemWithStringId(int command_id, int string_id, int group_id);
+
+ // These three methods take pointers to various sub-models. These models
+ // should be owned by the same owner of this SimpleMenuModel.
+ void AddButtonItem(int command_id, ButtonMenuItemModel* model);
void AddSubMenu(int command_id, const string16& label, MenuModel* model);
void AddSubMenuWithStringId(int command_id, int string_id, MenuModel* model);
@@ -102,6 +107,7 @@ class SimpleMenuModel : public MenuModel {
virtual bool IsItemCheckedAt(int index) const;
virtual int GetGroupIdAt(int index) const;
virtual bool GetIconAt(int index, SkBitmap* icon) const;
+ virtual menus::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const;
virtual bool IsEnabledAt(int index) const;
virtual void HighlightChangedTo(int index);
virtual void ActivatedAt(int index);
@@ -125,6 +131,7 @@ class SimpleMenuModel : public MenuModel {
ItemType type;
int group_id;
MenuModel* submenu;
+ ButtonMenuItemModel* button_model;
};
std::vector<Item> items_;
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 5f69015..450a158 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -822,12 +822,21 @@ each locale. -->
<message name="IDS_ZOOM_PLUS" desc="In Title Case: The text label of the Make Text Larger menu item">
&amp;Larger
</message>
+ <message name="IDS_ZOOM_PLUS2" desc="The text label of the Make Text Larger menu item in the merged menu">
+ +
+ </message>
<message name="IDS_ZOOM_NORMAL" desc="In Title Case: The text label of the Make Text Normal Size menu item">
&amp;Normal
</message>
<message name="IDS_ZOOM_MINUS" desc="In Title Case: The text label of the Make Text Smaller menu item">
&amp;Smaller
</message>
+ <message name="IDS_ZOOM_MINUS2" desc="The text label of the Make Text Smaller menu item in the merged menu">
+ -
+ </message>
+ <message name="IDS_ZOOM_PERCENT" desc="Current pages zoom factor; shown in merged menu">
+ <ph name="VALUE">$1<ex>100</ex></ph>%
+ </message>
<message name="IDS_ENCODING_MENU" desc="In Title Case: The text label of the Encoding submenu">
&amp;Encoding
</message>
diff --git a/chrome/browser/back_forward_menu_model.cc b/chrome/browser/back_forward_menu_model.cc
index e1bc742..5a164e7 100644
--- a/chrome/browser/back_forward_menu_model.cc
+++ b/chrome/browser/back_forward_menu_model.cc
@@ -115,6 +115,11 @@ bool BackForwardMenuModel::GetIconAt(int index, SkBitmap* icon) const {
return true;
}
+menus::ButtonMenuItemModel* BackForwardMenuModel::GetButtonMenuItemAt(
+ int index) const {
+ return NULL;
+}
+
bool BackForwardMenuModel::IsEnabledAt(int index) const {
return index < GetItemCount() && !IsSeparator(index);
}
diff --git a/chrome/browser/back_forward_menu_model.h b/chrome/browser/back_forward_menu_model.h
index 802c6a6..2315a41 100644
--- a/chrome/browser/back_forward_menu_model.h
+++ b/chrome/browser/back_forward_menu_model.h
@@ -53,6 +53,7 @@ class BackForwardMenuModel : public menus::MenuModel {
virtual bool IsItemCheckedAt(int index) const;
virtual int GetGroupIdAt(int index) const;
virtual bool GetIconAt(int index, SkBitmap* icon) const;
+ virtual menus::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const;
virtual bool IsEnabledAt(int index) const;
virtual MenuModel* GetSubmenuModelAt(int index) const;
virtual void HighlightChangedTo(int index);
diff --git a/chrome/browser/chromeos/status/clock_menu_button.h b/chrome/browser/chromeos/status/clock_menu_button.h
index 07287f6..5457bec 100644
--- a/chrome/browser/chromeos/status/clock_menu_button.h
+++ b/chrome/browser/chromeos/status/clock_menu_button.h
@@ -41,6 +41,9 @@ class ClockMenuButton : public views::MenuButton,
virtual bool IsItemCheckedAt(int index) const { return false; }
virtual int GetGroupIdAt(int index) const { return 0; }
virtual bool GetIconAt(int index, SkBitmap* icon) const { return false; }
+ virtual menus::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const {
+ return NULL;
+ }
virtual bool IsEnabledAt(int index) const;
virtual menus::MenuModel* GetSubmenuModelAt(int index) const { return NULL; }
virtual void HighlightChangedTo(int index) {}
diff --git a/chrome/browser/chromeos/status/feedback_menu_button.h b/chrome/browser/chromeos/status/feedback_menu_button.h
index bf7bb49..1b6b1df 100644
--- a/chrome/browser/chromeos/status/feedback_menu_button.h
+++ b/chrome/browser/chromeos/status/feedback_menu_button.h
@@ -47,6 +47,9 @@ class FeedbackMenuButton : public StatusAreaButton,
virtual bool IsItemCheckedAt(int index) const { return false; }
virtual int GetGroupIdAt(int index) const { return 0; }
virtual bool GetIconAt(int index, SkBitmap* icon) const { return false; }
+ virtual menus::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const {
+ return NULL;
+ }
virtual bool IsEnabledAt(int index) const { return false; }
virtual menus::MenuModel* GetSubmenuModelAt(int index) const { return NULL; }
virtual void HighlightChangedTo(int index) {}
diff --git a/chrome/browser/chromeos/status/language_menu_button.cc b/chrome/browser/chromeos/status/language_menu_button.cc
index a4b7f49..c13ea7b 100644
--- a/chrome/browser/chromeos/status/language_menu_button.cc
+++ b/chrome/browser/chromeos/status/language_menu_button.cc
@@ -239,6 +239,11 @@ bool LanguageMenuButton::GetIconAt(int index, SkBitmap* icon) const {
return false;
}
+menus::ButtonMenuItemModel* LanguageMenuButton::GetButtonMenuItemAt(
+ int index) const {
+ return NULL;
+}
+
bool LanguageMenuButton::IsEnabledAt(int index) const {
// Just return true so all input method names and input method propertie names
// could be clicked.
diff --git a/chrome/browser/chromeos/status/language_menu_button.h b/chrome/browser/chromeos/status/language_menu_button.h
index a029b9d..b0483ce 100644
--- a/chrome/browser/chromeos/status/language_menu_button.h
+++ b/chrome/browser/chromeos/status/language_menu_button.h
@@ -43,6 +43,7 @@ class LanguageMenuButton : public views::MenuButton,
virtual bool IsItemCheckedAt(int index) const;
virtual int GetGroupIdAt(int index) const;
virtual bool GetIconAt(int index, SkBitmap* icon) const;
+ virtual menus::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const;
virtual bool IsEnabledAt(int index) const;
virtual menus::MenuModel* GetSubmenuModelAt(int index) const;
virtual void HighlightChangedTo(int index);
diff --git a/chrome/browser/chromeos/status/network_menu_button.h b/chrome/browser/chromeos/status/network_menu_button.h
index aab1c05..c38c35e 100644
--- a/chrome/browser/chromeos/status/network_menu_button.h
+++ b/chrome/browser/chromeos/status/network_menu_button.h
@@ -71,6 +71,9 @@ class NetworkMenuButton : public StatusAreaButton,
virtual bool IsItemCheckedAt(int index) const;
virtual int GetGroupIdAt(int index) const { return 0; }
virtual bool GetIconAt(int index, SkBitmap* icon) const;
+ virtual menus::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const {
+ return NULL;
+ }
virtual bool IsEnabledAt(int index) const;
virtual menus::MenuModel* GetSubmenuModelAt(int index) const { return NULL; }
virtual void HighlightChangedTo(int index) {}
diff --git a/chrome/browser/chromeos/status/power_menu_button.h b/chrome/browser/chromeos/status/power_menu_button.h
index 15601bc..413b744 100644
--- a/chrome/browser/chromeos/status/power_menu_button.h
+++ b/chrome/browser/chromeos/status/power_menu_button.h
@@ -37,6 +37,9 @@ class PowerMenuButton : public StatusAreaButton,
virtual bool IsItemCheckedAt(int index) const { return false; }
virtual int GetGroupIdAt(int index) const { return 0; }
virtual bool GetIconAt(int index, SkBitmap* icon) const { return false; }
+ virtual menus::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const {
+ return NULL;
+ }
virtual bool IsEnabledAt(int index) const { return false; }
virtual menus::MenuModel* GetSubmenuModelAt(int index) const { return NULL; }
virtual void HighlightChangedTo(int index) {}
diff --git a/chrome/browser/gtk/gtk_custom_menu.cc b/chrome/browser/gtk/gtk_custom_menu.cc
new file mode 100644
index 0000000..d666091
--- /dev/null
+++ b/chrome/browser/gtk/gtk_custom_menu.cc
@@ -0,0 +1,144 @@
+// Copyright (c) 2010 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/gtk_custom_menu.h"
+
+#include "chrome/browser/gtk/gtk_custom_menu_item.h"
+
+G_DEFINE_TYPE(GtkCustomMenu, gtk_custom_menu, GTK_TYPE_MENU)
+
+// Stolen directly from gtkmenushell.c. I'd love to call the library version
+// instead, but it's static and isn't exported. :(
+static gint gtk_menu_shell_is_item(GtkMenuShell* menu_shell,
+ GtkWidget* child) {
+ GtkWidget *parent;
+
+ g_return_val_if_fail(GTK_IS_MENU_SHELL(menu_shell), FALSE);
+ g_return_val_if_fail(child != NULL, FALSE);
+
+ parent = child->parent;
+ while (GTK_IS_MENU_SHELL(parent)) {
+ if (parent == reinterpret_cast<GtkWidget*>(menu_shell))
+ return TRUE;
+ parent = GTK_MENU_SHELL(parent)->parent_menu_shell;
+ }
+
+ return FALSE;
+}
+
+// Stolen directly from gtkmenushell.c. I'd love to call the library version
+// instead, but it's static and isn't exported. :(
+static GtkWidget* gtk_menu_shell_get_item(GtkMenuShell* menu_shell,
+ GdkEvent* event) {
+ GtkWidget* menu_item = gtk_get_event_widget(event);
+
+ while (menu_item && !GTK_IS_MENU_ITEM(menu_item))
+ menu_item = menu_item->parent;
+
+ if (menu_item && gtk_menu_shell_is_item(menu_shell, menu_item))
+ return menu_item;
+ else
+ return NULL;
+}
+
+// When processing a button event, abort processing if the cursor isn't in a
+// clickable region.
+static gboolean gtk_custom_menu_button_press(GtkWidget* widget,
+ GdkEventButton* event) {
+ GtkWidget* menu_item = gtk_menu_shell_get_item(
+ GTK_MENU_SHELL(widget), reinterpret_cast<GdkEvent*>(event));
+ if (GTK_IS_CUSTOM_MENU_ITEM(menu_item)) {
+ if (!gtk_custom_menu_item_is_in_clickable_region(
+ GTK_CUSTOM_MENU_ITEM(menu_item))) {
+ return TRUE;
+ }
+ }
+
+ return GTK_WIDGET_CLASS(gtk_custom_menu_parent_class)->
+ button_press_event(widget, event);
+}
+
+// When processing a button event, abort processing if the cursor isn't in a
+// clickable region.
+static gboolean gtk_custom_menu_button_release(GtkWidget* widget,
+ GdkEventButton* event) {
+ GtkWidget* menu_item = gtk_menu_shell_get_item(
+ GTK_MENU_SHELL(widget), reinterpret_cast<GdkEvent*>(event));
+ if (GTK_IS_CUSTOM_MENU_ITEM(menu_item)) {
+ if (!gtk_custom_menu_item_is_in_clickable_region(
+ GTK_CUSTOM_MENU_ITEM(menu_item))) {
+ // Stop processing this event. This isn't a clickable region.
+ return TRUE;
+ }
+ }
+
+ return GTK_WIDGET_CLASS(gtk_custom_menu_parent_class)->
+ button_release_event(widget, event);
+}
+
+// Manually forward button press events to the menu item (and then do what we'd
+// do normally).
+static gboolean gtk_custom_menu_motion_notify(GtkWidget* widget,
+ GdkEventMotion* event) {
+ GtkWidget* menu_item = gtk_menu_shell_get_item(
+ GTK_MENU_SHELL(widget), (GdkEvent*)event);
+ if (GTK_IS_CUSTOM_MENU_ITEM(menu_item)) {
+ gtk_custom_menu_item_receive_motion_event(GTK_CUSTOM_MENU_ITEM(menu_item),
+ event->x, event->y);
+ }
+
+ return GTK_WIDGET_CLASS(gtk_custom_menu_parent_class)->
+ motion_notify_event(widget, event);
+}
+
+static void gtk_custom_menu_move_current(GtkMenuShell* menu_shell,
+ GtkMenuDirectionType direction) {
+ // If the currently selected item is custom, we give it first chance to catch
+ // up/down events.
+
+ // TODO(erg): We are breaking a GSEAL by directly accessing this. We'll need
+ // to fix this by the time gtk3 comes out.
+ GtkWidget* menu_item = GTK_MENU_SHELL(menu_shell)->active_menu_item;
+ if (GTK_IS_CUSTOM_MENU_ITEM(menu_item)) {
+ switch (direction) {
+ case GTK_MENU_DIR_PREV:
+ case GTK_MENU_DIR_NEXT:
+ if (gtk_custom_menu_item_handle_move(GTK_CUSTOM_MENU_ITEM(menu_item),
+ direction))
+ return;
+ break;
+ default:
+ break;
+ }
+ }
+
+ GTK_MENU_SHELL_CLASS(gtk_custom_menu_parent_class)->
+ move_current(menu_shell, direction);
+
+ // In the case of hitting PREV and transitioning to a custom menu, we want to
+ // make sure we're selecting the final item in the list, not the first one.
+ menu_item = GTK_MENU_SHELL(menu_shell)->active_menu_item;
+ if (GTK_IS_CUSTOM_MENU_ITEM(menu_item)) {
+ gtk_custom_menu_item_select_item_by_direction(
+ GTK_CUSTOM_MENU_ITEM(menu_item), direction);
+ }
+}
+
+static void gtk_custom_menu_init(GtkCustomMenu* menu) {
+}
+
+static void gtk_custom_menu_class_init(GtkCustomMenuClass* klass) {
+ GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
+ GtkMenuShellClass* menu_shell_class = GTK_MENU_SHELL_CLASS(klass);
+
+ widget_class->button_press_event = gtk_custom_menu_button_press;
+ widget_class->button_release_event = gtk_custom_menu_button_release;
+ widget_class->motion_notify_event = gtk_custom_menu_motion_notify;
+
+ menu_shell_class->move_current = gtk_custom_menu_move_current;
+}
+
+GtkWidget* gtk_custom_menu_new() {
+ return GTK_WIDGET(g_object_new(GTK_TYPE_CUSTOM_MENU, NULL));
+}
diff --git a/chrome/browser/gtk/gtk_custom_menu.h b/chrome/browser/gtk/gtk_custom_menu.h
new file mode 100644
index 0000000..a2fc757
--- /dev/null
+++ b/chrome/browser/gtk/gtk_custom_menu.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_BROWSER_GTK_GTK_CUSTOM_MENU_H_
+#define CHROME_BROWSER_GTK_GTK_CUSTOM_MENU_H_
+
+// GtkCustomMenu is a GtkMenu subclass that can contain, and collaborates with,
+// GtkCustomMenuItem instances. GtkCustomMenuItem is a GtkMenuItem that can
+// have buttons and other normal widgets embeded in it. GtkCustomMenu exists
+// only to override most of the button/motion/move callback functions so
+// that the normal GtkMenu implementation doesn't handle events related to
+// GtkCustomMenuItem items.
+//
+// For a more through overview of this system, see the comments in
+// gtk_custom_menu_item.h.
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+#include <gtk/gtkmenuitem.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CUSTOM_MENU \
+ (gtk_custom_menu_get_type())
+#define GTK_CUSTOM_MENU(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_CUSTOM_MENU, GtkCustomMenu))
+#define GTK_CUSTOM_MENU_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_CUSTOM_MENU, GtkCustomMenuClass))
+#define GTK_IS_CUSTOM_MENU(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_CUSTOM_MENU))
+#define GTK_IS_CUSTOM_MENU_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_CUSTOM_MENU))
+#define GTK_CUSTOM_MENU_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_CUSTOM_MENU, GtkCustomMenuClass))
+
+typedef struct _GtkCustomMenu GtkCustomMenu;
+typedef struct _GtkCustomMenuClass GtkCustomMenuClass;
+
+struct _GtkCustomMenu {
+ GtkMenu menu;
+};
+
+struct _GtkCustomMenuClass {
+ GtkMenuClass parent_class;
+};
+
+GType gtk_custom_menu_get_type(void) G_GNUC_CONST;
+GtkWidget* gtk_custom_menu_new();
+
+G_END_DECLS
+
+#endif // CHROME_BROWSER_GTK_GTK_CUSTOM_MENU_H_
diff --git a/chrome/browser/gtk/gtk_custom_menu_item.cc b/chrome/browser/gtk/gtk_custom_menu_item.cc
new file mode 100644
index 0000000..f925c48
--- /dev/null
+++ b/chrome/browser/gtk/gtk_custom_menu_item.cc
@@ -0,0 +1,282 @@
+// Copyright (c) 2010 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/gtk_custom_menu_item.h"
+
+#include "base/logging.h"
+#include "chrome/browser/gtk/gtk_custom_menu.h"
+
+enum {
+ BUTTON_PUSHED,
+ LAST_SIGNAL
+};
+
+static guint custom_menu_item_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE(GtkCustomMenuItem, gtk_custom_menu_item, GTK_TYPE_MENU_ITEM)
+
+static void set_selected(GtkCustomMenuItem* item, GtkWidget* selected) {
+ if (selected != item->currently_selected_button) {
+ if (item->currently_selected_button)
+ gtk_widget_set_state(item->currently_selected_button, GTK_STATE_NORMAL);
+
+ item->currently_selected_button = selected;
+ if (item->currently_selected_button)
+ gtk_widget_set_state(item->currently_selected_button, GTK_STATE_SELECTED);
+ }
+}
+
+static void gtk_custom_menu_item_finalize(GObject *object);
+static gint gtk_custom_menu_item_expose(GtkWidget* widget,
+ GdkEventExpose* event);
+static void gtk_custom_menu_item_select(GtkItem *item);
+static void gtk_custom_menu_item_deselect(GtkItem *item);
+static void gtk_custom_menu_item_activate(GtkMenuItem* menu_item);
+
+static void gtk_custom_menu_item_style_set(GtkCustomMenuItem* item,
+ GtkStyle* old_style) {
+ // Because several popular themes have no idea about styling buttons in menus
+ // (it's sort of a weird concept) and look like crap, we manually apply the
+ // menu item's prelight information to the button.
+ GtkStyle* style = gtk_widget_get_style(GTK_WIDGET(item));
+
+ for (GList* i = item->button_widgets; i; i = g_list_next(i)) {
+ // Set the button prelight colors.
+ GtkWidget* button = GTK_WIDGET(i->data);
+ gtk_widget_modify_fg(button, GTK_STATE_PRELIGHT,
+ &style->fg[GTK_STATE_PRELIGHT]);
+ gtk_widget_modify_bg(button, GTK_STATE_PRELIGHT,
+ &style->bg[GTK_STATE_PRELIGHT]);
+ gtk_widget_modify_text(button, GTK_STATE_PRELIGHT,
+ &style->text[GTK_STATE_PRELIGHT]);
+ gtk_widget_modify_base(button, GTK_STATE_PRELIGHT,
+ &style->base[GTK_STATE_PRELIGHT]);
+ }
+}
+
+static void gtk_custom_menu_item_init(GtkCustomMenuItem* item) {
+ item->button_widgets = NULL;
+ item->currently_selected_button = NULL;
+ item->previously_selected_button = NULL;
+
+ GtkWidget* menu_hbox = gtk_hbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(item), menu_hbox);
+
+ item->label = gtk_label_new(NULL);
+ gtk_misc_set_alignment(GTK_MISC(item->label), 0.0, 0.5);
+ gtk_box_pack_start(GTK_BOX(menu_hbox), item->label, TRUE, TRUE, 0);
+
+ item->hbox = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(menu_hbox), item->hbox, FALSE, FALSE, 0);
+
+ g_signal_connect(item, "style-set",
+ G_CALLBACK(gtk_custom_menu_item_style_set), NULL);
+
+ gtk_widget_show_all(menu_hbox);
+}
+
+static void gtk_custom_menu_item_class_init(GtkCustomMenuItemClass* klass) {
+ GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
+ GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass);
+ GtkItemClass* item_class = GTK_ITEM_CLASS(klass);
+ GtkMenuItemClass* menu_item_class = GTK_MENU_ITEM_CLASS(klass);
+
+ gobject_class->finalize = gtk_custom_menu_item_finalize;
+
+ widget_class->expose_event = gtk_custom_menu_item_expose;
+
+ item_class->select = gtk_custom_menu_item_select;
+ item_class->deselect = gtk_custom_menu_item_deselect;
+
+ menu_item_class->activate = gtk_custom_menu_item_activate;
+
+ custom_menu_item_signals[BUTTON_PUSHED] =
+ g_signal_new("button-pushed",
+ G_OBJECT_CLASS_TYPE(gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL,
+ gtk_marshal_NONE__INT,
+ G_TYPE_NONE, 1, GTK_TYPE_INT);
+}
+
+static void gtk_custom_menu_item_finalize(GObject *object) {
+ GtkCustomMenuItem* item = GTK_CUSTOM_MENU_ITEM(object);
+ g_list_free(item->button_widgets);
+
+ G_OBJECT_CLASS(gtk_custom_menu_item_parent_class)->finalize(object);
+}
+
+static gint gtk_custom_menu_item_expose(GtkWidget* widget,
+ GdkEventExpose* event) {
+ if (GTK_WIDGET_VISIBLE(widget) &&
+ GTK_WIDGET_MAPPED(widget) &&
+ gtk_bin_get_child(GTK_BIN(widget))) {
+ // We skip the drawing in the GtkMenuItem class it draws the highlighted
+ // background and we don't want that.
+ gtk_container_propagate_expose(GTK_CONTAINER(widget),
+ gtk_bin_get_child(GTK_BIN(widget)),
+ event);
+ }
+
+ return FALSE;
+}
+
+static void gtk_custom_menu_item_select(GtkItem* item) {
+ GtkCustomMenuItem* custom_item = GTK_CUSTOM_MENU_ITEM(item);
+
+ // When we are selected, the only thing we do is clear information from
+ // previous selections. Actual selection of a button is done either in the
+ // "mouse-motion-event" or is manually set from GtkCustomMenu's overridden
+ // "move-current" handler.
+ custom_item->previously_selected_button = NULL;
+
+ gtk_widget_queue_draw(GTK_WIDGET(item));
+}
+
+static void gtk_custom_menu_item_deselect(GtkItem* item) {
+ GtkCustomMenuItem* custom_item = GTK_CUSTOM_MENU_ITEM(item);
+
+ // When we are deselected, we store the item that was currently selected so
+ // that it can be acted on. Menu items are first deselected before they are
+ // activated.
+ custom_item->previously_selected_button =
+ custom_item->currently_selected_button;
+ if (custom_item->currently_selected_button)
+ set_selected(custom_item, NULL);
+
+ gtk_widget_queue_draw(GTK_WIDGET(item));
+}
+
+static void gtk_custom_menu_item_activate(GtkMenuItem* menu_item) {
+ GtkCustomMenuItem* custom_item = GTK_CUSTOM_MENU_ITEM(menu_item);
+
+ // We look at |previously_selected_button| because by the time we've been
+ // activated, we've already gone through our deselect handler.
+ if (custom_item->previously_selected_button) {
+ gpointer id_ptr = g_object_get_data(
+ G_OBJECT(custom_item->previously_selected_button), "command-id");
+ if (id_ptr != NULL) {
+ int command_id = GPOINTER_TO_INT(id_ptr);
+ g_signal_emit(custom_item, custom_menu_item_signals[BUTTON_PUSHED], 0,
+ command_id);
+ set_selected(custom_item, NULL);
+ }
+ }
+}
+
+GtkWidget* gtk_custom_menu_item_new(const char* title) {
+ GtkCustomMenuItem* item = GTK_CUSTOM_MENU_ITEM(
+ g_object_new(GTK_TYPE_CUSTOM_MENU_ITEM, NULL));
+
+ char* markup = g_markup_printf_escaped("<b>%s</b>", title);
+ gtk_label_set_markup(GTK_LABEL(item->label), markup);
+ g_free(markup);
+
+ return GTK_WIDGET(item);
+}
+
+GtkWidget* gtk_custom_menu_item_add_button(GtkCustomMenuItem* menu_item,
+ int command_id) {
+ GtkWidget* button = gtk_button_new();
+ g_object_set_data(G_OBJECT(button), "command-id",
+ GINT_TO_POINTER(command_id));
+ gtk_box_pack_start(GTK_BOX(menu_item->hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+
+ menu_item->button_widgets = g_list_append(menu_item->button_widgets, button);
+ return button;
+}
+
+void gtk_custom_menu_item_add_space(GtkCustomMenuItem* menu_item) {
+ GtkWidget* fixed = gtk_fixed_new();
+ gtk_widget_set_size_request(fixed, 5, -1);
+
+ gtk_box_pack_start(GTK_BOX(menu_item->hbox), fixed, FALSE, FALSE, 0);
+ gtk_widget_show(fixed);
+}
+
+void gtk_custom_menu_item_receive_motion_event(GtkCustomMenuItem* menu_item,
+ gdouble x, gdouble y) {
+ GtkWidget* new_selected_widget = NULL;
+ GList* current = menu_item->button_widgets;
+ for (; current != NULL; current = current->next) {
+ GtkWidget* current_widget = GTK_WIDGET(current->data);
+ GtkAllocation alloc = current_widget->allocation;
+ int offset_x, offset_y;
+ gtk_widget_translate_coordinates(current_widget, GTK_WIDGET(menu_item),
+ 0, 0, &offset_x, &offset_y);
+ if (x >= offset_x && x < (offset_x + alloc.width) &&
+ y >= offset_y && y < (offset_y + alloc.height)) {
+ new_selected_widget = current_widget;
+ break;
+ }
+ }
+
+ set_selected(menu_item, new_selected_widget);
+}
+
+gboolean gtk_custom_menu_item_handle_move(GtkCustomMenuItem* menu_item,
+ GtkMenuDirectionType direction) {
+ GtkWidget* current = menu_item->currently_selected_button;
+ if (menu_item->button_widgets && current) {
+ switch (direction) {
+ case GTK_MENU_DIR_PREV: {
+ if (g_list_first(menu_item->button_widgets)->data == current)
+ return FALSE;
+
+ set_selected(menu_item, GTK_WIDGET(g_list_previous(g_list_find(
+ menu_item->button_widgets, current))->data));
+ break;
+ }
+ case GTK_MENU_DIR_NEXT: {
+ if (g_list_last(menu_item->button_widgets)->data == current)
+ return FALSE;
+
+ set_selected(menu_item, GTK_WIDGET(g_list_next(g_list_find(
+ menu_item->button_widgets, current))->data));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+void gtk_custom_menu_item_select_item_by_direction(
+ GtkCustomMenuItem* menu_item, GtkMenuDirectionType direction) {
+ menu_item->previously_selected_button = NULL;
+
+ // If we're just told to be selected by the menu system, select the first
+ // item.
+ if (menu_item->button_widgets) {
+ switch (direction) {
+ case GTK_MENU_DIR_PREV: {
+ GtkWidget* last_button =
+ GTK_WIDGET(g_list_last(menu_item->button_widgets)->data);
+ if (last_button)
+ set_selected(menu_item, last_button);
+ break;
+ }
+ case GTK_MENU_DIR_NEXT: {
+ GtkWidget* first_button =
+ GTK_WIDGET(g_list_first(menu_item->button_widgets)->data);
+ if (first_button)
+ set_selected(menu_item, first_button);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ gtk_widget_queue_draw(GTK_WIDGET(menu_item));
+}
+
+gboolean gtk_custom_menu_item_is_in_clickable_region(
+ GtkCustomMenuItem* menu_item) {
+ return menu_item->currently_selected_button != NULL;
+}
diff --git a/chrome/browser/gtk/gtk_custom_menu_item.h b/chrome/browser/gtk/gtk_custom_menu_item.h
new file mode 100644
index 0000000..a7c152b
--- /dev/null
+++ b/chrome/browser/gtk/gtk_custom_menu_item.h
@@ -0,0 +1,118 @@
+// Copyright (c) 2010 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.
+
+#ifndef CHROME_BROWSER_GTK_GTK_CUSTOM_MENU_ITEM_H_
+#define CHROME_BROWSER_GTK_GTK_CUSTOM_MENU_ITEM_H_
+
+// GtkCustomMenuItem is a GtkMenuItem subclass that has buttons in it and acts
+// to support this. GtkCustomMenuItems only render properly when put in a
+// GtkCustomMenu; there's a lot of collaboration between these two classes
+// necessary to work around how gtk normally does menus.
+//
+// We can't rely on the normal event infrastructure. While a menu is up, the
+// GtkMenu has a grab on all events. Instead of trying to pump events through
+// the normal channels, we have the GtkCustomMenu selectively forward mouse
+// motion through a back channel. The GtkCustomMenu only listens for button
+// press information so it can block the effects of the click if the cursor
+// isn't in a button in the menu item.
+//
+// A GtkCustomMenuItem doesn't try to take these signals and forward them to
+// the buttons it owns. The GtkCustomMenu class keeps track of which button is
+// selected (due to key events and mouse movement) and otherwise acts like a
+// normal GtkItem. The buttons are only for sizing and rendering; they don't
+// respond to events. Instead, when the GtkCustomMenuItem is activated by the
+// GtkMenu, it uses which button was selected as a signal of what to do.
+//
+// Users should connect to the "button-pushed" signal to be notified when a
+// button was pushed. We don't go through the normal "activate" signal because
+// we need to communicate additional information, namely which button was
+// activated.
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_CUSTOM_MENU_ITEM \
+ (gtk_custom_menu_item_get_type())
+#define GTK_CUSTOM_MENU_ITEM(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_CUSTOM_MENU_ITEM, \
+ GtkCustomMenuItem))
+#define GTK_CUSTOM_MENU_ITEM_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_CUSTOM_MENU_ITEM, \
+ GtkCustomMenuItemClass))
+#define GTK_IS_CUSTOM_MENU_ITEM(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_CUSTOM_MENU_ITEM))
+#define GTK_IS_CUSTOM_MENU_ITEM_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), GTK_TYPE_CUSTOM_MENU_ITEM))
+#define GTK_CUSTOM_MENU_ITEM_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_CUSTOM_MENU_ITEM, \
+ GtkCustomMenuItemClass))
+
+typedef struct _GtkCustomMenuItem GtkCustomMenuItem;
+typedef struct _GtkCustomMenuItemClass GtkCustomMenuItemClass;
+
+struct _GtkCustomMenuItem {
+ GtkMenuItem menu_item;
+
+ // Container for button widgets.
+ GtkWidget* hbox;
+
+ // Label on left side of menu item.
+ GtkWidget* label;
+
+ // Possible button widgets
+ GList* button_widgets;
+
+ // The widget that currently has highlight.
+ GtkWidget* currently_selected_button;
+
+ // The widget that was selected *before* |currently_selected_button|. Why do
+ // we hang on to this? Because the menu system sends us a deselect signal
+ // right before activating us. We need to listen to deselect since that's
+ // what we receive when the mouse cursor leaves us entirely.
+ GtkWidget* previously_selected_button;
+};
+
+struct _GtkCustomMenuItemClass {
+ GtkMenuItemClass parent_class;
+};
+
+GType gtk_custom_menu_item_get_type(void) G_GNUC_CONST;
+GtkWidget* gtk_custom_menu_item_new(const char* title);
+
+// Adds a button to our list of items in the |hbox|.
+GtkWidget* gtk_custom_menu_item_add_button(GtkCustomMenuItem* menu_item,
+ int command_id);
+
+// Adds a vertical space in the |hbox|.
+void gtk_custom_menu_item_add_space(GtkCustomMenuItem* menu_item);
+
+// Receives a motion event from the GtkCustomMenu that contains us. We can't
+// just subscribe to motion-event or the individual widget enter/leave events
+// because the top level GtkMenu has an event grab.
+void gtk_custom_menu_item_receive_motion_event(GtkCustomMenuItem* menu_item,
+ gdouble x, gdouble y);
+
+// Notification that the menu got a cursor key event. Used to move up/down
+// within the menu buttons. Returns TRUE to stop the default signal handler
+// from running.
+gboolean gtk_custom_menu_item_handle_move(GtkCustomMenuItem* menu_item,
+ GtkMenuDirectionType direction);
+
+// Because we only get a generic "selected" signal when we've changed, we need
+// to have a way for the GtkCustomMenu to tell us that we were just
+// selected.
+void gtk_custom_menu_item_select_item_by_direction(
+ GtkCustomMenuItem* menu_item, GtkMenuDirectionType direction);
+
+// Whether we are currently hovering over a clickable region on the menu
+// item. Used by GtkCustomMenu to determine whether it should discard click
+// events.
+gboolean gtk_custom_menu_item_is_in_clickable_region(
+ GtkCustomMenuItem* menu_item);
+
+G_END_DECLS
+
+#endif // CHROME_BROWSER_GTK_GTK_CUSTOM_MENU_ITEM_H_
diff --git a/chrome/browser/gtk/gtk_util.cc b/chrome/browser/gtk/gtk_util.cc
index 1bca63ed..62f1af0 100644
--- a/chrome/browser/gtk/gtk_util.cc
+++ b/chrome/browser/gtk/gtk_util.cc
@@ -449,7 +449,12 @@ GtkWidget* CenterWidgetInHBox(GtkWidget* hbox, GtkWidget* widget,
return centering_vbox;
}
-std::string ConvertAcceleratorsFromWindowsStyle(const std::string& label) {
+namespace {
+
+// Common implementation of ConvertAcceleratorsFromWindowsStyle() and
+// RemoveWindowsStyleAccelerators().
+std::string ConvertAmperstandsTo(const std::string& label,
+ const std::string& target) {
std::string ret;
ret.reserve(label.length() * 2);
for (size_t i = 0; i < label.length(); ++i) {
@@ -461,7 +466,7 @@ std::string ConvertAcceleratorsFromWindowsStyle(const std::string& label) {
ret.push_back(label[i]);
++i;
} else {
- ret.push_back('_');
+ ret.append(target);
}
} else {
ret.push_back(label[i]);
@@ -471,6 +476,16 @@ std::string ConvertAcceleratorsFromWindowsStyle(const std::string& label) {
return ret;
}
+} // namespace
+
+std::string ConvertAcceleratorsFromWindowsStyle(const std::string& label) {
+ return ConvertAmperstandsTo(label, "_");
+}
+
+std::string RemoveWindowsStyleAccelerators(const std::string& label) {
+ return ConvertAmperstandsTo(label, "");
+}
+
bool IsScreenComposited() {
GdkScreen* screen = gdk_screen_get_default();
return gdk_screen_is_composited(screen) == TRUE;
diff --git a/chrome/browser/gtk/gtk_util.h b/chrome/browser/gtk/gtk_util.h
index 7852493..3954729 100644
--- a/chrome/browser/gtk/gtk_util.h
+++ b/chrome/browser/gtk/gtk_util.h
@@ -141,6 +141,9 @@ GtkWidget* CenterWidgetInHBox(GtkWidget* hbox, GtkWidget* widget,
// accelerators. Windows uses & with && as an escape for &.)
std::string ConvertAcceleratorsFromWindowsStyle(const std::string& label);
+// Removes the "&" accelerators from a Windows label.
+std::string RemoveWindowsStyleAccelerators(const std::string& label);
+
// Returns true if the screen is composited, false otherwise.
bool IsScreenComposited();
diff --git a/chrome/browser/gtk/menu_gtk.cc b/chrome/browser/gtk/menu_gtk.cc
index 6c24fd8..104b39f 100644
--- a/chrome/browser/gtk/menu_gtk.cc
+++ b/chrome/browser/gtk/menu_gtk.cc
@@ -8,17 +8,22 @@
#include "app/l10n_util.h"
#include "app/menus/accelerator_gtk.h"
+#include "app/menus/button_menu_item_model.h"
#include "app/menus/menu_model.h"
+#include "app/resource_bundle.h"
#include "base/i18n/rtl.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/stl_util-inl.h"
#include "base/utf_string_conversions.h"
+#include "chrome/browser/gtk/gtk_custom_menu.h"
+#include "chrome/browser/gtk/gtk_custom_menu_item.h"
#include "chrome/browser/gtk/gtk_util.h"
#include "gfx/gtk_util.h"
#include "third_party/skia/include/core/SkBitmap.h"
using gtk_util::ConvertAcceleratorsFromWindowsStyle;
+using gtk_util::RemoveWindowsStyleAccelerators;
bool MenuGtk::block_activation_ = false;
@@ -99,7 +104,7 @@ MenuGtk::MenuGtk(MenuGtk::Delegate* delegate,
: delegate_(delegate),
model_(model),
dummy_accel_group_(gtk_accel_group_new()),
- menu_(gtk_menu_new()),
+ menu_(gtk_custom_menu_new()),
factory_(this) {
DCHECK(model);
g_object_ref_sink(menu_);
@@ -156,15 +161,20 @@ GtkWidget* MenuGtk::AppendSeparator() {
}
GtkWidget* MenuGtk::AppendMenuItem(int command_id, GtkWidget* menu_item) {
- return AppendMenuItemToMenu(command_id, menu_item, menu_);
+ return AppendMenuItemToMenu(command_id, menu_item, menu_, true);
}
GtkWidget* MenuGtk::AppendMenuItemToMenu(int command_id,
GtkWidget* menu_item,
- GtkWidget* menu) {
- SetMenuItemID(menu_item, command_id);
- g_signal_connect(menu_item, "activate",
- G_CALLBACK(OnMenuItemActivated), this);
+ GtkWidget* menu,
+ bool connect_to_activate) {
+ // Native menu items do their own thing, so only selectively listen for the
+ // activate signal.
+ if (connect_to_activate) {
+ SetMenuItemID(menu_item, command_id);
+ g_signal_connect(menu_item, "activate",
+ G_CALLBACK(OnMenuItemActivated), this);
+ }
gtk_widget_show(menu_item);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
@@ -231,6 +241,7 @@ void MenuGtk::BuildSubmenuFromModel(menus::MenuModel* model, GtkWidget* menu) {
SkBitmap icon;
std::string label =
ConvertAcceleratorsFromWindowsStyle(UTF16ToUTF8(model->GetLabelAt(i)));
+ bool connect_to_activate = true;
switch (model->GetTypeAt(i)) {
case menus::MenuModel::TYPE_SEPARATOR:
@@ -255,6 +266,15 @@ void MenuGtk::BuildSubmenuFromModel(menus::MenuModel* model, GtkWidget* menu) {
}
break;
}
+ case menus::MenuModel::TYPE_BUTTON_ITEM: {
+ menus::ButtonMenuItemModel* button_menu_item_model =
+ model->GetButtonMenuItemAt(i);
+
+ menu_item = BuildButtomMenuItem(button_menu_item_model);
+
+ connect_to_activate = false;
+ break;
+ }
case menus::MenuModel::TYPE_SUBMENU:
case menus::MenuModel::TYPE_COMMAND:
if (model->GetIconAt(i, &icon))
@@ -285,7 +305,7 @@ void MenuGtk::BuildSubmenuFromModel(menus::MenuModel* model, GtkWidget* menu) {
g_object_set_data(G_OBJECT(menu_item), "model",
reinterpret_cast<void*>(model));
- AppendMenuItemToMenu(i, menu_item, menu);
+ AppendMenuItemToMenu(i, menu_item, menu, connect_to_activate);
if (model->IsLabelDynamicAt(i)) {
g_signal_connect(menu, "show", G_CALLBACK(OnSubmenuShow),
@@ -296,6 +316,50 @@ void MenuGtk::BuildSubmenuFromModel(menus::MenuModel* model, GtkWidget* menu) {
}
}
+GtkWidget* MenuGtk::BuildButtomMenuItem(menus::ButtonMenuItemModel* model) {
+ GtkWidget* menu_item = gtk_custom_menu_item_new(
+ RemoveWindowsStyleAccelerators(UTF16ToUTF8(model->label())).c_str());
+ for (int i = 0; i < model->GetItemCount(); ++i) {
+ switch (model->GetTypeAt(i)) {
+ case menus::ButtonMenuItemModel::TYPE_SPACE: {
+ gtk_custom_menu_item_add_space(GTK_CUSTOM_MENU_ITEM(menu_item));
+ break;
+ }
+ case menus::ButtonMenuItemModel::TYPE_BUTTON: {
+ GtkWidget* 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)) {
+ // TODO(erg): This should go through the GtkThemeProvider so we can
+ // get a version tinted to label color.
+ gtk_button_set_image(
+ GTK_BUTTON(button),
+ gtk_image_new_from_pixbuf(
+ ResourceBundle::GetSharedInstance().
+ GetPixbufNamed(icon_idr)));
+ } else {
+ gtk_button_set_label(
+ GTK_BUTTON(button),
+ RemoveWindowsStyleAccelerators(
+ UTF16ToUTF8(model->GetLabelAt(i))).c_str());
+ }
+
+ // TODO(erg): Set up dynamic labels here.
+ break;
+ }
+ }
+ }
+
+ // Set up the callback to the model for when it is clicked.
+ g_object_set_data(G_OBJECT(menu_item), "button-model",
+ reinterpret_cast<void*>(model));
+ g_signal_connect(menu_item, "button-pushed",
+ G_CALLBACK(OnMenuButtonPressed), this);
+ return menu_item;
+}
+
// static
void MenuGtk::OnMenuItemActivated(GtkMenuItem* menuitem, MenuGtk* menu) {
if (block_activation_)
@@ -324,6 +388,19 @@ void MenuGtk::OnMenuItemActivated(GtkMenuItem* menuitem, MenuGtk* menu) {
menu->ExecuteCommand(model, id);
}
+void MenuGtk::OnMenuButtonPressed(GtkMenuItem* menu_item, int command_id,
+ MenuGtk* menu) {
+ menus::ButtonMenuItemModel* model =
+ reinterpret_cast<menus::ButtonMenuItemModel*>(
+ g_object_get_data(G_OBJECT(menu_item), "button-model"));
+ if (model) {
+ if (menu->delegate_)
+ menu->delegate_->CommandWillBeExecuted();
+
+ model->ActivatedCommand(command_id);
+ }
+}
+
// static
void MenuGtk::WidgetMenuPositionFunc(GtkMenu* menu,
int* x,
diff --git a/chrome/browser/gtk/menu_gtk.h b/chrome/browser/gtk/menu_gtk.h
index 0612d4c..a9a703a 100644
--- a/chrome/browser/gtk/menu_gtk.h
+++ b/chrome/browser/gtk/menu_gtk.h
@@ -16,6 +16,7 @@
class SkBitmap;
namespace menus {
+class ButtonMenuItemModel;
class MenuModel;
}
@@ -60,7 +61,8 @@ class MenuGtk {
GtkWidget* AppendMenuItem(int command_id, GtkWidget* menu_item);
GtkWidget* AppendMenuItemToMenu(int command_id,
GtkWidget* menu_item,
- GtkWidget* menu);
+ GtkWidget* menu,
+ bool connect_to_activate);
// Displays the menu. |timestamp| is the time of activation. The popup is
// statically positioned at |widget|.
@@ -114,6 +116,8 @@ class MenuGtk {
void BuildMenuFromModel();
// Implementation of the above; called recursively.
void BuildSubmenuFromModel(menus::MenuModel* model, GtkWidget* menu);
+ // Builds a menu item with buttons in it from the data in the model.
+ GtkWidget* BuildButtomMenuItem(menus::ButtonMenuItemModel* model);
// Contains implementation for OnMenuShow.
void UpdateMenu();
@@ -123,6 +127,10 @@ class MenuGtk {
// Callback for when a menu item is clicked.
static void OnMenuItemActivated(GtkMenuItem* menuitem, MenuGtk* menu);
+ // Called when one of the buttons are pressed.
+ static void OnMenuButtonPressed(GtkMenuItem* menuitem, int command_id,
+ MenuGtk* menu);
+
// Sets the check mark and enabled/disabled state on our menu items.
static void SetMenuItemInfo(GtkWidget* widget, void* raw_menu);
diff --git a/chrome/browser/gtk/notifications/notification_options_menu_model.cc b/chrome/browser/gtk/notifications/notification_options_menu_model.cc
index 0cbb81e..9b5225a 100644
--- a/chrome/browser/gtk/notifications/notification_options_menu_model.cc
+++ b/chrome/browser/gtk/notifications/notification_options_menu_model.cc
@@ -64,6 +64,11 @@ bool NotificationOptionsMenuModel::GetIconAt(int index, SkBitmap* icon) const {
return false;
}
+menus::ButtonMenuItemModel* NotificationOptionsMenuModel::GetButtonMenuItemAt(
+ int index) const {
+ return NULL;
+}
+
bool NotificationOptionsMenuModel::IsEnabledAt(int index) const {
return true;
}
diff --git a/chrome/browser/gtk/notifications/notification_options_menu_model.h b/chrome/browser/gtk/notifications/notification_options_menu_model.h
index f4b67f3..a88adb7 100644
--- a/chrome/browser/gtk/notifications/notification_options_menu_model.h
+++ b/chrome/browser/gtk/notifications/notification_options_menu_model.h
@@ -27,6 +27,7 @@ class NotificationOptionsMenuModel : public menus::MenuModel {
virtual bool IsItemCheckedAt(int index) const;
virtual int GetGroupIdAt(int index) const;
virtual bool GetIconAt(int index, SkBitmap* icon) const;
+ virtual menus::ButtonMenuItemModel* GetButtonMenuItemAt(int index) const;
virtual bool IsEnabledAt(int index) const;
virtual MenuModel* GetSubmenuModelAt(int index) const;
virtual void HighlightChangedTo(int index);
diff --git a/chrome/browser/wrench_menu_model.cc b/chrome/browser/wrench_menu_model.cc
index d4c94f0..326e12f 100644
--- a/chrome/browser/wrench_menu_model.cc
+++ b/chrome/browser/wrench_menu_model.cc
@@ -5,20 +5,26 @@
#include "chrome/browser/wrench_menu_model.h"
#include <algorithm>
+#include <cmath>
#include "app/l10n_util.h"
+#include "app/menus/button_menu_item_model.h"
#include "app/resource_bundle.h"
#include "base/command_line.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/browser.h"
+#include "chrome/browser/host_zoom_map.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/defaults.h"
#include "chrome/browser/page_menu_model.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/sync_ui_util.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/upgrade_detector.h"
#include "chrome/common/chrome_switches.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/notification_type.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
@@ -61,8 +67,13 @@ void ToolsMenuModel::Build(Browser* browser) {
WrenchMenuModel::WrenchMenuModel(menus::SimpleMenuModel::Delegate* delegate,
Browser* browser)
: menus::SimpleMenuModel(delegate),
+ delegate_(delegate),
browser_(browser) {
Build();
+ UpdateZoomControls();
+
+ registrar_.Add(this, NotificationType::ZOOM_LEVEL_CHANGED,
+ Source<Profile>(browser_->profile()));
}
WrenchMenuModel::~WrenchMenuModel() {
@@ -125,15 +136,59 @@ bool WrenchMenuModel::GetIconAt(int index, SkBitmap* icon) const {
return false;
}
+bool WrenchMenuModel::IsLabelForCommandIdDynamic(int command_id) const {
+ return false;
+}
+
+string16 WrenchMenuModel::GetLabelForCommandId(int command_id) const {
+ // TODO(erg): Hook up percentage calculation once I add that widget.
+ return string16();
+}
+
+void WrenchMenuModel::ExecuteCommand(int command_id) {
+ if (delegate_)
+ delegate_->ExecuteCommand(command_id);
+}
+
+void WrenchMenuModel::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK_EQ(NotificationType::ZOOM_LEVEL_CHANGED, type.value);
+ UpdateZoomControls();
+}
+
void WrenchMenuModel::Build() {
AddItemWithStringId(IDC_NEW_TAB, IDS_NEW_TAB);
AddItemWithStringId(IDC_NEW_WINDOW, IDS_NEW_WINDOW);
AddItemWithStringId(IDC_NEW_INCOGNITO_WINDOW, IDS_NEW_INCOGNITO_WINDOW);
AddSeparator();
+#if defined(OS_LINUX)
+ edit_menu_item_model_.reset(new menus::ButtonMenuItemModel(IDS_EDIT, this));
+ edit_menu_item_model_->AddItemWithStringId(IDC_CUT, IDS_CUT);
+ edit_menu_item_model_->AddItemWithStringId(IDC_COPY, IDS_COPY);
+ edit_menu_item_model_->AddItemWithStringId(IDC_PASTE, IDS_PASTE);
+ AddButtonItem(0, edit_menu_item_model_.get());
+#else
+ // TODO(port): Move to the above.
CreateCutCopyPaste();
+#endif
+
AddSeparator();
+#if defined(OS_LINUX)
+ zoom_menu_item_model_.reset(
+ new menus::ButtonMenuItemModel(IDS_ZOOM_MENU, this));
+ zoom_menu_item_model_->AddItemWithStringId(IDC_ZOOM_PLUS, IDS_ZOOM_PLUS2);
+ zoom_menu_item_model_->AddItemWithStringId(IDC_ZOOM_MINUS, IDS_ZOOM_MINUS2);
+ zoom_menu_item_model_->AddSpace();
+ zoom_menu_item_model_->AddItemWithImage(
+ IDC_FULLSCREEN, IDR_FULLSCREEN_MENU_BUTTON);
+ AddButtonItem(0, zoom_menu_item_model_.get());
+#else
+ // TODO(port): Move to the above.
CreateZoomFullscreen();
+#endif
+
AddSeparator();
AddItemWithStringId(IDC_SAVE_PAGE, IDS_SAVE_PAGE);
AddItemWithStringId(IDC_FIND, IDS_FIND);
@@ -188,6 +243,37 @@ void WrenchMenuModel::CreateZoomFullscreen() {
AddItemWithStringId(IDC_FULLSCREEN, IDS_FULLSCREEN);
}
+void WrenchMenuModel::UpdateZoomControls() {
+ bool enable_increment, enable_decrement;
+ int zoom_percent =
+ static_cast<int>(GetZoom(&enable_increment, &enable_decrement) * 100);
+
+ // TODO(erg): Route the stringified zoom_percent through
+ // GetLabelForCommandId(). Also make a way to use enable_increment/decrement.
+ zoom_label_ = l10n_util::GetStringFUTF16(
+ IDS_ZOOM_PERCENT, IntToString16(zoom_percent));
+}
+
+double WrenchMenuModel::GetZoom(bool* enable_increment,
+ bool* enable_decrement) {
+ TabContents* selected_tab = browser_->GetSelectedTabContents();
+ *enable_decrement = *enable_increment = false;
+ if (!selected_tab)
+ return 1;
+
+ HostZoomMap* zoom_map = selected_tab->profile()->GetHostZoomMap();
+ if (!zoom_map)
+ return 1;
+
+ // This code comes from WebViewImpl::setZoomLevel.
+ int zoom_level = zoom_map->GetZoomLevel(selected_tab->GetURL());
+ double value = static_cast<double>(
+ std::max(std::min(std::pow(1.2, zoom_level), 3.0), .5));
+ *enable_decrement = (value != .5);
+ *enable_increment = (value != 3.0);
+ return value;
+}
+
string16 WrenchMenuModel::GetSyncMenuLabel() const {
return sync_ui_util::GetSyncMenuLabel(
browser_->profile()->GetOriginalProfile()->GetProfileSyncService());
diff --git a/chrome/browser/wrench_menu_model.h b/chrome/browser/wrench_menu_model.h
index f86a170..5b7d2d5 100644
--- a/chrome/browser/wrench_menu_model.h
+++ b/chrome/browser/wrench_menu_model.h
@@ -11,14 +11,19 @@
#include "app/menus/simple_menu_model.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
class Browser;
class EncodingMenuModel;
+namespace menus {
+class ButtonMenuItemModel;
+} // namespace menus
+
class ToolsMenuModel : public menus::SimpleMenuModel {
public:
- explicit ToolsMenuModel(menus::SimpleMenuModel::Delegate* delegate,
- Browser* browser);
+ ToolsMenuModel(menus::SimpleMenuModel::Delegate* delegate, Browser* browser);
virtual ~ToolsMenuModel();
private:
@@ -30,10 +35,12 @@ class ToolsMenuModel : public menus::SimpleMenuModel {
};
// A menu model that builds the contents of the wrench menu.
-class WrenchMenuModel : public menus::SimpleMenuModel {
+class WrenchMenuModel : public menus::SimpleMenuModel,
+ public menus::ButtonMenuItemModel::Delegate,
+ public NotificationObserver {
public:
- explicit WrenchMenuModel(menus::SimpleMenuModel::Delegate* delegate,
- Browser* browser);
+ WrenchMenuModel(menus::SimpleMenuModel::Delegate* delegate,
+ Browser* browser);
virtual ~WrenchMenuModel();
// Returns true if the WrenchMenuModel is enabled.
@@ -45,27 +52,48 @@ class WrenchMenuModel : public menus::SimpleMenuModel {
virtual bool HasIcons() const { return true; }
virtual bool GetIconAt(int index, SkBitmap* icon) const;
- protected:
- // Adds the cut/copy/paste items to the menu. The default implementation adds
- // three real menu items, while platform specific subclasses add their own
- // native monstrosities.
- virtual void CreateCutCopyPaste();
+ // Overridden from menus::ButtonMenuItemModel::Delegate:
+ virtual bool IsLabelForCommandIdDynamic(int command_id) const;
+ virtual string16 GetLabelForCommandId(int command_id) const;
+ virtual void ExecuteCommand(int command_id);
- // Adds the zoom/fullscreen items to the menu. Like CreateCutCopyPaste().
- virtual void CreateZoomFullscreen();
+ // Overridden from NotificationObserver:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
private:
void Build();
+ // Adds custom items to the menu. Deprecated in favor of a cross platform
+ // model for button items.
+ void CreateCutCopyPaste();
+ void CreateZoomFullscreen();
+
+ // Calculates |zoom_label_| in response to a zoom change.
+ void UpdateZoomControls();
+ double GetZoom(bool* enable_increment, bool* enable_decrement);
+
string16 GetSyncMenuLabel() const;
string16 GetAboutEntryMenuLabel() const;
bool IsDynamicItem(int index) const;
+ // Models for the special menu items with buttons.
+ scoped_ptr<menus::ButtonMenuItemModel> edit_menu_item_model_;
+ scoped_ptr<menus::ButtonMenuItemModel> zoom_menu_item_model_;
+
+ // Label of the zoom label in the zoom menu item.
+ string16 zoom_label_;
+
// Tools menu.
scoped_ptr<ToolsMenuModel> tools_menu_model_;
+ menus::SimpleMenuModel::Delegate* delegate_; // weak
+
Browser* browser_; // weak
+ NotificationRegistrar registrar_;
+
DISALLOW_COPY_AND_ASSIGN(WrenchMenuModel);
};
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 3f49e89..f382ff7 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1395,6 +1395,10 @@
'browser/gtk/gtk_chrome_link_button.h',
'browser/gtk/gtk_chrome_shrinkable_hbox.cc',
'browser/gtk/gtk_chrome_shrinkable_hbox.h',
+ 'browser/gtk/gtk_custom_menu.cc',
+ 'browser/gtk/gtk_custom_menu.h',
+ 'browser/gtk/gtk_custom_menu_item.cc',
+ 'browser/gtk/gtk_custom_menu_item.h',
'browser/gtk/gtk_expanded_container.cc',
'browser/gtk/gtk_expanded_container.h',
'browser/gtk/gtk_floating_container.cc',
@@ -3279,6 +3283,10 @@
['include', '^browser/gtk/gtk_chrome_button.h'],
['include', '^browser/gtk/gtk_chrome_link_button.cc'],
['include', '^browser/gtk/gtk_chrome_link_button.h'],
+ ['include', '^browser/gtk/gtk_custom_menu.cc'],
+ ['include', '^browser/gtk/gtk_custom_menu.h'],
+ ['include', '^browser/gtk/gtk_custom_menu_item.cc'],
+ ['include', '^browser/gtk/gtk_custom_menu_item.h'],
['include', '^browser/gtk/gtk_theme_provider.cc'],
['include', '^browser/gtk/gtk_theme_provider.h'],
['include', '^browser/gtk/gtk_tree.cc'],