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

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

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

Powered by Google App Engine
This is Rietveld 408576698