39e3fb45f269b04b6bc225d09a4e23cfb12ebe76
[WebKit.git] / Source / WebKit2 / UIProcess / API / gtk / WebKitContextMenuItem.cpp
1 /*
2  * Copyright (C) 2012 Igalia S.L.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "WebKitContextMenuItem.h"
22
23 #include "APIArray.h"
24 #include "WebContextMenuItem.h"
25 #include "WebContextMenuItemGtk.h"
26 #include "WebKitContextMenuActionsPrivate.h"
27 #include "WebKitContextMenuItemPrivate.h"
28 #include "WebKitContextMenuPrivate.h"
29 #include <WebCore/ContextMenu.h>
30 #include <WebCore/ContextMenuItem.h>
31 #include <gtk/gtk.h>
32 #include <memory>
33 #include <wtf/glib/GRefPtr.h>
34 #include <wtf/glib/GUniquePtr.h>
35 #include <wtf/glib/WTFGType.h>
36
37 using namespace WebKit;
38 using namespace WebCore;
39
40 /**
41  * SECTION: WebKitContextMenuItem
42  * @Short_description: One item of the #WebKitContextMenu
43  * @Title: WebKitContextMenuItem
44  *
45  * The #WebKitContextMenu is composed of #WebKitContextMenuItem<!--
46  * -->s. These items can be created from a #GtkAction, from a
47  * #WebKitContextMenuAction or from a #WebKitContextMenuAction and a
48  * label. These #WebKitContextMenuAction<!-- -->s denote stock actions
49  * for the items. You can also create separators and submenus.
50  *
51  */
52
53 struct _WebKitContextMenuItemPrivate {
54     ~_WebKitContextMenuItemPrivate()
55     {
56         if (subMenu)
57             webkitContextMenuSetParentItem(subMenu.get(), 0);
58     }
59
60     std::unique_ptr<WebContextMenuItemGtk> menuItem;
61     GRefPtr<WebKitContextMenu> subMenu;
62 };
63
64 WEBKIT_DEFINE_TYPE(WebKitContextMenuItem, webkit_context_menu_item, G_TYPE_INITIALLY_UNOWNED)
65
66 static void webkit_context_menu_item_class_init(WebKitContextMenuItemClass*)
67 {
68 }
69
70 static bool checkAndWarnIfMenuHasParentItem(WebKitContextMenu* menu)
71 {
72     if (menu && webkitContextMenuGetParentItem(menu)) {
73         g_warning("Attempting to set a WebKitContextMenu as submenu of "
74                   "a WebKitContextMenuItem, but the menu is already "
75                   "a submenu of a WebKitContextMenuItem");
76         return true;
77     }
78
79     return false;
80 }
81
82 static void webkitContextMenuItemSetSubMenu(WebKitContextMenuItem* item, GRefPtr<WebKitContextMenu> subMenu)
83 {
84     if (checkAndWarnIfMenuHasParentItem(subMenu.get()))
85         return;
86
87     if (item->priv->subMenu)
88         webkitContextMenuSetParentItem(item->priv->subMenu.get(), nullptr);
89     item->priv->subMenu = subMenu;
90     if (subMenu)
91         webkitContextMenuSetParentItem(subMenu.get(), item);
92 }
93
94 WebKitContextMenuItem* webkitContextMenuItemCreate(const WebContextMenuItemData& itemData)
95 {
96     WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(g_object_new(WEBKIT_TYPE_CONTEXT_MENU_ITEM, NULL));
97
98     item->priv->menuItem = std::make_unique<WebContextMenuItemGtk>(itemData);
99     const Vector<WebContextMenuItemData>& subMenu = itemData.submenu();
100     if (!subMenu.isEmpty())
101         webkitContextMenuItemSetSubMenu(item, adoptGRef(webkitContextMenuCreate(subMenu)));
102
103     return item;
104 }
105
106 WebContextMenuItemGtk webkitContextMenuItemToWebContextMenuItemGtk(WebKitContextMenuItem* item)
107 {
108     if (item->priv->subMenu) {
109         Vector<WebContextMenuItemGtk> subMenuItems;
110         webkitContextMenuPopulate(item->priv->subMenu.get(), subMenuItems);
111         return WebContextMenuItemGtk(*item->priv->menuItem, WTFMove(subMenuItems));
112     }
113
114     return *item->priv->menuItem;
115 }
116
117 WebContextMenuItemData webkitContextMenuItemToWebContextMenuItemData(WebKitContextMenuItem* item)
118 {
119     if (item->priv->subMenu) {
120         Vector<WebContextMenuItemData> subMenuItems;
121         webkitContextMenuPopulate(item->priv->subMenu.get(), subMenuItems);
122         return WebContextMenuItemData(item->priv->menuItem->action(), item->priv->menuItem->title(), item->priv->menuItem->enabled(), subMenuItems);
123     }
124
125     return WebContextMenuItemData(item->priv->menuItem->type(), item->priv->menuItem->action(), item->priv->menuItem->title(), item->priv->menuItem->enabled(), item->priv->menuItem->checked());
126 }
127
128 /**
129  * webkit_context_menu_item_new:
130  * @action: a #GtkAction
131  *
132  * Creates a new #WebKitContextMenuItem for the given @action.
133  *
134  * Returns: the newly created #WebKitContextMenuItem object.
135  *
136  * Deprecated: 2.18: Use webkit_context_menu_item_new_from_gaction() instead.
137  */
138 WebKitContextMenuItem* webkit_context_menu_item_new(GtkAction* action)
139 {
140     g_return_val_if_fail(GTK_IS_ACTION(action), nullptr);
141
142     WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(g_object_new(WEBKIT_TYPE_CONTEXT_MENU_ITEM, nullptr));
143     item->priv->menuItem = std::make_unique<WebContextMenuItemGtk>(action);
144
145     return item;
146 }
147
148 /**
149  * webkit_context_menu_item_new_from_gaction:
150  * @action: a #GAction
151  * @label: the menu item label text
152  * @target: (allow-none): a #GVariant to use as the action target
153  *
154  * Creates a new #WebKitContextMenuItem for the given @action and @label. On activation
155  * @target will be passed as parameter to the callback.
156  *
157  * Returns: the newly created #WebKitContextMenuItem object.
158  *
159  * Since: 2.18
160  */
161 WebKitContextMenuItem* webkit_context_menu_item_new_from_gaction(GAction* action, const gchar* label, GVariant* target)
162 {
163     g_return_val_if_fail(G_IS_ACTION(action), nullptr);
164     g_return_val_if_fail(!g_action_get_state_type(action) || g_variant_type_equal(g_action_get_state_type(action), G_VARIANT_TYPE_BOOLEAN), nullptr);
165     g_return_val_if_fail(label, nullptr);
166     g_return_val_if_fail(!target || g_variant_is_of_type(target, g_action_get_parameter_type(action)), nullptr);
167
168     WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(g_object_new(WEBKIT_TYPE_CONTEXT_MENU_ITEM, nullptr));
169     item->priv->menuItem = std::make_unique<WebContextMenuItemGtk>(action, String::fromUTF8(label), target);
170
171     return item;
172 }
173
174 /**
175  * webkit_context_menu_item_new_from_stock_action:
176  * @action: a #WebKitContextMenuAction stock action
177  *
178  * Creates a new #WebKitContextMenuItem for the given stock action.
179  * Stock actions are handled automatically by WebKit so that, for example,
180  * when a menu item created with a %WEBKIT_CONTEXT_MENU_ACTION_STOP is
181  * activated the action associated will be handled by WebKit and the current
182  * load operation will be stopped. You can get the #GtkAction of a
183  * #WebKitContextMenuItem created with a #WebKitContextMenuAction with
184  * webkit_context_menu_item_get_action() and connect to #GtkAction::activate signal
185  * to be notified when the item is activated. But you can't prevent the associated
186  * action from being performed.
187  *
188  * Returns: the newly created #WebKitContextMenuItem object.
189  */
190 WebKitContextMenuItem* webkit_context_menu_item_new_from_stock_action(WebKitContextMenuAction action)
191 {
192     g_return_val_if_fail(action > WEBKIT_CONTEXT_MENU_ACTION_NO_ACTION && action < WEBKIT_CONTEXT_MENU_ACTION_CUSTOM, nullptr);
193
194     WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(g_object_new(WEBKIT_TYPE_CONTEXT_MENU_ITEM, nullptr));
195     ContextMenuItemType type = webkitContextMenuActionIsCheckable(action) ? CheckableActionType : ActionType;
196     item->priv->menuItem = std::make_unique<WebContextMenuItemGtk>(type, webkitContextMenuActionGetActionTag(action), webkitContextMenuActionGetLabel(action));
197
198     return item;
199 }
200
201 /**
202  * webkit_context_menu_item_new_from_stock_action_with_label:
203  * @action: a #WebKitContextMenuAction stock action
204  * @label: a custom label text to use instead of the predefined one
205  *
206  * Creates a new #WebKitContextMenuItem for the given stock action using the given @label.
207  * Stock actions have a predefined label, this method can be used to create a
208  * #WebKitContextMenuItem for a #WebKitContextMenuAction but using a custom label.
209  *
210  * Returns: the newly created #WebKitContextMenuItem object.
211  */
212 WebKitContextMenuItem* webkit_context_menu_item_new_from_stock_action_with_label(WebKitContextMenuAction action, const gchar* label)
213 {
214     g_return_val_if_fail(action > WEBKIT_CONTEXT_MENU_ACTION_NO_ACTION && action < WEBKIT_CONTEXT_MENU_ACTION_CUSTOM, nullptr);
215
216     WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(g_object_new(WEBKIT_TYPE_CONTEXT_MENU_ITEM, nullptr));
217     ContextMenuItemType type = webkitContextMenuActionIsCheckable(action) ? CheckableActionType : ActionType;
218     item->priv->menuItem = std::make_unique<WebContextMenuItemGtk>(type, webkitContextMenuActionGetActionTag(action), String::fromUTF8(label));
219
220     return item;
221 }
222
223 /**
224  * webkit_context_menu_item_new_with_submenu:
225  * @label: the menu item label text
226  * @submenu: a #WebKitContextMenu to set
227  *
228  * Creates a new #WebKitContextMenuItem using the given @label with a submenu.
229  *
230  * Returns: the newly created #WebKitContextMenuItem object.
231  */
232 WebKitContextMenuItem* webkit_context_menu_item_new_with_submenu(const gchar* label, WebKitContextMenu* submenu)
233 {
234     g_return_val_if_fail(label, nullptr);
235     g_return_val_if_fail(WEBKIT_IS_CONTEXT_MENU(submenu), nullptr);
236
237     if (checkAndWarnIfMenuHasParentItem(submenu))
238         return nullptr;
239
240     WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(g_object_new(WEBKIT_TYPE_CONTEXT_MENU_ITEM, nullptr));
241     item->priv->menuItem = std::make_unique<WebContextMenuItemGtk>(ActionType, ContextMenuItemBaseApplicationTag, String::fromUTF8(label));
242     item->priv->subMenu = submenu;
243     webkitContextMenuSetParentItem(submenu, item);
244
245     return item;
246 }
247
248 /**
249  * webkit_context_menu_item_new_separator:
250  *
251  * Creates a new #WebKitContextMenuItem representing a separator.
252  *
253  * Returns: the newly created #WebKitContextMenuItem object.
254  */
255 WebKitContextMenuItem* webkit_context_menu_item_new_separator(void)
256 {
257     WebKitContextMenuItem* item = WEBKIT_CONTEXT_MENU_ITEM(g_object_new(WEBKIT_TYPE_CONTEXT_MENU_ITEM, nullptr));
258     item->priv->menuItem = std::make_unique<WebContextMenuItemGtk>(SeparatorType, ContextMenuItemTagNoAction, String());
259
260     return item;
261 }
262
263 /**
264  * webkit_context_menu_item_get_action:
265  * @item: a #WebKitContextMenuItem
266  *
267  * Gets the action associated to @item as a #GtkAction.
268  *
269  * Returns: (transfer none): the #GtkAction associated to the #WebKitContextMenuItem,
270  *    or %NULL if @item is a separator.
271  *
272  * Deprecated: 2.18: Use webkit_context_menu_item_get_gaction() instead.
273  */
274 GtkAction* webkit_context_menu_item_get_action(WebKitContextMenuItem* item)
275 {
276     g_return_val_if_fail(WEBKIT_IS_CONTEXT_MENU_ITEM(item), nullptr);
277
278     return item->priv->menuItem->gtkAction();
279 }
280
281 /**
282  * webkit_context_menu_item_get_gaction:
283  * @item: a #WebKitContextMenuItem
284  *
285  * Gets the action associated to @item as a #GAction.
286  *
287  * Returns: (transfer none): the #GAction associated to the #WebKitContextMenuItem,
288  *    or %NULL if @item is a separator.
289  *
290  * Since: 2.18
291  */
292 GAction* webkit_context_menu_item_get_gaction(WebKitContextMenuItem* item)
293 {
294     g_return_val_if_fail(WEBKIT_IS_CONTEXT_MENU_ITEM(item), nullptr);
295
296     return item->priv->menuItem->gAction();
297 }
298
299 /**
300  * webkit_context_menu_item_get_stock_action:
301  * @item: a #WebKitContextMenuItem
302  *
303  * Gets the #WebKitContextMenuAction of @item. If the #WebKitContextMenuItem was not
304  * created for a stock action %WEBKIT_CONTEXT_MENU_ACTION_CUSTOM will be
305  * returned. If the #WebKitContextMenuItem is a separator %WEBKIT_CONTEXT_MENU_ACTION_NO_ACTION
306  * will be returned.
307  *
308  * Returns: the #WebKitContextMenuAction of @item
309  */
310 WebKitContextMenuAction webkit_context_menu_item_get_stock_action(WebKitContextMenuItem* item)
311 {
312     g_return_val_if_fail(WEBKIT_IS_CONTEXT_MENU_ITEM(item), WEBKIT_CONTEXT_MENU_ACTION_NO_ACTION);
313
314     return webkitContextMenuActionGetForContextMenuItem(*item->priv->menuItem);
315 }
316
317 /**
318  * webkit_context_menu_item_is_separator:
319  * @item: a #WebKitContextMenuItem
320  *
321  * Checks whether @item is a separator.
322  *
323  * Returns: %TRUE is @item is a separator or %FALSE otherwise
324  */
325 gboolean webkit_context_menu_item_is_separator(WebKitContextMenuItem* item)
326 {
327     g_return_val_if_fail(WEBKIT_IS_CONTEXT_MENU_ITEM(item), FALSE);
328
329     return item->priv->menuItem->type() == SeparatorType;
330 }
331
332 /**
333  * webkit_context_menu_item_set_submenu:
334  * @item: a #WebKitContextMenuItem
335  * @submenu: (allow-none): a #WebKitContextMenu
336  *
337  * Sets or replaces the @item submenu. If @submenu is %NULL the current
338  * submenu of @item is removed.
339  */
340 void webkit_context_menu_item_set_submenu(WebKitContextMenuItem* item, WebKitContextMenu* submenu)
341 {
342     g_return_if_fail(WEBKIT_IS_CONTEXT_MENU_ITEM(item));
343
344     if (item->priv->subMenu == submenu)
345         return;
346
347     webkitContextMenuItemSetSubMenu(item, submenu);
348 }
349
350 /**
351  * webkit_context_menu_item_get_submenu:
352  * @item: a #WebKitContextMenuItem
353  *
354  * Gets the submenu of @item.
355  *
356  * Returns: (transfer none): the #WebKitContextMenu representing the submenu of
357  *    @item or %NULL if @item doesn't have a submenu.
358  */
359 WebKitContextMenu* webkit_context_menu_item_get_submenu(WebKitContextMenuItem* item)
360 {
361     g_return_val_if_fail(WEBKIT_IS_CONTEXT_MENU_ITEM(item), 0);
362
363     return item->priv->subMenu.get();
364 }
365