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

Side by Side Diff: chrome/browser/ui/gtk/bookmarks/bookmark_menu_controller_gtk.cc

Issue 231733005: Delete the GTK+ port of Chrome. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remerge to ToT Created 6 years, 8 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) 2012 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/ui/gtk/bookmarks/bookmark_menu_controller_gtk.h"
6
7 #include <gtk/gtk.h>
8
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/bookmarks/bookmark_model.h"
12 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
13 #include "chrome/browser/bookmarks/bookmark_stats.h"
14 #include "chrome/browser/bookmarks/bookmark_utils.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/gtk/bookmarks/bookmark_utils_gtk.h"
19 #include "chrome/browser/ui/gtk/event_utils.h"
20 #include "chrome/browser/ui/gtk/gtk_chrome_button.h"
21 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
22 #include "chrome/browser/ui/gtk/gtk_util.h"
23 #include "chrome/browser/ui/gtk/menu_gtk.h"
24 #include "content/public/browser/page_navigator.h"
25 #include "grit/generated_resources.h"
26 #include "grit/theme_resources.h"
27 #include "grit/ui_resources.h"
28 #include "ui/base/dragdrop/gtk_dnd_util.h"
29 #include "ui/base/l10n/l10n_util.h"
30 #include "ui/base/window_open_disposition.h"
31 #include "ui/gfx/gtk_util.h"
32
33 using content::OpenURLParams;
34 using content::PageNavigator;
35
36 namespace {
37
38 void SetImageMenuItem(GtkWidget* menu_item,
39 const BookmarkNode* node,
40 BookmarkModel* model) {
41 GdkPixbuf* pixbuf = GetPixbufForNode(node, model, true);
42 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),
43 gtk_image_new_from_pixbuf(pixbuf));
44 g_object_unref(pixbuf);
45 }
46
47 const BookmarkNode* GetNodeFromMenuItem(GtkWidget* menu_item) {
48 return static_cast<const BookmarkNode*>(
49 g_object_get_data(G_OBJECT(menu_item), "bookmark-node"));
50 }
51
52 const BookmarkNode* GetParentNodeFromEmptyMenu(GtkWidget* menu) {
53 return static_cast<const BookmarkNode*>(
54 g_object_get_data(G_OBJECT(menu), "parent-node"));
55 }
56
57 void* AsVoid(const BookmarkNode* node) {
58 return const_cast<BookmarkNode*>(node);
59 }
60
61 // The context menu has been dismissed, restore the X and application grabs
62 // to whichever menu last had them. (Assuming that menu is still showing.)
63 void OnContextMenuHide(GtkWidget* context_menu, GtkWidget* grab_menu) {
64 gtk_util::GrabAllInput(grab_menu);
65
66 // Match the ref we took when connecting this signal.
67 g_object_unref(grab_menu);
68 }
69
70 } // namespace
71
72 BookmarkMenuController::BookmarkMenuController(Browser* browser,
73 PageNavigator* navigator,
74 GtkWindow* window,
75 const BookmarkNode* node,
76 int start_child_index)
77 : browser_(browser),
78 page_navigator_(navigator),
79 parent_window_(window),
80 model_(BookmarkModelFactory::GetForProfile(browser->profile())),
81 node_(node),
82 drag_icon_(NULL),
83 ignore_button_release_(false),
84 triggering_widget_(NULL) {
85 menu_ = gtk_menu_new();
86 g_object_ref_sink(menu_);
87 BuildMenu(node, start_child_index, menu_);
88 signals_.Connect(menu_, "hide", G_CALLBACK(OnMenuHiddenThunk), this);
89 gtk_widget_show_all(menu_);
90 }
91
92 BookmarkMenuController::~BookmarkMenuController() {
93 model_->RemoveObserver(this);
94 // Make sure the hide handler runs.
95 gtk_widget_hide(menu_);
96 gtk_widget_destroy(menu_);
97 g_object_unref(menu_);
98 }
99
100 void BookmarkMenuController::Popup(GtkWidget* widget, gint button_type,
101 guint32 timestamp) {
102 model_->AddObserver(this);
103
104 triggering_widget_ = widget;
105 signals_.Connect(triggering_widget_, "destroy",
106 G_CALLBACK(gtk_widget_destroyed), &triggering_widget_);
107 gtk_chrome_button_set_paint_state(GTK_CHROME_BUTTON(widget),
108 GTK_STATE_ACTIVE);
109 gtk_menu_popup(GTK_MENU(menu_), NULL, NULL,
110 &MenuGtk::WidgetMenuPositionFunc,
111 widget, button_type, timestamp);
112 }
113
114 void BookmarkMenuController::BookmarkModelChanged() {
115 gtk_menu_popdown(GTK_MENU(menu_));
116 }
117
118 void BookmarkMenuController::BookmarkNodeFaviconChanged(
119 BookmarkModel* model, const BookmarkNode* node) {
120 std::map<const BookmarkNode*, GtkWidget*>::iterator it =
121 node_to_menu_widget_map_.find(node);
122 if (it != node_to_menu_widget_map_.end())
123 SetImageMenuItem(it->second, node, model);
124 }
125
126 void BookmarkMenuController::WillExecuteCommand(
127 int command_id,
128 const std::vector<const BookmarkNode*>& bookmarks) {
129 gtk_menu_popdown(GTK_MENU(menu_));
130 }
131
132 void BookmarkMenuController::CloseMenu() {
133 context_menu_->Cancel();
134 }
135
136 void BookmarkMenuController::NavigateToMenuItem(
137 GtkWidget* menu_item,
138 WindowOpenDisposition disposition) {
139 const BookmarkNode* node = GetNodeFromMenuItem(menu_item);
140 DCHECK(node);
141 DCHECK(page_navigator_);
142 RecordBookmarkLaunch(node, BOOKMARK_LAUNCH_LOCATION_BAR_SUBFOLDER);
143 page_navigator_->OpenURL(OpenURLParams(
144 node->url(), content::Referrer(), disposition,
145 content::PAGE_TRANSITION_AUTO_BOOKMARK, false));
146 }
147
148 void BookmarkMenuController::BuildMenu(const BookmarkNode* parent,
149 int start_child_index,
150 GtkWidget* menu) {
151 DCHECK(parent->empty() || start_child_index < parent->child_count());
152
153 signals_.Connect(menu, "button-press-event",
154 G_CALLBACK(OnMenuButtonPressedOrReleasedThunk), this);
155 signals_.Connect(menu, "button-release-event",
156 G_CALLBACK(OnMenuButtonPressedOrReleasedThunk), this);
157
158 for (int i = start_child_index; i < parent->child_count(); ++i) {
159 const BookmarkNode* node = parent->GetChild(i);
160
161 GtkWidget* menu_item =
162 gtk_image_menu_item_new_with_label(BuildMenuLabelFor(node).c_str());
163 g_object_set_data(G_OBJECT(menu_item), "bookmark-node", AsVoid(node));
164 SetImageMenuItem(menu_item, node, model_);
165 gtk_util::SetAlwaysShowImage(menu_item);
166
167 signals_.Connect(menu_item, "button-release-event",
168 G_CALLBACK(OnButtonReleasedThunk), this);
169 if (node->is_url()) {
170 signals_.Connect(menu_item, "activate",
171 G_CALLBACK(OnMenuItemActivatedThunk), this);
172 } else if (node->is_folder()) {
173 GtkWidget* submenu = gtk_menu_new();
174 BuildMenu(node, 0, submenu);
175 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu);
176 } else {
177 NOTREACHED();
178 }
179
180 gtk_drag_source_set(menu_item, GDK_BUTTON1_MASK, NULL, 0,
181 static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_LINK));
182 int target_mask = ui::CHROME_BOOKMARK_ITEM;
183 if (node->is_url())
184 target_mask |= ui::TEXT_URI_LIST | ui::NETSCAPE_URL;
185 ui::SetSourceTargetListFromCodeMask(menu_item, target_mask);
186 signals_.Connect(menu_item, "drag-begin",
187 G_CALLBACK(OnMenuItemDragBeginThunk), this);
188 signals_.Connect(menu_item, "drag-end",
189 G_CALLBACK(OnMenuItemDragEndThunk), this);
190 signals_.Connect(menu_item, "drag-data-get",
191 G_CALLBACK(OnMenuItemDragGetThunk), this);
192
193 // It is important to connect to this signal after setting up the drag
194 // source because we only want to stifle the menu's default handler and
195 // not the handler that the drag source uses.
196 if (node->is_folder()) {
197 signals_.Connect(menu_item, "button-press-event",
198 G_CALLBACK(OnFolderButtonPressedThunk), this);
199 }
200
201 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
202 node_to_menu_widget_map_[node] = menu_item;
203 }
204
205 if (parent->empty()) {
206 GtkWidget* empty_menu = gtk_menu_item_new_with_label(
207 l10n_util::GetStringUTF8(IDS_MENU_EMPTY_SUBMENU).c_str());
208 gtk_widget_set_sensitive(empty_menu, FALSE);
209 g_object_set_data(G_OBJECT(menu), "parent-node", AsVoid(parent));
210 gtk_menu_shell_append(GTK_MENU_SHELL(menu), empty_menu);
211 }
212 }
213
214 gboolean BookmarkMenuController::OnMenuButtonPressedOrReleased(
215 GtkWidget* sender,
216 GdkEventButton* event) {
217 // Handle middle mouse downs and right mouse ups.
218 if (!((event->button == 2 && event->type == GDK_BUTTON_RELEASE) ||
219 (event->button == 3 && event->type == GDK_BUTTON_PRESS))) {
220 return FALSE;
221 }
222
223 ignore_button_release_ = false;
224 GtkMenuShell* menu_shell = GTK_MENU_SHELL(sender);
225 // If the cursor is outside our bounds, pass this event up to the parent.
226 if (!gtk_util::WidgetContainsCursor(sender)) {
227 if (menu_shell->parent_menu_shell) {
228 return OnMenuButtonPressedOrReleased(menu_shell->parent_menu_shell,
229 event);
230 } else {
231 // We are the top level menu; we can propagate no further.
232 return FALSE;
233 }
234 }
235
236 // This will return NULL if we are not an empty menu.
237 const BookmarkNode* parent = GetParentNodeFromEmptyMenu(sender);
238 bool is_empty_menu = !!parent;
239 // If there is no active menu item and we are not an empty menu, then do
240 // nothing. This can happen if the user has canceled a context menu while
241 // the cursor is hovering over a bookmark menu. Doing nothing is not optimal
242 // (the hovered item should be active), but it's a hopefully rare corner
243 // case.
244 GtkWidget* menu_item = menu_shell->active_menu_item;
245 if (!is_empty_menu && !menu_item)
246 return TRUE;
247 const BookmarkNode* node =
248 menu_item ? GetNodeFromMenuItem(menu_item) : NULL;
249
250 if (event->button == 2 && node && node->is_folder()) {
251 chrome::OpenAll(parent_window_, page_navigator_, node, NEW_BACKGROUND_TAB,
252 browser_->profile());
253 gtk_menu_popdown(GTK_MENU(menu_));
254 return TRUE;
255 } else if (event->button == 3) {
256 DCHECK_NE(is_empty_menu, !!node);
257 if (!is_empty_menu)
258 parent = node->parent();
259
260 // Show the right click menu and stop processing this button event.
261 std::vector<const BookmarkNode*> nodes;
262 if (node)
263 nodes.push_back(node);
264 context_menu_controller_.reset(
265 new BookmarkContextMenuController(
266 parent_window_, this, browser_, browser_->profile(),
267 page_navigator_, parent, nodes));
268 context_menu_.reset(
269 new MenuGtk(NULL, context_menu_controller_->menu_model()));
270
271 // Our bookmark folder menu loses the grab to the context menu. When the
272 // context menu is hidden, re-assert our grab.
273 GtkWidget* grabbing_menu = gtk_grab_get_current();
274 g_object_ref(grabbing_menu);
275 signals_.Connect(context_menu_->widget(), "hide",
276 G_CALLBACK(OnContextMenuHide), grabbing_menu);
277
278 context_menu_->PopupAsContext(gfx::Point(event->x_root, event->y_root),
279 event->time);
280 return TRUE;
281 }
282
283 return FALSE;
284 }
285
286 gboolean BookmarkMenuController::OnButtonReleased(
287 GtkWidget* sender,
288 GdkEventButton* event) {
289 if (ignore_button_release_) {
290 // Don't handle this message; it was a drag.
291 ignore_button_release_ = false;
292 return FALSE;
293 }
294
295 // Releasing either button 1 or 2 should trigger the bookmark.
296 if (!gtk_menu_item_get_submenu(GTK_MENU_ITEM(sender))) {
297 // The menu item is a link node.
298 if (event->button == 1 || event->button == 2) {
299 WindowOpenDisposition disposition =
300 event_utils::DispositionFromGdkState(event->state);
301
302 NavigateToMenuItem(sender, disposition);
303
304 // We need to manually dismiss the popup menu because we're overriding
305 // button-release-event.
306 gtk_menu_popdown(GTK_MENU(menu_));
307 return TRUE;
308 }
309 } else {
310 // The menu item is a folder node.
311 if (event->button == 1) {
312 // Having overriden the normal handling, we need to manually activate
313 // the item.
314 gtk_menu_shell_select_item(GTK_MENU_SHELL(sender->parent), sender);
315 g_signal_emit_by_name(sender->parent, "activate-current");
316 return TRUE;
317 }
318 }
319
320 return FALSE;
321 }
322
323 gboolean BookmarkMenuController::OnFolderButtonPressed(
324 GtkWidget* sender, GdkEventButton* event) {
325 // The button press may start a drag; don't let the default handler run.
326 if (event->button == 1)
327 return TRUE;
328 return FALSE;
329 }
330
331 void BookmarkMenuController::OnMenuHidden(GtkWidget* menu) {
332 if (triggering_widget_)
333 gtk_chrome_button_unset_paint_state(GTK_CHROME_BUTTON(triggering_widget_));
334 }
335
336 void BookmarkMenuController::OnMenuItemActivated(GtkWidget* menu_item) {
337 NavigateToMenuItem(menu_item, CURRENT_TAB);
338 }
339
340 void BookmarkMenuController::OnMenuItemDragBegin(GtkWidget* menu_item,
341 GdkDragContext* drag_context) {
342 // The parent menu item might be removed during the drag. Ref it so |button|
343 // won't get destroyed.
344 g_object_ref(menu_item->parent);
345
346 // Signal to any future OnButtonReleased calls that we're dragging instead of
347 // pressing.
348 ignore_button_release_ = true;
349
350 const BookmarkNode* node = BookmarkNodeForWidget(menu_item);
351 drag_icon_ = GetDragRepresentationForNode(
352 node, model_, GtkThemeService::GetFrom(browser_->profile()));
353 gint x, y;
354 gtk_widget_get_pointer(menu_item, &x, &y);
355 gtk_drag_set_icon_widget(drag_context, drag_icon_, x, y);
356
357 // Hide our node.
358 gtk_widget_hide(menu_item);
359 }
360
361 void BookmarkMenuController::OnMenuItemDragEnd(GtkWidget* menu_item,
362 GdkDragContext* drag_context) {
363 gtk_widget_show(menu_item);
364 g_object_unref(menu_item->parent);
365
366 gtk_widget_destroy(drag_icon_);
367 drag_icon_ = NULL;
368 }
369
370 void BookmarkMenuController::OnMenuItemDragGet(GtkWidget* widget,
371 GdkDragContext* context,
372 GtkSelectionData* selection_data,
373 guint target_type,
374 guint time) {
375 const BookmarkNode* node = BookmarkNodeForWidget(widget);
376 WriteBookmarkToSelection(
377 node, selection_data, target_type, browser_->profile());
378 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698