15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/bookmarks/bookmark_menu_controller_gtk.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <gtk/gtk.h> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 9868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h" 10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/bookmarks/bookmark_model.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/bookmarks/bookmark_model_factory.h" 130f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)#include "chrome/browser/bookmarks/bookmark_stats.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/bookmarks/bookmark_utils.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/bookmarks/bookmark_utils.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/browser.h" 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/bookmarks/bookmark_utils_gtk.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/event_utils.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/gtk_chrome_button.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/gtk_theme_service.h" 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/gtk_util.h" 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/gtk/menu_gtk.h" 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/page_navigator.h" 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/generated_resources.h" 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/theme_resources.h" 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/ui_resources.h" 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/dragdrop/gtk_dnd_util.h" 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h" 302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/base/window_open_disposition.h" 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/gtk_util.h" 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::OpenURLParams; 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::PageNavigator; 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace { 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SetImageMenuItem(GtkWidget* menu_item, 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const BookmarkNode* node, 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BookmarkModel* model) { 413551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) GdkPixbuf* pixbuf = GetPixbufForNode(node, model, true); 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_image_new_from_pixbuf(pixbuf)); 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_object_unref(pixbuf); 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const BookmarkNode* GetNodeFromMenuItem(GtkWidget* menu_item) { 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return static_cast<const BookmarkNode*>( 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_object_get_data(G_OBJECT(menu_item), "bookmark-node")); 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const BookmarkNode* GetParentNodeFromEmptyMenu(GtkWidget* menu) { 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return static_cast<const BookmarkNode*>( 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_object_get_data(G_OBJECT(menu), "parent-node")); 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void* AsVoid(const BookmarkNode* node) { 585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return const_cast<BookmarkNode*>(node); 595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The context menu has been dismissed, restore the X and application grabs 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// to whichever menu last had them. (Assuming that menu is still showing.) 635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void OnContextMenuHide(GtkWidget* context_menu, GtkWidget* grab_menu) { 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_util::GrabAllInput(grab_menu); 655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Match the ref we took when connecting this signal. 675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_object_unref(grab_menu); 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace 715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BookmarkMenuController::BookmarkMenuController(Browser* browser, 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PageNavigator* navigator, 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GtkWindow* window, 755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const BookmarkNode* node, 765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int start_child_index) 775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) : browser_(browser), 785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) page_navigator_(navigator), 795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parent_window_(window), 805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) model_(BookmarkModelFactory::GetForProfile(browser->profile())), 815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) node_(node), 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) drag_icon_(NULL), 835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ignore_button_release_(false), 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) triggering_widget_(NULL) { 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_ = gtk_menu_new(); 865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_object_ref_sink(menu_); 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BuildMenu(node, start_child_index, menu_); 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) signals_.Connect(menu_, "hide", G_CALLBACK(OnMenuHiddenThunk), this); 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_widget_show_all(menu_); 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)BookmarkMenuController::~BookmarkMenuController() { 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) model_->RemoveObserver(this); 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Make sure the hide handler runs. 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_widget_hide(menu_); 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_widget_destroy(menu_); 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_object_unref(menu_); 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkMenuController::Popup(GtkWidget* widget, gint button_type, 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) guint32 timestamp) { 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) model_->AddObserver(this); 1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) triggering_widget_ = widget; 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) signals_.Connect(triggering_widget_, "destroy", 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) G_CALLBACK(gtk_widget_destroyed), &triggering_widget_); 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_chrome_button_set_paint_state(GTK_CHROME_BUTTON(widget), 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GTK_STATE_ACTIVE); 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_menu_popup(GTK_MENU(menu_), NULL, NULL, 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) &MenuGtk::WidgetMenuPositionFunc, 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) widget, button_type, timestamp); 1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkMenuController::BookmarkModelChanged() { 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_menu_popdown(GTK_MENU(menu_)); 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkMenuController::BookmarkNodeFaviconChanged( 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BookmarkModel* model, const BookmarkNode* node) { 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::map<const BookmarkNode*, GtkWidget*>::iterator it = 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) node_to_menu_widget_map_.find(node); 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (it != node_to_menu_widget_map_.end()) 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetImageMenuItem(it->second, node, model); 1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void BookmarkMenuController::WillExecuteCommand( 1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int command_id, 1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const std::vector<const BookmarkNode*>& bookmarks) { 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_menu_popdown(GTK_MENU(menu_)); 1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkMenuController::CloseMenu() { 1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) context_menu_->Cancel(); 1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkMenuController::NavigateToMenuItem( 1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GtkWidget* menu_item, 1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) WindowOpenDisposition disposition) { 1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const BookmarkNode* node = GetNodeFromMenuItem(menu_item); 1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(node); 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(page_navigator_); 1420f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles) RecordBookmarkLaunch(node, BOOKMARK_LAUNCH_LOCATION_BAR_SUBFOLDER); 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) page_navigator_->OpenURL(OpenURLParams( 1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) node->url(), content::Referrer(), disposition, 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) content::PAGE_TRANSITION_AUTO_BOOKMARK, false)); 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkMenuController::BuildMenu(const BookmarkNode* parent, 1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int start_child_index, 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GtkWidget* menu) { 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK(parent->empty() || start_child_index < parent->child_count()); 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) signals_.Connect(menu, "button-press-event", 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) G_CALLBACK(OnMenuButtonPressedOrReleasedThunk), this); 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) signals_.Connect(menu, "button-release-event", 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) G_CALLBACK(OnMenuButtonPressedOrReleasedThunk), this); 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (int i = start_child_index; i < parent->child_count(); ++i) { 1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const BookmarkNode* node = parent->GetChild(i); 1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1613551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) GtkWidget* menu_item = 1623551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) gtk_image_menu_item_new_with_label(BuildMenuLabelFor(node).c_str()); 1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_object_set_data(G_OBJECT(menu_item), "bookmark-node", AsVoid(node)); 1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetImageMenuItem(menu_item, node, model_); 1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_util::SetAlwaysShowImage(menu_item); 1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) signals_.Connect(menu_item, "button-release-event", 1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) G_CALLBACK(OnButtonReleasedThunk), this); 1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (node->is_url()) { 1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) signals_.Connect(menu_item, "activate", 1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) G_CALLBACK(OnMenuItemActivatedThunk), this); 1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (node->is_folder()) { 1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GtkWidget* submenu = gtk_menu_new(); 1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) BuildMenu(node, 0, submenu); 1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu); 1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NOTREACHED(); 1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_drag_source_set(menu_item, GDK_BUTTON1_MASK, NULL, 0, 1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_LINK)); 1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int target_mask = ui::CHROME_BOOKMARK_ITEM; 1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (node->is_url()) 1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) target_mask |= ui::TEXT_URI_LIST | ui::NETSCAPE_URL; 1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ui::SetSourceTargetListFromCodeMask(menu_item, target_mask); 1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) signals_.Connect(menu_item, "drag-begin", 1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) G_CALLBACK(OnMenuItemDragBeginThunk), this); 1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) signals_.Connect(menu_item, "drag-end", 1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) G_CALLBACK(OnMenuItemDragEndThunk), this); 1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) signals_.Connect(menu_item, "drag-data-get", 1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) G_CALLBACK(OnMenuItemDragGetThunk), this); 1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // It is important to connect to this signal after setting up the drag 1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // source because we only want to stifle the menu's default handler and 1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // not the handler that the drag source uses. 1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (node->is_folder()) { 1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) signals_.Connect(menu_item, "button-press-event", 1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) G_CALLBACK(OnFolderButtonPressedThunk), this); 1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); 2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) node_to_menu_widget_map_[node] = menu_item; 2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (parent->empty()) { 2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GtkWidget* empty_menu = gtk_menu_item_new_with_label( 2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) l10n_util::GetStringUTF8(IDS_MENU_EMPTY_SUBMENU).c_str()); 2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_widget_set_sensitive(empty_menu, FALSE); 2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_object_set_data(G_OBJECT(menu), "parent-node", AsVoid(parent)); 2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_menu_shell_append(GTK_MENU_SHELL(menu), empty_menu); 2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gboolean BookmarkMenuController::OnMenuButtonPressedOrReleased( 2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GtkWidget* sender, 2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GdkEventButton* event) { 2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Handle middle mouse downs and right mouse ups. 2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!((event->button == 2 && event->type == GDK_BUTTON_RELEASE) || 2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (event->button == 3 && event->type == GDK_BUTTON_PRESS))) { 2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FALSE; 2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ignore_button_release_ = false; 2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GtkMenuShell* menu_shell = GTK_MENU_SHELL(sender); 2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the cursor is outside our bounds, pass this event up to the parent. 2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!gtk_util::WidgetContainsCursor(sender)) { 2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (menu_shell->parent_menu_shell) { 2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return OnMenuButtonPressedOrReleased(menu_shell->parent_menu_shell, 2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) event); 2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We are the top level menu; we can propagate no further. 2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FALSE; 2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // This will return NULL if we are not an empty menu. 2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const BookmarkNode* parent = GetParentNodeFromEmptyMenu(sender); 2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) bool is_empty_menu = !!parent; 2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If there is no active menu item and we are not an empty menu, then do 2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // nothing. This can happen if the user has canceled a context menu while 2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the cursor is hovering over a bookmark menu. Doing nothing is not optimal 2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // (the hovered item should be active), but it's a hopefully rare corner 2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // case. 2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GtkWidget* menu_item = menu_shell->active_menu_item; 2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!is_empty_menu && !menu_item) 2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return TRUE; 2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const BookmarkNode* node = 2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) menu_item ? GetNodeFromMenuItem(menu_item) : NULL; 2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (event->button == 2 && node && node->is_folder()) { 2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) chrome::OpenAll(parent_window_, page_navigator_, node, NEW_BACKGROUND_TAB, 2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) browser_->profile()); 2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_menu_popdown(GTK_MENU(menu_)); 2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return TRUE; 2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (event->button == 3) { 2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) DCHECK_NE(is_empty_menu, !!node); 2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!is_empty_menu) 2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parent = node->parent(); 2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Show the right click menu and stop processing this button event. 2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<const BookmarkNode*> nodes; 2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (node) 2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) nodes.push_back(node); 2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) context_menu_controller_.reset( 2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new BookmarkContextMenuController( 2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) parent_window_, this, browser_, browser_->profile(), 2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) page_navigator_, parent, nodes)); 2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) context_menu_.reset( 2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) new MenuGtk(NULL, context_menu_controller_->menu_model())); 2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Our bookmark folder menu loses the grab to the context menu. When the 2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // context menu is hidden, re-assert our grab. 2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GtkWidget* grabbing_menu = gtk_grab_get_current(); 2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_object_ref(grabbing_menu); 2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) signals_.Connect(context_menu_->widget(), "hide", 2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) G_CALLBACK(OnContextMenuHide), grabbing_menu); 2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) context_menu_->PopupAsContext(gfx::Point(event->x_root, event->y_root), 2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) event->time); 2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return TRUE; 2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FALSE; 2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gboolean BookmarkMenuController::OnButtonReleased( 2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GtkWidget* sender, 2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GdkEventButton* event) { 2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (ignore_button_release_) { 2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Don't handle this message; it was a drag. 2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ignore_button_release_ = false; 2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FALSE; 2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Releasing either button 1 or 2 should trigger the bookmark. 2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!gtk_menu_item_get_submenu(GTK_MENU_ITEM(sender))) { 2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The menu item is a link node. 2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (event->button == 1 || event->button == 2) { 2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) WindowOpenDisposition disposition = 3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) event_utils::DispositionFromGdkState(event->state); 3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NavigateToMenuItem(sender, disposition); 3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We need to manually dismiss the popup menu because we're overriding 3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // button-release-event. 3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_menu_popdown(GTK_MENU(menu_)); 3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return TRUE; 3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The menu item is a folder node. 3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (event->button == 1) { 3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Having overriden the normal handling, we need to manually activate 3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // the item. 3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_menu_shell_select_item(GTK_MENU_SHELL(sender->parent), sender); 3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_signal_emit_by_name(sender->parent, "activate-current"); 3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return TRUE; 3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FALSE; 3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gboolean BookmarkMenuController::OnFolderButtonPressed( 3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GtkWidget* sender, GdkEventButton* event) { 3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The button press may start a drag; don't let the default handler run. 3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (event->button == 1) 3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return TRUE; 3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return FALSE; 3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkMenuController::OnMenuHidden(GtkWidget* menu) { 3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (triggering_widget_) 3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_chrome_button_unset_paint_state(GTK_CHROME_BUTTON(triggering_widget_)); 3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkMenuController::OnMenuItemActivated(GtkWidget* menu_item) { 3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) NavigateToMenuItem(menu_item, CURRENT_TAB); 3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkMenuController::OnMenuItemDragBegin(GtkWidget* menu_item, 3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GdkDragContext* drag_context) { 3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // The parent menu item might be removed during the drag. Ref it so |button| 3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // won't get destroyed. 3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_object_ref(menu_item->parent); 3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Signal to any future OnButtonReleased calls that we're dragging instead of 3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // pressing. 3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) ignore_button_release_ = true; 3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) const BookmarkNode* node = BookmarkNodeForWidget(menu_item); 3513551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) drag_icon_ = GetDragRepresentationForNode( 3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) node, model_, GtkThemeService::GetFrom(browser_->profile())); 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gint x, y; 3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_widget_get_pointer(menu_item, &x, &y); 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_drag_set_icon_widget(drag_context, drag_icon_, x, y); 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Hide our node. 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_widget_hide(menu_item); 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void BookmarkMenuController::OnMenuItemDragEnd(GtkWidget* menu_item, 3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GdkDragContext* drag_context) { 3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_widget_show(menu_item); 3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) g_object_unref(menu_item->parent); 3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) gtk_widget_destroy(drag_icon_); 3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) drag_icon_ = NULL; 3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3703551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)void BookmarkMenuController::OnMenuItemDragGet(GtkWidget* widget, 3713551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) GdkDragContext* context, 3723551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) GtkSelectionData* selection_data, 3733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) guint target_type, 3743551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) guint time) { 3753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) const BookmarkNode* node = BookmarkNodeForWidget(widget); 3763551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) WriteBookmarkToSelection( 3773551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) node, selection_data, target_type, browser_->profile()); 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 379