172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "chrome/browser/ui/gtk/bookmarks/bookmark_menu_controller_gtk.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <gtk/gtk.h> 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h" 103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/utf_string_conversions.h" 11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/bookmarks/bookmark_model.h" 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/bookmarks/bookmark_utils.h" 1321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/profiles/profile.h" 14dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "chrome/browser/ui/gtk/bookmarks/bookmark_utils_gtk.h" 1572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/gtk_chrome_button.h" 16ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/gtk/gtk_theme_service.h" 1772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/gtk_util.h" 1872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/menu_gtk.h" 19dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/page_navigator.h" 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/app_resources.h" 21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/generated_resources.h" 22c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/theme_resources.h" 2372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/dragdrop/gtk_dnd_util.h" 2472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/l10n/l10n_util.h" 2572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/gtk_util.h" 26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "webkit/glue/window_open_disposition.h" 27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace { 29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TODO(estade): It might be a good idea to vary this by locale. 31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kMaxChars = 50; 32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid SetImageMenuItem(GtkWidget* menu_item, 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const BookmarkNode* node, 35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BookmarkModel* model) { 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GdkPixbuf* pixbuf = bookmark_utils::GetPixbufForNode(node, model, true); 37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_image_new_from_pixbuf(pixbuf)); 39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_object_unref(pixbuf); 40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst BookmarkNode* GetNodeFromMenuItem(GtkWidget* menu_item) { 43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return static_cast<const BookmarkNode*>( 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_object_get_data(G_OBJECT(menu_item), "bookmark-node")); 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst BookmarkNode* GetParentNodeFromEmptyMenu(GtkWidget* menu) { 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return static_cast<const BookmarkNode*>( 49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_object_get_data(G_OBJECT(menu), "parent-node")); 50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid* AsVoid(const BookmarkNode* node) { 53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return const_cast<BookmarkNode*>(node); 54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The context menu has been dismissed, restore the X and application grabs 57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// to whichever menu last had them. (Assuming that menu is still showing.) 58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid OnContextMenuHide(GtkWidget* context_menu, GtkWidget* grab_menu) { 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_util::GrabAllInput(grab_menu); 60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Match the ref we took when connecting this signal. 62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_object_unref(grab_menu); 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} // namespace 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 67c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBookmarkMenuController::BookmarkMenuController(Browser* browser, 68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch Profile* profile, 69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch PageNavigator* navigator, 70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWindow* window, 71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const BookmarkNode* node, 72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int start_child_index) 73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch : browser_(browser), 74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch profile_(profile), 75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch page_navigator_(navigator), 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch parent_window_(window), 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch model_(profile->GetBookmarkModel()), 78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch node_(node), 79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drag_icon_(NULL), 80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ignore_button_release_(false), 81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch triggering_widget_(NULL) { 82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch menu_ = gtk_menu_new(); 834a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch g_object_ref_sink(menu_); 84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BuildMenu(node, start_child_index, menu_); 854a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch signals_.Connect(menu_, "hide", 86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(OnMenuHiddenThunk), this); 87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_widget_show_all(menu_); 88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 90c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBookmarkMenuController::~BookmarkMenuController() { 91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch profile_->GetBookmarkModel()->RemoveObserver(this); 92201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch // Make sure the hide handler runs. 93201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch gtk_widget_hide(menu_); 944a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch gtk_widget_destroy(menu_); 954a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch g_object_unref(menu_); 96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BookmarkMenuController::Popup(GtkWidget* widget, gint button_type, 99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch guint32 timestamp) { 100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch profile_->GetBookmarkModel()->AddObserver(this); 101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch triggering_widget_ = widget; 1034a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch signals_.Connect(triggering_widget_, "destroy", 1044a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch G_CALLBACK(gtk_widget_destroyed), &triggering_widget_); 105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_chrome_button_set_paint_state(GTK_CHROME_BUTTON(widget), 106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GTK_STATE_ACTIVE); 107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_menu_popup(GTK_MENU(menu_), NULL, NULL, 108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch &MenuGtk::WidgetMenuPositionFunc, 109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch widget, button_type, timestamp); 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BookmarkMenuController::BookmarkModelChanged() { 113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_menu_popdown(GTK_MENU(menu_)); 114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 116ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid BookmarkMenuController::BookmarkNodeFaviconLoaded( 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BookmarkModel* model, const BookmarkNode* node) { 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::map<const BookmarkNode*, GtkWidget*>::iterator it = 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch node_to_menu_widget_map_.find(node); 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (it != node_to_menu_widget_map_.end()) 121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SetImageMenuItem(it->second, node, model); 122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BookmarkMenuController::WillExecuteCommand() { 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_menu_popdown(GTK_MENU(menu_)); 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BookmarkMenuController::CloseMenu() { 129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch context_menu_->Cancel(); 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BookmarkMenuController::NavigateToMenuItem( 133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWidget* menu_item, 134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch WindowOpenDisposition disposition) { 135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const BookmarkNode* node = GetNodeFromMenuItem(menu_item); 136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(node); 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(page_navigator_); 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch page_navigator_->OpenURL( 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch node->GetURL(), GURL(), disposition, PageTransition::AUTO_BOOKMARK); 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BookmarkMenuController::BuildMenu(const BookmarkNode* parent, 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int start_child_index, 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWidget* menu) { 145ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen DCHECK(!parent->child_count() || 146ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen start_child_index < parent->child_count()); 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 1484a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch signals_.Connect(menu, "button-press-event", 149513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch G_CALLBACK(OnMenuButtonPressedOrReleasedThunk), this); 1504a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch signals_.Connect(menu, "button-release-event", 151513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch G_CALLBACK(OnMenuButtonPressedOrReleasedThunk), this); 152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 153ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen for (int i = start_child_index; i < parent->child_count(); ++i) { 154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const BookmarkNode* node = parent->GetChild(i); 155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This breaks on word boundaries. Ideally we would break on character 157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // boundaries. 15821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen string16 elided_name = l10n_util::TruncateString(node->GetTitle(), 15921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen kMaxChars); 160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWidget* menu_item = 16121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen gtk_image_menu_item_new_with_label(UTF16ToUTF8(elided_name).c_str()); 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_object_set_data(G_OBJECT(menu_item), "bookmark-node", AsVoid(node)); 163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch SetImageMenuItem(menu_item, node, profile_->GetBookmarkModel()); 164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_util::SetAlwaysShowImage(menu_item); 165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 1664a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch signals_.Connect(menu_item, "button-release-event", 167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(OnButtonReleasedThunk), this); 168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (node->is_url()) { 1694a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch signals_.Connect(menu_item, "activate", 170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(OnMenuItemActivatedThunk), this); 171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else if (node->is_folder()) { 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWidget* submenu = gtk_menu_new(); 173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch BuildMenu(node, 0, submenu); 174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu); 175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NOTREACHED(); 177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_drag_source_set(menu_item, GDK_BUTTON1_MASK, NULL, 0, 180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_LINK)); 18172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen int target_mask = ui::CHROME_BOOKMARK_ITEM; 182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (node->is_url()) 18372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen target_mask |= ui::TEXT_URI_LIST | ui::NETSCAPE_URL; 18472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen ui::SetSourceTargetListFromCodeMask(menu_item, target_mask); 1854a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch signals_.Connect(menu_item, "drag-begin", 186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(OnMenuItemDragBeginThunk), this); 1874a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch signals_.Connect(menu_item, "drag-end", 188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(OnMenuItemDragEndThunk), this); 1894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch signals_.Connect(menu_item, "drag-data-get", 190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(OnMenuItemDragGetThunk), this); 191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // It is important to connect to this signal after setting up the drag 193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // source because we only want to stifle the menu's default handler and 194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // not the handler that the drag source uses. 195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (node->is_folder()) { 1964a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch signals_.Connect(menu_item, "button-press-event", 197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(OnFolderButtonPressedThunk), this); 198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); 201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch node_to_menu_widget_map_[node] = menu_item; 202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 204ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (parent->child_count() == 0) { 205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWidget* empty_menu = gtk_menu_item_new_with_label( 206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch l10n_util::GetStringUTF8(IDS_MENU_EMPTY_SUBMENU).c_str()); 207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_widget_set_sensitive(empty_menu, FALSE); 208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_object_set_data(G_OBJECT(menu), "parent-node", AsVoid(parent)); 209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_menu_shell_append(GTK_MENU_SHELL(menu), empty_menu); 210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 213513209b27ff55e2841eac0e4120199c23acce758Ben Murdochgboolean BookmarkMenuController::OnMenuButtonPressedOrReleased( 214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWidget* sender, 215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GdkEventButton* event) { 216513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch // Handle middle mouse downs and right mouse ups. 217513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (!((event->button == 2 && event->type == GDK_BUTTON_RELEASE) || 218513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch (event->button == 3 && event->type == GDK_BUTTON_PRESS))) { 219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return FALSE; 220513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch } 221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ignore_button_release_ = false; 223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkMenuShell* menu_shell = GTK_MENU_SHELL(sender); 224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If the cursor is outside our bounds, pass this event up to the parent. 225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!gtk_util::WidgetContainsCursor(sender)) { 226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (menu_shell->parent_menu_shell) { 227513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch return OnMenuButtonPressedOrReleased(menu_shell->parent_menu_shell, 228513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch event); 229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We are the top level menu; we can propagate no further. 231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return FALSE; 232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // This will return NULL if we are not an empty menu. 236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const BookmarkNode* parent = GetParentNodeFromEmptyMenu(sender); 237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bool is_empty_menu = !!parent; 238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // If there is no active menu item and we are not an empty menu, then do 239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // nothing. This can happen if the user has canceled a context menu while 240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the cursor is hovering over a bookmark menu. Doing nothing is not optimal 241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // (the hovered item should be active), but it's a hopefully rare corner 242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // case. 243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWidget* menu_item = menu_shell->active_menu_item; 244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!is_empty_menu && !menu_item) 245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return TRUE; 246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const BookmarkNode* node = 247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch menu_item ? GetNodeFromMenuItem(menu_item) : NULL; 248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 249513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch if (event->button == 2 && node && node->is_folder()) { 250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bookmark_utils::OpenAll(parent_window_, 251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch profile_, page_navigator_, 252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch node, NEW_BACKGROUND_TAB); 253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_menu_popdown(GTK_MENU(menu_)); 254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return TRUE; 255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else if (event->button == 3) { 256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK_NE(is_empty_menu, !!node); 257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!is_empty_menu) 258ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen parent = node->parent(); 259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Show the right click menu and stop processing this button event. 261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::vector<const BookmarkNode*> nodes; 262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (node) 263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch nodes.push_back(node); 264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch context_menu_controller_.reset( 265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch new BookmarkContextMenuController( 266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch parent_window_, this, profile_, 267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch page_navigator_, parent, nodes)); 268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch context_menu_.reset( 269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch new MenuGtk(NULL, context_menu_controller_->menu_model())); 270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Our bookmark folder menu loses the grab to the context menu. When the 272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // context menu is hidden, re-assert our grab. 273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWidget* grabbing_menu = gtk_grab_get_current(); 274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_object_ref(grabbing_menu); 2754a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch signals_.Connect(context_menu_->widget(), "hide", 276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch G_CALLBACK(OnContextMenuHide), grabbing_menu); 277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 27872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen context_menu_->PopupAsContext(gfx::Point(event->x_root, event->y_root), 27972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen event->time); 280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return TRUE; 281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return FALSE; 284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgboolean BookmarkMenuController::OnButtonReleased( 287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWidget* sender, 288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GdkEventButton* event) { 289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (ignore_button_release_) { 290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Don't handle this message; it was a drag. 291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ignore_button_release_ = false; 292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return FALSE; 293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Releasing either button 1 or 2 should trigger the bookmark. 296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (!gtk_menu_item_get_submenu(GTK_MENU_ITEM(sender))) { 297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The menu item is a link node. 298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (event->button == 1 || event->button == 2) { 299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch WindowOpenDisposition disposition = 300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch event_utils::DispositionFromEventFlags(event->state); 301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NavigateToMenuItem(sender, disposition); 302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We need to manually dismiss the popup menu because we're overriding 304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // button-release-event. 305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_menu_popdown(GTK_MENU(menu_)); 306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return TRUE; 307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } else { 309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The menu item is a folder node. 310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (event->button == 1) { 311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Having overriden the normal handling, we need to manually activate 312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // the item. 313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_menu_shell_select_item(GTK_MENU_SHELL(sender->parent), sender); 314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_signal_emit_by_name(sender->parent, "activate-current"); 315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return TRUE; 316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return FALSE; 320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgboolean BookmarkMenuController::OnFolderButtonPressed( 323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWidget* sender, GdkEventButton* event) { 324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The button press may start a drag; don't let the default handler run. 325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (event->button == 1) 326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return TRUE; 327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return FALSE; 328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BookmarkMenuController::OnMenuHidden(GtkWidget* menu) { 331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (triggering_widget_) 332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_chrome_button_unset_paint_state(GTK_CHROME_BUTTON(triggering_widget_)); 333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BookmarkMenuController::OnMenuItemActivated(GtkWidget* menu_item) { 336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch NavigateToMenuItem(menu_item, CURRENT_TAB); 337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BookmarkMenuController::OnMenuItemDragBegin(GtkWidget* menu_item, 340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GdkDragContext* drag_context) { 341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The parent menu item might be removed during the drag. Ref it so |button| 342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // won't get destroyed. 343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_object_ref(menu_item->parent); 344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Signal to any future OnButtonReleased calls that we're dragging instead of 346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // pressing. 347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ignore_button_release_ = true; 348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const BookmarkNode* node = bookmark_utils::BookmarkNodeForWidget(menu_item); 350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drag_icon_ = bookmark_utils::GetDragRepresentationForNode( 351ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen node, model_, GtkThemeService::GetFrom(profile_)); 352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gint x, y; 353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_widget_get_pointer(menu_item, &x, &y); 354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_drag_set_icon_widget(drag_context, drag_icon_, x, y); 355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // Hide our node. 357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_widget_hide(menu_item); 358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BookmarkMenuController::OnMenuItemDragEnd(GtkWidget* menu_item, 361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GdkDragContext* drag_context) { 362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_widget_show(menu_item); 363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch g_object_unref(menu_item->parent); 364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch gtk_widget_destroy(drag_icon_); 366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch drag_icon_ = NULL; 367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid BookmarkMenuController::OnMenuItemDragGet( 370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkWidget* widget, GdkDragContext* context, 371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch GtkSelectionData* selection_data, 372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch guint target_type, guint time) { 373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const BookmarkNode* node = bookmark_utils::BookmarkNodeForWidget(widget); 374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bookmark_utils::WriteBookmarkToSelection(node, selection_data, target_type, 375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch profile_); 376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 377