summaryrefslogtreecommitdiffstats
path: root/chrome/browser/gtk
diff options
context:
space:
mode:
authorestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-08 23:11:03 +0000
committerestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-08 23:11:03 +0000
commitaf4282a5e55c5c14413654f498a0117413ab0565 (patch)
tree35e45ce4b35ffacd25939dae68f99c1dd2679850 /chrome/browser/gtk
parent53b31efebbd3ceffdde84bb62f78986620179040 (diff)
downloadchromium_src-af4282a5e55c5c14413654f498a0117413ab0565.zip
chromium_src-af4282a5e55c5c14413654f498a0117413ab0565.tar.gz
chromium_src-af4282a5e55c5c14413654f498a0117413ab0565.tar.bz2
Make the bookmark toolbar folders act like a menu bar.
I didn't convert the page/app menu to use the new helper class in an effort to keep this patch small and reviewable. BUG=19675 BUG=15889 TEST=fiddled with it for while, and ran it under valgrind Review URL: http://codereview.chromium.org/200029 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@25677 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/gtk')
-rw-r--r--chrome/browser/gtk/bookmark_bar_gtk.cc97
-rw-r--r--chrome/browser/gtk/bookmark_bar_gtk.h14
-rw-r--r--chrome/browser/gtk/bookmark_menu_controller_gtk.cc16
-rw-r--r--chrome/browser/gtk/bookmark_menu_controller_gtk.h4
-rw-r--r--chrome/browser/gtk/menu_bar_helper.cc181
-rw-r--r--chrome/browser/gtk/menu_bar_helper.h82
6 files changed, 361 insertions, 33 deletions
diff --git a/chrome/browser/gtk/bookmark_bar_gtk.cc b/chrome/browser/gtk/bookmark_bar_gtk.cc
index eb16e2e..0504349 100644
--- a/chrome/browser/gtk/bookmark_bar_gtk.cc
+++ b/chrome/browser/gtk/bookmark_bar_gtk.cc
@@ -101,7 +101,8 @@ BookmarkBarGtk::BookmarkBarGtk(Profile* profile, Browser* browser,
dragged_node_(NULL),
toolbar_drop_item_(NULL),
theme_provider_(GtkThemeProvider::GetFrom(profile)),
- show_instructions_(true) {
+ show_instructions_(true),
+ menu_bar_helper_(this) {
Init(profile);
SetProfile(profile);
@@ -310,9 +311,12 @@ void BookmarkBarGtk::BookmarkNodeAdded(BookmarkModel* model,
}
DCHECK(index >= 0 && index <= GetBookmarkButtonCount());
- GtkToolItem* item = CreateBookmarkToolItem(parent->GetChild(index));
+ const BookmarkNode* node = parent->GetChild(index);
+ GtkToolItem* item = CreateBookmarkToolItem(node);
gtk_toolbar_insert(GTK_TOOLBAR(bookmark_toolbar_.get()),
item, index);
+ if (node->is_folder())
+ menu_bar_helper_.Add(gtk_bin_get_child(GTK_BIN(item)));
SetInstructionState();
SetChevronState();
@@ -330,6 +334,7 @@ void BookmarkBarGtk::BookmarkNodeRemoved(BookmarkModel* model,
GtkWidget* to_remove = GTK_WIDGET(gtk_toolbar_get_nth_item(
GTK_TOOLBAR(bookmark_toolbar_.get()), old_index));
+ menu_bar_helper_.Remove(gtk_bin_get_child(GTK_BIN(to_remove)));
gtk_container_remove(GTK_CONTAINER(bookmark_toolbar_.get()),
to_remove);
@@ -376,6 +381,8 @@ void BookmarkBarGtk::CreateAllBookmarkButtons() {
for (int i = 0; i < node->GetChildCount(); ++i) {
GtkToolItem* item = CreateBookmarkToolItem(node->GetChild(i));
gtk_toolbar_insert(GTK_TOOLBAR(bookmark_toolbar_.get()), item, -1);
+ if (node->is_folder())
+ menu_bar_helper_.Add(gtk_bin_get_child(GTK_BIN(item)));
}
bookmark_utils::ConfigureButtonForNode(model_->other_node(),
@@ -400,7 +407,7 @@ void BookmarkBarGtk::SetChevronState() {
if (GTK_WIDGET_VISIBLE(overflow_button_))
extra_space = overflow_button_->allocation.width;
- int overflow_idx = GetFirstHiddenBookmark(extra_space);
+ int overflow_idx = GetFirstHiddenBookmark(extra_space, NULL);
if (overflow_idx == -1)
gtk_widget_hide(overflow_button_);
else
@@ -409,6 +416,9 @@ void BookmarkBarGtk::SetChevronState() {
void BookmarkBarGtk::RemoveAllBookmarkButtons() {
gtk_util::RemoveAllChildren(bookmark_toolbar_.get());
+ menu_bar_helper_.Clear();
+ menu_bar_helper_.Add(other_bookmarks_button_);
+ menu_bar_helper_.Add(overflow_button_);
}
int BookmarkBarGtk::GetBookmarkButtonCount() {
@@ -433,7 +443,8 @@ void BookmarkBarGtk::SetOverflowButtonAppearance() {
SetChevronState();
}
-int BookmarkBarGtk::GetFirstHiddenBookmark(int extra_space) {
+int BookmarkBarGtk::GetFirstHiddenBookmark(
+ int extra_space, std::vector<GtkWidget*>* showing_folders) {
int rv = 0;
bool overflow = false;
GList* toolbar_items =
@@ -445,6 +456,10 @@ int BookmarkBarGtk::GetFirstHiddenBookmark(int extra_space) {
overflow = true;
break;
}
+ if (showing_folders &&
+ model_->GetBookmarkBarNode()->GetChild(rv)->is_folder()) {
+ showing_folders->push_back(gtk_bin_get_child(GTK_BIN(tool_item)));
+ }
rv++;
}
@@ -534,7 +549,6 @@ GtkWidget* BookmarkBarGtk::CreateBookmarkButton(const BookmarkNode* node) {
G_CALLBACK(OnClicked), this);
gtk_util::SetButtonTriggersNavigation(button);
} else {
- // TODO(erg): This button can also be a drop target.
ConnectFolderButtonEvents(button);
}
@@ -717,24 +731,7 @@ void BookmarkBarGtk::OnButtonDragGet(GtkWidget* widget, GdkDragContext* context,
// static
void BookmarkBarGtk::OnFolderClicked(GtkWidget* sender,
BookmarkBarGtk* bar) {
- const BookmarkNode* node = bar->GetNodeForToolButton(sender);
- DCHECK(node);
- DCHECK(bar->page_navigator_);
-
- int start_child_idx = 0;
- if (sender == bar->overflow_button_)
- start_child_idx = bar->GetFirstHiddenBookmark(0);
-
- bar->current_menu_.reset(
- new BookmarkMenuController(bar->browser_, bar->profile_,
- bar->page_navigator_,
- GTK_WINDOW(gtk_widget_get_toplevel(sender)),
- node,
- start_child_idx,
- false));
- GdkEventButton* event =
- reinterpret_cast<GdkEventButton*>(gtk_get_current_event());
- bar->current_menu_->Popup(sender, event->button, event->time);
+ bar->PopupForButton(sender);
}
// static
@@ -972,3 +969,57 @@ gboolean BookmarkBarGtk::OnSeparatorExpose(GtkWidget* widget,
return TRUE;
}
+
+// MenuBarHelper::Delegate implementation --------------------------------------
+void BookmarkBarGtk::PopupForButton(GtkWidget* button) {
+ const BookmarkNode* node = GetNodeForToolButton(button);
+ DCHECK(node);
+ DCHECK(page_navigator_);
+
+ int first_hidden = GetFirstHiddenBookmark(0, NULL);
+ if (button != overflow_button_ && button != other_bookmarks_button_ &&
+ node->GetParent()->IndexOfChild(node) >= first_hidden) {
+ return;
+ }
+
+ current_menu_.reset(
+ new BookmarkMenuController(browser_, profile_,
+ page_navigator_,
+ GTK_WINDOW(gtk_widget_get_toplevel(button)),
+ node,
+ button == overflow_button_ ?
+ first_hidden : 0,
+ false));
+ menu_bar_helper_.MenuStartedShowing(button, current_menu_->widget());
+ GdkEventButton* event =
+ reinterpret_cast<GdkEventButton*>(gtk_get_current_event());
+ current_menu_->Popup(button, event->button, event->time);
+}
+
+void BookmarkBarGtk::PopupForButtonNextTo(GtkWidget* button,
+ GtkMenuDirectionType dir) {
+ const BookmarkNode* relative_node = GetNodeForToolButton(button);
+ DCHECK(relative_node);
+
+ // Find out the order of the buttons.
+ std::vector<GtkWidget*> folder_list;
+ const int first_hidden = GetFirstHiddenBookmark(0, &folder_list);
+ if (first_hidden != -1)
+ folder_list.push_back(overflow_button_);
+ folder_list.push_back(other_bookmarks_button_);
+
+ // Find the position of |button|.
+ int button_idx = -1;
+ for (size_t i = 0; i < folder_list.size(); ++i) {
+ if (folder_list[i] == button) {
+ button_idx = i;
+ break;
+ }
+ }
+ DCHECK_NE(button_idx, -1);
+
+ // Find the GtkWidget* for the actual target button.
+ int shift = dir == GTK_MENU_DIR_PARENT ? -1 : 1;
+ button_idx = (button_idx + shift + folder_list.size()) % folder_list.size();
+ PopupForButton(folder_list[button_idx]);
+}
diff --git a/chrome/browser/gtk/bookmark_bar_gtk.h b/chrome/browser/gtk/bookmark_bar_gtk.h
index afc7e8a..c7cb22d 100644
--- a/chrome/browser/gtk/bookmark_bar_gtk.h
+++ b/chrome/browser/gtk/bookmark_bar_gtk.h
@@ -8,10 +8,12 @@
#include <gtk/gtk.h>
#include <string>
+#include <vector>
#include "app/slide_animation.h"
#include "base/scoped_ptr.h"
#include "chrome/browser/bookmarks/bookmark_model_observer.h"
+#include "chrome/browser/gtk/menu_bar_helper.h"
#include "chrome/browser/gtk/view_id_util.h"
#include "chrome/common/notification_observer.h"
#include "chrome/common/notification_registrar.h"
@@ -28,6 +30,7 @@ struct GtkThemeProvider;
class BookmarkBarGtk : public AnimationDelegate,
public BookmarkModelObserver,
+ public MenuBarHelper::Delegate,
public NotificationObserver {
public:
explicit BookmarkBarGtk(Profile* profile, Browser* browser,
@@ -76,6 +79,10 @@ class BookmarkBarGtk : public AnimationDelegate,
virtual void AnimationProgressed(const Animation* animation);
virtual void AnimationEnded(const Animation* animation);
+ // MenuBarHelper::Delegate implementation ------------------------------------
+ virtual void PopupForButton(GtkWidget* button);
+ virtual void PopupForButtonNextTo(GtkWidget* button,
+ GtkMenuDirectionType dir);
private:
// Helper function which generates GtkToolItems for |bookmark_toolbar_|.
void CreateAllBookmarkButtons();
@@ -104,7 +111,10 @@ class BookmarkBarGtk : public AnimationDelegate,
// |extra_space| is how much extra space to give the toolbar during the
// calculation (for the purposes of determining if ditching the chevron
// would be a good idea).
- int GetFirstHiddenBookmark(int extra_space);
+ // If non-NULL, |showing_folders| is packed with all the folders that are
+ // showing on the bar.
+ int GetFirstHiddenBookmark(int extra_space,
+ std::vector<GtkWidget*>* showing_folders);
// Overridden from BookmarkModelObserver:
@@ -252,6 +262,8 @@ class BookmarkBarGtk : public AnimationDelegate,
// Whether we should show the instructional text in the bookmark bar.
bool show_instructions_;
+ MenuBarHelper menu_bar_helper_;
+
// The last displayed right click menu, or NULL if no menus have been
// displayed yet.
scoped_ptr<BookmarkContextMenu> current_context_menu_;
diff --git a/chrome/browser/gtk/bookmark_menu_controller_gtk.cc b/chrome/browser/gtk/bookmark_menu_controller_gtk.cc
index e17a918b..8334fd5 100644
--- a/chrome/browser/gtk/bookmark_menu_controller_gtk.cc
+++ b/chrome/browser/gtk/bookmark_menu_controller_gtk.cc
@@ -97,16 +97,16 @@ BookmarkMenuController::BookmarkMenuController(Browser* browser,
node_(node),
ignore_button_release_(false),
triggering_widget_(NULL) {
- menu_.Own(gtk_menu_new());
- BuildMenu(node, start_child_index, menu_.get());
- g_signal_connect(menu_.get(), "hide",
+ menu_ = gtk_menu_new();
+ BuildMenu(node, start_child_index, menu_);
+ g_signal_connect(menu_, "hide",
G_CALLBACK(OnMenuHidden), this);
- gtk_widget_show_all(menu_.get());
+ gtk_widget_show_all(menu_);
}
BookmarkMenuController::~BookmarkMenuController() {
profile_->GetBookmarkModel()->RemoveObserver(this);
- menu_.Destroy();
+ gtk_menu_popdown(GTK_MENU(menu_));
}
void BookmarkMenuController::Popup(GtkWidget* widget, gint button_type,
@@ -116,13 +116,13 @@ void BookmarkMenuController::Popup(GtkWidget* widget, gint button_type,
triggering_widget_ = widget;
gtk_chrome_button_set_paint_state(GTK_CHROME_BUTTON(widget),
GTK_STATE_ACTIVE);
- gtk_menu_popup(GTK_MENU(menu_.get()), NULL, NULL,
+ gtk_menu_popup(GTK_MENU(menu_), NULL, NULL,
&MenuGtk::MenuPositionFunc,
widget, button_type, timestamp);
}
void BookmarkMenuController::BookmarkModelChanged() {
- gtk_menu_popdown(GTK_MENU(menu_.get()));
+ gtk_menu_popdown(GTK_MENU(menu_));
}
void BookmarkMenuController::BookmarkNodeFavIconLoaded(
@@ -279,7 +279,7 @@ gboolean BookmarkMenuController::OnButtonReleased(
// We need to manually dismiss the popup menu because we're overriding
// button-release-event.
- gtk_menu_popdown(GTK_MENU(controller->menu_.get()));
+ gtk_menu_popdown(GTK_MENU(controller->menu_));
return TRUE;
}
diff --git a/chrome/browser/gtk/bookmark_menu_controller_gtk.h b/chrome/browser/gtk/bookmark_menu_controller_gtk.h
index 7c5cc5f..f3182df 100644
--- a/chrome/browser/gtk/bookmark_menu_controller_gtk.h
+++ b/chrome/browser/gtk/bookmark_menu_controller_gtk.h
@@ -35,6 +35,8 @@ class BookmarkMenuController : public BaseBookmarkModelObserver {
bool show_other_folder);
virtual ~BookmarkMenuController();
+ GtkWidget* widget() { return menu_; }
+
// Pops up the menu. |widget| must be a GtkChromeButton.
void Popup(GtkWidget* widget, gint button_type, guint32 timestamp);
@@ -104,7 +106,7 @@ class BookmarkMenuController : public BaseBookmarkModelObserver {
// all sorts of weird non-standard things with this menu, like:
// - The menu is a drag target
// - The menu items have context menus.
- OwnedWidgetGtk menu_;
+ GtkWidget* menu_;
// Whether we should ignore the next button release event (because we were
// dragging).
diff --git a/chrome/browser/gtk/menu_bar_helper.cc b/chrome/browser/gtk/menu_bar_helper.cc
new file mode 100644
index 0000000..4eb228a
--- /dev/null
+++ b/chrome/browser/gtk/menu_bar_helper.cc
@@ -0,0 +1,181 @@
+// 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_bar_helper.h"
+
+#include "base/logging.h"
+#include "chrome/common/gtk_util.h"
+
+namespace {
+
+// Recursively find all the GtkMenus that are attached to menu item |child|
+// and add them to |data|, which is a vector of GtkWidgets.
+void PopulateSubmenus(GtkWidget* child, gpointer data) {
+ std::vector<GtkWidget*>* submenus =
+ static_cast<std::vector<GtkWidget*>*>(data);
+ GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(child));
+ if (submenu) {
+ submenus->push_back(submenu);
+ gtk_container_foreach(GTK_CONTAINER(submenu), PopulateSubmenus, submenus);
+ }
+}
+
+// Is the cursor over |menu| or one of its parent menus?
+bool MotionIsOverMenu(GtkWidget* menu, GdkEventMotion* motion) {
+ if (motion->x >= 0 && motion->y >= 0 &&
+ motion->x < menu->allocation.width &&
+ motion->y < menu->allocation.height) {
+ return true;
+ }
+
+ while (menu) {
+ GtkWidget* menu_item = gtk_menu_get_attach_widget(GTK_MENU(menu));
+ if (!menu_item)
+ return false;
+ GtkWidget* parent = gtk_widget_get_parent(menu_item);
+
+ if (gtk_util::WidgetContainsCursor(parent))
+ return true;
+ menu = parent;
+ }
+
+ return false;
+}
+
+} // namespace
+
+MenuBarHelper::MenuBarHelper(Delegate* delegate)
+ : button_showing_menu_(NULL),
+ showing_menu_(NULL),
+ delegate_(delegate) {
+ DCHECK(delegate_);
+}
+
+MenuBarHelper::~MenuBarHelper() {
+}
+
+void MenuBarHelper::Add(GtkWidget* button) {
+ buttons_.push_back(button);
+}
+
+void MenuBarHelper::Remove(GtkWidget* button) {
+ std::vector<GtkWidget*>::iterator iter =
+ find(buttons_.begin(), buttons_.end(), button);
+ if (iter == buttons_.end()) {
+ NOTREACHED();
+ return;
+ }
+ buttons_.erase(iter);
+}
+
+void MenuBarHelper::Clear() {
+ buttons_.clear();
+}
+
+void MenuBarHelper::MenuStartedShowing(GtkWidget* button, GtkWidget* menu) {
+ DCHECK(GTK_IS_MENU(menu));
+ button_showing_menu_ = button;
+ showing_menu_ = menu;
+ g_signal_connect(menu, "motion-notify-event",
+ G_CALLBACK(OnMenuMotionNotifyThunk), this);
+ g_signal_connect(menu, "hide",
+ G_CALLBACK(OnMenuHiddenThunk), this);
+ g_signal_connect(menu, "move-current",
+ G_CALLBACK(OnMenuMoveCurrentThunk), this);
+ gtk_container_foreach(GTK_CONTAINER(menu), PopulateSubmenus, &submenus_);
+ for (size_t i = 0; i < submenus_.size(); ++i) {
+ g_signal_connect(submenus_[i], "motion-notify-event",
+ G_CALLBACK(OnMenuMotionNotifyThunk), this);
+ }
+}
+
+gboolean MenuBarHelper::OnMenuMotionNotify(GtkWidget* menu,
+ GdkEventMotion* motion) {
+ // Don't do anything if pointer is in the menu.
+ if (MotionIsOverMenu(menu, motion))
+ return FALSE;
+ if (buttons_.empty())
+ return FALSE;
+
+ gint x = 0;
+ gint y = 0;
+ GtkWidget* last_button = NULL;
+
+ for (size_t i = 0; i < buttons_.size(); ++i) {
+ GtkWidget* button = buttons_[i];
+ // Figure out coordinates relative to this button. Avoid using
+ // gtk_widget_get_pointer() unnecessarily.
+ if (i == 0) {
+ // We have to make this call because the menu is a popup window, so it
+ // doesn't share a toplevel with the buttons and we can't just use
+ // gtk_widget_translate_coordinates().
+ gtk_widget_get_pointer(buttons_[0], &x, &y);
+ } else {
+ gint last_x = x;
+ gint last_y = y;
+ if (!gtk_widget_translate_coordinates(
+ last_button, button, last_x, last_y, &x, &y)) {
+ NOTREACHED();
+ return FALSE;
+ }
+ }
+
+ last_button = button;
+
+ if (x >= 0 && y >= 0 && x < button->allocation.width &&
+ y < button->allocation.height) {
+ if (button != button_showing_menu_)
+ delegate_->PopupForButton(button);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+void MenuBarHelper::OnMenuHidden(GtkWidget* menu) {
+ DCHECK_EQ(showing_menu_, menu);
+ int matched = g_signal_handlers_disconnect_matched(showing_menu_,
+ G_SIGNAL_MATCH_DATA, 0, NULL, NULL, NULL, this);
+ DCHECK_EQ(3, matched);
+
+ for (size_t i = 0; i < submenus_.size(); ++i) {
+ g_signal_handlers_disconnect_by_func(submenus_[i],
+ reinterpret_cast<gpointer>(OnMenuMotionNotifyThunk), this);
+ }
+ showing_menu_ = NULL;
+ button_showing_menu_ = NULL;
+ submenus_.clear();
+}
+
+void MenuBarHelper::OnMenuMoveCurrent(GtkWidget* menu,
+ GtkMenuDirectionType dir) {
+ // The menu directions are triggered by the arrow keys as follows
+ //
+ // PARENT left
+ // CHILD right
+ // NEXT down
+ // PREV up
+ //
+ // We only care about left and right. Note that for RTL, they are swapped.
+ switch (dir) {
+ case GTK_MENU_DIR_CHILD: {
+ GtkWidget* active_item = GTK_MENU_SHELL(menu)->active_menu_item;
+ // The move is going to open a submenu; don't override default behavior.
+ if (active_item && gtk_menu_item_get_submenu(GTK_MENU_ITEM(active_item)))
+ return;
+ // Fall through.
+ }
+ case GTK_MENU_DIR_PARENT: {
+ delegate_->PopupForButtonNextTo(button_showing_menu_, dir);
+ break;
+ }
+ default:
+ return;
+ }
+
+ // This signal doesn't have a return value; we have to manually stop its
+ // propagation.
+ g_signal_stop_emission_by_name(menu, "move-current");
+}
diff --git a/chrome/browser/gtk/menu_bar_helper.h b/chrome/browser/gtk/menu_bar_helper.h
new file mode 100644
index 0000000..9b6448c
--- /dev/null
+++ b/chrome/browser/gtk/menu_bar_helper.h
@@ -0,0 +1,82 @@
+// 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.
+//
+// This class replicates some menubar behaviors that are tricky to get right.
+// It is used to create a more native feel for the bookmark bar and the
+// page/app menus.
+
+#ifndef CHROME_BROWSER_GTK_MENU_BAR_HELPER_H_
+#define CHROME_BROWSER_GTK_MENU_BAR_HELPER_H_
+
+#include <gtk/gtk.h>
+
+#include <vector>
+
+class MenuBarHelper {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ // Called when a the menu for a button ought to be triggered.
+ virtual void PopupForButton(GtkWidget* button) = 0;
+ virtual void PopupForButtonNextTo(GtkWidget* button,
+ GtkMenuDirectionType dir) = 0;
+ };
+
+ // |delegate| cannot be null.
+ explicit MenuBarHelper(Delegate* delegate);
+ ~MenuBarHelper();
+
+ // Must be called whenever a button's menu starts showing. It triggers the
+ // MenuBarHelper to start listening for certain events.
+ void MenuStartedShowing(GtkWidget* button, GtkWidget* menu);
+
+ // Add |button| to the set of buttons we care about.
+ void Add(GtkWidget* button);
+
+ // Remove |button| from the set of buttons we care about.
+ void Remove(GtkWidget* button);
+
+ // Clear all buttons from the set.
+ void Clear();
+
+ private:
+ static gboolean OnMenuMotionNotifyThunk(GtkWidget* menu,
+ GdkEventMotion* motion,
+ MenuBarHelper* helper) {
+ return helper->OnMenuMotionNotify(menu, motion);
+ }
+ gboolean OnMenuMotionNotify(GtkWidget* menu, GdkEventMotion* motion);
+
+ static void OnMenuHiddenThunk(GtkWidget* menu, MenuBarHelper* helper) {
+ helper->OnMenuHidden(menu);
+ }
+ void OnMenuHidden(GtkWidget* menu);
+
+ static void OnMenuMoveCurrentThunk(GtkWidget* menu,
+ GtkMenuDirectionType dir,
+ MenuBarHelper* helper) {
+ helper->OnMenuMoveCurrent(menu, dir);
+ }
+ void OnMenuMoveCurrent(GtkWidget* menu, GtkMenuDirectionType dir);
+
+ // The buttons for which we pop up menus. We do not own these, or even add
+ // refs to them.
+ std::vector<GtkWidget*> buttons_;
+
+ // The button that is currently showing a menu, or NULL.
+ GtkWidget* button_showing_menu_;
+
+ // The highest level menu that is currently showing, or NULL.
+ GtkWidget* showing_menu_;
+
+ // All the submenus of |showing_menu_|. We connect to motion events on all
+ // of them.
+ std::vector<GtkWidget*> submenus_;
+
+ Delegate* delegate_;
+};
+
+#endif // CHROME_BROWSER_GTK_MENU_BAR_HELPER_H_