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

Side by Side Diff: chrome/browser/gtk/menu_bar_helper.cc

Issue 200029: Make the bookmark toolbar folders act like a menu bar.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: lint Created 11 years, 3 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/gtk/menu_bar_helper.h"
6
7 #include "base/logging.h"
8 #include "chrome/common/gtk_util.h"
9
10 namespace {
11
12 // Recursively find all the GtkMenus that are attached to menu item |child|
13 // and add them to |data|, which is a vector of GtkWidgets.
14 void PopulateSubmenus(GtkWidget* child, gpointer data) {
15 std::vector<GtkWidget*>* submenus =
16 static_cast<std::vector<GtkWidget*>*>(data);
17 GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(child));
18 if (submenu) {
19 submenus->push_back(submenu);
20 gtk_container_foreach(GTK_CONTAINER(submenu), PopulateSubmenus, submenus);
21 }
22 }
23
24 // Is the cursor over |menu| or one of its parent menus?
25 bool MotionIsOverMenu(GtkWidget* menu, GdkEventMotion* motion) {
26 if (motion->x >= 0 && motion->y >= 0 &&
27 motion->x < menu->allocation.width &&
28 motion->y < menu->allocation.height) {
29 return true;
30 }
31
32 while (menu) {
33 GtkWidget* menu_item = gtk_menu_get_attach_widget(GTK_MENU(menu));
34 if (!menu_item)
35 return false;
36 GtkWidget* parent = gtk_widget_get_parent(menu_item);
37
38 if (gtk_util::WidgetContainsCursor(parent))
39 return true;
40 menu = parent;
41 }
42
43 return false;
44 }
45
46 } // namespace
47
48 MenuBarHelper::MenuBarHelper(Delegate* delegate)
49 : button_showing_menu_(NULL),
50 showing_menu_(NULL),
51 delegate_(delegate) {
52 DCHECK(delegate_);
53 }
54
55 MenuBarHelper::~MenuBarHelper() {
56 }
57
58 void MenuBarHelper::Add(GtkWidget* button) {
59 buttons_.push_back(button);
60 }
61
62 void MenuBarHelper::Remove(GtkWidget* button) {
63 std::vector<GtkWidget*>::iterator iter =
64 find(buttons_.begin(), buttons_.end(), button);
65 if (iter == buttons_.end()) {
66 NOTREACHED();
67 return;
68 }
69 buttons_.erase(iter);
70 }
71
72 void MenuBarHelper::Clear() {
73 buttons_.clear();
74 }
75
76 void MenuBarHelper::MenuStartedShowing(GtkWidget* button, GtkWidget* menu) {
77 DCHECK(GTK_IS_MENU(menu));
78 button_showing_menu_ = button;
79 showing_menu_ = menu;
80 g_signal_connect(menu, "motion-notify-event",
81 G_CALLBACK(OnMenuMotionNotifyThunk), this);
82 g_signal_connect(menu, "hide",
83 G_CALLBACK(OnMenuHiddenThunk), this);
84 g_signal_connect(menu, "move-current",
85 G_CALLBACK(OnMenuMoveCurrentThunk), this);
86 gtk_container_foreach(GTK_CONTAINER(menu), PopulateSubmenus, &submenus_);
87 for (size_t i = 0; i < submenus_.size(); ++i) {
88 g_signal_connect(submenus_[i], "motion-notify-event",
89 G_CALLBACK(OnMenuMotionNotifyThunk), this);
90 }
91 }
92
93 gboolean MenuBarHelper::OnMenuMotionNotify(GtkWidget* menu,
94 GdkEventMotion* motion) {
95 // Don't do anything if pointer is in the menu.
96 if (MotionIsOverMenu(menu, motion))
97 return FALSE;
98 if (buttons_.empty())
99 return FALSE;
100
101 gint x = 0;
102 gint y = 0;
103 GtkWidget* last_button = NULL;
104
105 for (size_t i = 0; i < buttons_.size(); ++i) {
106 GtkWidget* button = buttons_[i];
107 // Figure out coordinates relative to this button. Avoid using
108 // gtk_widget_get_pointer() unnecessarily.
109 if (i == 0) {
110 // We have to make this call because the menu is a popup window, so it
111 // doesn't share a toplevel with the buttons and we can't just use
112 // gtk_widget_translate_coordinates().
113 gtk_widget_get_pointer(buttons_[0], &x, &y);
114 } else {
115 gint last_x = x;
116 gint last_y = y;
117 if (!gtk_widget_translate_coordinates(
118 last_button, button, last_x, last_y, &x, &y)) {
119 NOTREACHED();
120 return FALSE;
121 }
122 }
123
124 last_button = button;
125
126 if (x >= 0 && y >= 0 && x < button->allocation.width &&
127 y < button->allocation.height) {
128 if (button != button_showing_menu_)
129 delegate_->PopupForButton(button);
130 return TRUE;
131 }
132 }
133
134 return FALSE;
135 }
136
137 void MenuBarHelper::OnMenuHidden(GtkWidget* menu) {
138 DCHECK_EQ(showing_menu_, menu);
139 int matched = g_signal_handlers_disconnect_matched(showing_menu_,
140 G_SIGNAL_MATCH_DATA, 0, NULL, NULL, NULL, this);
141 DCHECK_EQ(3, matched);
142
143 for (size_t i = 0; i < submenus_.size(); ++i) {
144 g_signal_handlers_disconnect_by_func(submenus_[i],
145 reinterpret_cast<gpointer>(OnMenuMotionNotifyThunk), this);
146 }
147 showing_menu_ = NULL;
148 button_showing_menu_ = NULL;
149 submenus_.clear();
150 }
151
152 void MenuBarHelper::OnMenuMoveCurrent(GtkWidget* menu,
153 GtkMenuDirectionType dir) {
154 // The menu directions are triggered by the arrow keys as follows
155 //
156 // PARENT left
157 // CHILD right
158 // NEXT down
159 // PREV up
160 //
161 // We only care about left and right. Note that for RTL, they are swapped.
162 switch (dir) {
163 case GTK_MENU_DIR_CHILD: {
164 GtkWidget* active_item = GTK_MENU_SHELL(menu)->active_menu_item;
165 // The move is going to open a submenu; don't override default behavior.
166 if (active_item && gtk_menu_item_get_submenu(GTK_MENU_ITEM(active_item)))
167 return;
168 // Fall through.
169 }
170 case GTK_MENU_DIR_PARENT: {
171 delegate_->PopupForButtonNextTo(button_showing_menu_, dir);
172 break;
173 }
174 default:
175 return;
176 }
177
178 // This signal doesn't have a return value; we have to manually stop its
179 // propagation.
180 g_signal_stop_emission_by_name(menu, "move-current");
181 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698