1/* 2 * Copyright (C) 2007 Holger Hans Peter Freyther 3 * Copyright (C) 2010 Igalia S.L 4 * Portions Copyright (c) 2010 Motorola Mobility, Inc. All rights reserved. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 21#include "config.h" 22 23#if ENABLE(CONTEXT_MENUS) 24 25#include "ContextMenuItem.h" 26 27#include "ContextMenu.h" 28#include <gtk/gtk.h> 29#include <wtf/gobject/GRefPtr.h> 30#include <wtf/gobject/GUniquePtr.h> 31#include <wtf/text/CString.h> 32 33#define WEBKIT_CONTEXT_MENU_ACTION "webkit-context-menu" 34 35namespace WebCore { 36 37static const char* gtkStockIDFromContextMenuAction(const ContextMenuAction& action) 38{ 39 switch (action) { 40 case ContextMenuItemTagCopyLinkToClipboard: 41 case ContextMenuItemTagCopyImageToClipboard: 42 case ContextMenuItemTagCopyMediaLinkToClipboard: 43 case ContextMenuItemTagCopy: 44 return GTK_STOCK_COPY; 45 case ContextMenuItemTagOpenLinkInNewWindow: 46 case ContextMenuItemTagOpenImageInNewWindow: 47 case ContextMenuItemTagOpenFrameInNewWindow: 48 case ContextMenuItemTagOpenMediaInNewWindow: 49 return GTK_STOCK_OPEN; 50 case ContextMenuItemTagDownloadLinkToDisk: 51 case ContextMenuItemTagDownloadImageToDisk: 52 return GTK_STOCK_SAVE; 53 case ContextMenuItemTagGoBack: 54 return GTK_STOCK_GO_BACK; 55 case ContextMenuItemTagGoForward: 56 return GTK_STOCK_GO_FORWARD; 57 case ContextMenuItemTagStop: 58 return GTK_STOCK_STOP; 59 case ContextMenuItemTagReload: 60 return GTK_STOCK_REFRESH; 61 case ContextMenuItemTagCut: 62 return GTK_STOCK_CUT; 63 case ContextMenuItemTagPaste: 64 return GTK_STOCK_PASTE; 65 case ContextMenuItemTagDelete: 66 return GTK_STOCK_DELETE; 67 case ContextMenuItemTagSelectAll: 68 return GTK_STOCK_SELECT_ALL; 69 case ContextMenuItemTagSpellingGuess: 70 return 0; 71 case ContextMenuItemTagIgnoreSpelling: 72 return GTK_STOCK_NO; 73 case ContextMenuItemTagLearnSpelling: 74 return GTK_STOCK_OK; 75 case ContextMenuItemTagOther: 76 return GTK_STOCK_MISSING_IMAGE; 77 case ContextMenuItemTagSearchInSpotlight: 78 return GTK_STOCK_FIND; 79 case ContextMenuItemTagSearchWeb: 80 return GTK_STOCK_FIND; 81 case ContextMenuItemTagOpenWithDefaultApplication: 82 return GTK_STOCK_OPEN; 83 case ContextMenuItemPDFZoomIn: 84 return GTK_STOCK_ZOOM_IN; 85 case ContextMenuItemPDFZoomOut: 86 return GTK_STOCK_ZOOM_OUT; 87 case ContextMenuItemPDFAutoSize: 88 return GTK_STOCK_ZOOM_FIT; 89 case ContextMenuItemPDFNextPage: 90 return GTK_STOCK_GO_FORWARD; 91 case ContextMenuItemPDFPreviousPage: 92 return GTK_STOCK_GO_BACK; 93 // New tags, not part of API 94 case ContextMenuItemTagOpenLink: 95 return GTK_STOCK_OPEN; 96 case ContextMenuItemTagCheckSpelling: 97 return GTK_STOCK_SPELL_CHECK; 98 case ContextMenuItemTagFontMenu: 99 return GTK_STOCK_SELECT_FONT; 100 case ContextMenuItemTagShowFonts: 101 return GTK_STOCK_SELECT_FONT; 102 case ContextMenuItemTagBold: 103 return GTK_STOCK_BOLD; 104 case ContextMenuItemTagItalic: 105 return GTK_STOCK_ITALIC; 106 case ContextMenuItemTagUnderline: 107 return GTK_STOCK_UNDERLINE; 108 case ContextMenuItemTagShowColors: 109 return GTK_STOCK_SELECT_COLOR; 110 case ContextMenuItemTagToggleMediaControls: 111 case ContextMenuItemTagToggleMediaLoop: 112 case ContextMenuItemTagCopyImageUrlToClipboard: 113 // No icon for this. 114 return 0; 115 case ContextMenuItemTagEnterVideoFullscreen: 116 return GTK_STOCK_FULLSCREEN; 117 default: 118 return 0; 119 } 120} 121 122static PlatformMenuItemDescription createPlatformMenuItemDescription(ContextMenuItemType type, ContextMenuAction action, const String& title, bool enabled, bool checked) 123{ 124 if (type == SeparatorType) 125 return GTK_MENU_ITEM(gtk_separator_menu_item_new()); 126 127 GUniquePtr<char> actionName(g_strdup_printf("context-menu-action-%d", action)); 128 GRefPtr<GtkAction> platformAction; 129 130 if (type == CheckableActionType) { 131 platformAction = adoptGRef(GTK_ACTION(gtk_toggle_action_new(actionName.get(), title.utf8().data(), 0, gtkStockIDFromContextMenuAction(action)))); 132 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(platformAction.get()), checked); 133 } else 134 platformAction = adoptGRef(gtk_action_new(actionName.get(), title.utf8().data(), 0, gtkStockIDFromContextMenuAction(action))); 135 gtk_action_set_sensitive(platformAction.get(), enabled); 136 137 GtkMenuItem* item = GTK_MENU_ITEM(gtk_action_create_menu_item(platformAction.get())); 138 g_object_set_data(G_OBJECT(item), WEBKIT_CONTEXT_MENU_ACTION, GINT_TO_POINTER(action)); 139 140 return item; 141} 142 143// Extract the ActionType from the menu item 144ContextMenuItem::ContextMenuItem(PlatformMenuItemDescription item) 145 : m_platformDescription(item) 146{ 147 // Don't show accel labels in context menu items. 148 GtkAction* action = gtkAction(); 149 if (!action) 150 return; 151 152 if (!gtk_action_get_accel_path(action)) 153 return; 154 155 GtkWidget* child = gtk_bin_get_child(GTK_BIN(item)); 156 if (GTK_IS_ACCEL_LABEL(child)) 157 gtk_accel_label_set_accel_closure(GTK_ACCEL_LABEL(child), 0); 158} 159 160ContextMenuItem::ContextMenuItem(ContextMenu* subMenu) 161{ 162 m_platformDescription = GTK_MENU_ITEM(gtk_menu_item_new()); 163 if (subMenu) 164 setSubMenu(subMenu); 165} 166 167ContextMenuItem::ContextMenuItem(ContextMenuItemType type, ContextMenuAction action, const String& title, ContextMenu* subMenu) 168{ 169 m_platformDescription = createPlatformMenuItemDescription(type, action, title, true, false); 170 if (subMenu) 171 setSubMenu(subMenu); 172} 173 174ContextMenuItem::ContextMenuItem(ContextMenuItemType type, ContextMenuAction action, const String& title, bool enabled, bool checked) 175{ 176 m_platformDescription = createPlatformMenuItemDescription(type, action, title, enabled, checked); 177} 178 179ContextMenuItem::ContextMenuItem(ContextMenuAction action, const String& title, bool enabled, bool checked, Vector<ContextMenuItem>& subMenuItems) 180{ 181 m_platformDescription = createPlatformMenuItemDescription(SubmenuType, action, title, enabled, checked); 182 setSubMenu(subMenuItems); 183} 184 185ContextMenuItem::~ContextMenuItem() 186{ 187} 188 189PlatformMenuItemDescription ContextMenuItem::releasePlatformDescription() 190{ 191 PlatformMenuItemDescription platformDescription = m_platformDescription; 192 m_platformDescription = 0; 193 return platformDescription; 194} 195 196ContextMenuItemType ContextMenuItem::type() const 197{ 198 if (GTK_IS_SEPARATOR_MENU_ITEM(m_platformDescription)) 199 return SeparatorType; 200 if (GTK_IS_CHECK_MENU_ITEM(m_platformDescription)) 201 return CheckableActionType; 202 if (gtk_menu_item_get_submenu(m_platformDescription)) 203 return SubmenuType; 204 return ActionType; 205} 206 207void ContextMenuItem::setType(ContextMenuItemType type) 208{ 209 if (type == SeparatorType) 210 m_platformDescription = GTK_MENU_ITEM(gtk_separator_menu_item_new()); 211} 212 213ContextMenuAction ContextMenuItem::action() const 214{ 215 return static_cast<ContextMenuAction>(GPOINTER_TO_INT(g_object_get_data(G_OBJECT(m_platformDescription), WEBKIT_CONTEXT_MENU_ACTION))); 216} 217 218void ContextMenuItem::setAction(ContextMenuAction action) 219{ 220 g_object_set_data(G_OBJECT(m_platformDescription), WEBKIT_CONTEXT_MENU_ACTION, GINT_TO_POINTER(action)); 221} 222 223String ContextMenuItem::title() const 224{ 225 GtkAction* action = gtkAction(); 226 return action ? String::fromUTF8(gtk_action_get_label(action)) : String(); 227} 228 229void ContextMenuItem::setTitle(const String& title) 230{ 231 GtkAction* action = gtkAction(); 232 if (action) 233 gtk_action_set_label(action, title.utf8().data()); 234} 235 236PlatformMenuDescription ContextMenuItem::platformSubMenu() const 237{ 238 GtkWidget* subMenu = gtk_menu_item_get_submenu(m_platformDescription); 239 return subMenu ? GTK_MENU(subMenu) : 0; 240} 241 242void ContextMenuItem::setSubMenu(ContextMenu* menu) 243{ 244 gtk_menu_item_set_submenu(m_platformDescription, GTK_WIDGET(menu->releasePlatformDescription())); 245} 246 247void ContextMenuItem::setSubMenu(Vector<ContextMenuItem>& subMenuItems) 248{ 249 ContextMenu menu(platformMenuDescription(subMenuItems)); 250 setSubMenu(&menu); 251} 252 253void ContextMenuItem::setChecked(bool shouldCheck) 254{ 255 GtkAction* action = gtkAction(); 256 if (action && GTK_IS_TOGGLE_ACTION(action)) 257 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), shouldCheck); 258} 259 260bool ContextMenuItem::checked() const 261{ 262 GtkAction* action = gtkAction(); 263 if (action && GTK_IS_TOGGLE_ACTION(action)) 264 return gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)); 265 return false; 266} 267 268bool ContextMenuItem::enabled() const 269{ 270 GtkAction* action = gtkAction(); 271 return action ? gtk_action_get_sensitive(action) : false; 272} 273 274void ContextMenuItem::setEnabled(bool shouldEnable) 275{ 276 GtkAction* action = gtkAction(); 277 if (action) 278 gtk_action_set_sensitive(action, shouldEnable); 279} 280 281GtkAction* ContextMenuItem::gtkAction() const 282{ 283 return gtk_activatable_get_related_action(GTK_ACTIVATABLE(m_platformDescription)); 284} 285 286} 287 288#endif // ENABLE(CONTEXT_MENUS) 289