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 "WebKitContextMenu.h"
22
23#include "APIArray.h"
24#include "WebContextMenuItem.h"
25#include "WebKitContextMenuItemPrivate.h"
26#include "WebKitContextMenuPrivate.h"
27#include <wtf/glib/GRefPtr.h>
28#include <wtf/glib/WTFGType.h>
29
30using namespace WebKit;
31using namespace WebCore;
32
33/**
34 * SECTION: WebKitContextMenu
35 * @Short_description: Represents the context menu in a #WebKitWebView
36 * @Title: WebKitContextMenu
37 *
38 * #WebKitContextMenu represents a context menu containing
39 * #WebKitContextMenuItem<!-- -->s in a #WebKitWebView.
40 *
41 * When a #WebKitWebView is about to display the context menu, it
42 * emits the #WebKitWebView::context-menu signal, which has the
43 * #WebKitContextMenu as an argument. You can modify it, adding new
44 * submenus that you can create with webkit_context_menu_new(), adding
45 * new #WebKitContextMenuItem<!-- -->s with
46 * webkit_context_menu_prepend(), webkit_context_menu_append() or
47 * webkit_context_menu_insert(), maybe after having removed the
48 * existing ones with webkit_context_menu_remove_all().
49 *
50 */
51
52struct _WebKitContextMenuPrivate {
53 GList* items;
54 WebKitContextMenuItem* parentItem;
55 GRefPtr<GVariant> userData;
56};
57
58WEBKIT_DEFINE_TYPE(WebKitContextMenu, webkit_context_menu, G_TYPE_OBJECT)
59
60static void webkitContextMenuDispose(GObject* object)
61{
62 webkit_context_menu_remove_all(WEBKIT_CONTEXT_MENU(object));
63 G_OBJECT_CLASS(webkit_context_menu_parent_class)->dispose(object);
64}
65
66static void webkit_context_menu_class_init(WebKitContextMenuClass* listClass)
67{
68 GObjectClass* gObjectClass = G_OBJECT_CLASS(listClass);
69 gObjectClass->dispose = webkitContextMenuDispose;
70}
71
72void webkitContextMenuPopulate(WebKitContextMenu* menu, Vector<WebContextMenuItemData>& contextMenuItems)
73{
74 for (GList* item = menu->priv->items; item; item = g_list_next(item)) {
75 WebKitContextMenuItem* menuItem = WEBKIT_CONTEXT_MENU_ITEM(item->data);
76 contextMenuItems.append(webkitContextMenuItemToWebContextMenuItemData(menuItem));
77 }
78}
79
80void webkitContextMenuPopulate(WebKitContextMenu* menu, Vector<WebContextMenuItemGlib>& contextMenuItems)
81{
82 for (GList* item = menu->priv->items; item; item = g_list_next(item)) {
83 WebKitContextMenuItem* menuItem = WEBKIT_CONTEXT_MENU_ITEM(item->data);
84 contextMenuItems.append(webkitContextMenuItemToWebContextMenuItemGlib(menuItem));
85 }
86}
87
88WebKitContextMenu* webkitContextMenuCreate(const Vector<WebContextMenuItemData>& items)
89{
90 WebKitContextMenu* menu = webkit_context_menu_new();
91 for (const auto& item : items)
92 webkit_context_menu_prepend(menu, webkitContextMenuItemCreate(item));
93 menu->priv->items = g_list_reverse(menu->priv->items);
94
95 return menu;
96}
97
98void webkitContextMenuSetParentItem(WebKitContextMenu* menu, WebKitContextMenuItem* item)
99{
100 menu->priv->parentItem = item;
101}
102
103WebKitContextMenuItem* webkitContextMenuGetParentItem(WebKitContextMenu* menu)
104{
105 return menu->priv->parentItem;
106}
107
108/**
109 * webkit_context_menu_new:
110 *
111 * Creates a new #WebKitContextMenu object to be used as a submenu of an existing
112 * #WebKitContextMenu. The context menu of a #WebKitWebView is created by the view
113 * and passed as an argument of #WebKitWebView::context-menu signal.
114 * To add items to the menu use webkit_context_menu_prepend(),
115 * webkit_context_menu_append() or webkit_context_menu_insert().
116 * See also webkit_context_menu_new_with_items() to create a #WebKitContextMenu with
117 * a list of initial items.
118 *
119 * Returns: The newly created #WebKitContextMenu object
120 */
121WebKitContextMenu* webkit_context_menu_new()
122{
123 return WEBKIT_CONTEXT_MENU(g_object_new(WEBKIT_TYPE_CONTEXT_MENU, NULL));
124}
125
126/**
127 * webkit_context_menu_new_with_items:
128 * @items: (element-type WebKitContextMenuItem): a #GList of #WebKitContextMenuItem
129 *
130 * Creates a new #WebKitContextMenu object to be used as a submenu of an existing
131 * #WebKitContextMenu with the given initial items.
132 * See also webkit_context_menu_new()
133 *
134 * Returns: The newly created #WebKitContextMenu object
135 */
136WebKitContextMenu* webkit_context_menu_new_with_items(GList* items)
137{
138 WebKitContextMenu* menu = webkit_context_menu_new();
139 g_list_foreach(items, reinterpret_cast<GFunc>(reinterpret_cast<GCallback>(g_object_ref_sink)), 0);
140 menu->priv->items = g_list_copy(items);
141
142 return menu;
143}
144
145/**
146 * webkit_context_menu_prepend:
147 * @menu: a #WebKitContextMenu
148 * @item: the #WebKitContextMenuItem to add
149 *
150 * Adds @item at the beginning of the @menu.
151 */
152void webkit_context_menu_prepend(WebKitContextMenu* menu, WebKitContextMenuItem* item)
153{
154 webkit_context_menu_insert(menu, item, 0);
155}
156
157/**
158 * webkit_context_menu_append:
159 * @menu: a #WebKitContextMenu
160 * @item: the #WebKitContextMenuItem to add
161 *
162 * Adds @item at the end of the @menu.
163 */
164void webkit_context_menu_append(WebKitContextMenu* menu, WebKitContextMenuItem* item)
165{
166 webkit_context_menu_insert(menu, item, -1);
167}
168
169/**
170 * webkit_context_menu_insert:
171 * @menu: a #WebKitContextMenu
172 * @item: the #WebKitContextMenuItem to add
173 * @position: the position to insert the item
174 *
175 * Inserts @item into the @menu at the given position.
176 * If @position is negative, or is larger than the number of items
177 * in the #WebKitContextMenu, the item is added on to the end of
178 * the @menu. The first position is 0.
179 */
180void webkit_context_menu_insert(WebKitContextMenu* menu, WebKitContextMenuItem* item, int position)
181{
182 g_return_if_fail(WEBKIT_IS_CONTEXT_MENU(menu));
183 g_return_if_fail(WEBKIT_IS_CONTEXT_MENU_ITEM(item));
184
185 g_object_ref_sink(item);
186 menu->priv->items = g_list_insert(menu->priv->items, item, position);
187}
188
189/**
190 * webkit_context_menu_move_item:
191 * @menu: a #WebKitContextMenu
192 * @item: the #WebKitContextMenuItem to add
193 * @position: the new position to move the item
194 *
195 * Moves @item to the given position in the @menu.
196 * If @position is negative, or is larger than the number of items
197 * in the #WebKitContextMenu, the item is added on to the end of
198 * the @menu.
199 * The first position is 0.
200 */
201void webkit_context_menu_move_item(WebKitContextMenu* menu, WebKitContextMenuItem* item, int position)
202{
203 g_return_if_fail(WEBKIT_IS_CONTEXT_MENU(menu));
204 g_return_if_fail(WEBKIT_IS_CONTEXT_MENU_ITEM(item));
205
206 if (!g_list_find(menu->priv->items, item))
207 return;
208
209 menu->priv->items = g_list_remove(menu->priv->items, item);
210 menu->priv->items = g_list_insert(menu->priv->items, item, position);
211}
212
213/**
214 * webkit_context_menu_get_items:
215 * @menu: a #WebKitContextMenu
216 *
217 * Returns the item list of @menu.
218 *
219 * Returns: (element-type WebKitContextMenuItem) (transfer none): a #GList of
220 * #WebKitContextMenuItem<!-- -->s
221 */
222GList* webkit_context_menu_get_items(WebKitContextMenu* menu)
223{
224 g_return_val_if_fail(WEBKIT_IS_CONTEXT_MENU(menu), 0);
225
226 return menu->priv->items;
227}
228
229/**
230 * webkit_context_menu_get_n_items:
231 * @menu: a #WebKitContextMenu
232 *
233 * Gets the length of the @menu.
234 *
235 * Returns: the number of #WebKitContextMenuItem<!-- -->s in @menu
236 */
237guint webkit_context_menu_get_n_items(WebKitContextMenu* menu)
238{
239 g_return_val_if_fail(WEBKIT_IS_CONTEXT_MENU(menu), 0);
240
241 return g_list_length(menu->priv->items);
242}
243
244/**
245 * webkit_context_menu_first:
246 * @menu: a #WebKitContextMenu
247 *
248 * Gets the first item in the @menu.
249 *
250 * Returns: (transfer none): the first #WebKitContextMenuItem of @menu,
251 * or %NULL if the #WebKitContextMenu is empty.
252 */
253WebKitContextMenuItem* webkit_context_menu_first(WebKitContextMenu* menu)
254{
255 g_return_val_if_fail(WEBKIT_IS_CONTEXT_MENU(menu), 0);
256
257 return menu->priv->items ? WEBKIT_CONTEXT_MENU_ITEM(menu->priv->items->data) : 0;
258}
259
260/**
261 * webkit_context_menu_last:
262 * @menu: a #WebKitContextMenu
263 *
264 * Gets the last item in the @menu.
265 *
266 * Returns: (transfer none): the last #WebKitContextMenuItem of @menu,
267 * or %NULL if the #WebKitContextMenu is empty.
268 */
269WebKitContextMenuItem* webkit_context_menu_last(WebKitContextMenu* menu)
270{
271 g_return_val_if_fail(WEBKIT_IS_CONTEXT_MENU(menu), 0);
272
273 GList* last = g_list_last(menu->priv->items);
274 return last ? WEBKIT_CONTEXT_MENU_ITEM(last->data) : 0;
275}
276
277/**
278 * webkit_context_menu_get_item_at_position:
279 * @menu: a #WebKitContextMenu
280 * @position: the position of the item, counting from 0
281 *
282 * Gets the item at the given position in the @menu.
283 *
284 * Returns: (transfer none): the #WebKitContextMenuItem at position @position in @menu,
285 * or %NULL if the position is off the end of the @menu.
286 */
287WebKitContextMenuItem* webkit_context_menu_get_item_at_position(WebKitContextMenu* menu, unsigned position)
288{
289 g_return_val_if_fail(WEBKIT_IS_CONTEXT_MENU(menu), 0);
290
291 gpointer item = g_list_nth_data(menu->priv->items, position);
292 return item ? WEBKIT_CONTEXT_MENU_ITEM(item) : 0;
293}
294
295/**
296 * webkit_context_menu_remove:
297 * @menu: a #WebKitContextMenu
298 * @item: the #WebKitContextMenuItem to remove
299 *
300 * Removes @item from the @menu.
301 * See also webkit_context_menu_remove_all() to remove all items.
302 */
303void webkit_context_menu_remove(WebKitContextMenu* menu, WebKitContextMenuItem* item)
304{
305 g_return_if_fail(WEBKIT_IS_CONTEXT_MENU(menu));
306 g_return_if_fail(WEBKIT_IS_CONTEXT_MENU_ITEM(item));
307
308 if (!g_list_find(menu->priv->items, item))
309 return;
310
311 menu->priv->items = g_list_remove(menu->priv->items, item);
312 g_object_unref(item);
313}
314
315/**
316 * webkit_context_menu_remove_all:
317 * @menu: a #WebKitContextMenu
318 *
319 * Removes all items of the @menu.
320 */
321void webkit_context_menu_remove_all(WebKitContextMenu* menu)
322{
323 g_return_if_fail(WEBKIT_IS_CONTEXT_MENU(menu));
324
325 g_list_free_full(menu->priv->items, reinterpret_cast<GDestroyNotify>(g_object_unref));
326 menu->priv->items = 0;
327}
328
329/**
330 * webkit_context_menu_set_user_data:
331 * @menu: a #WebKitContextMenu
332 * @user_data: a #GVariant
333 *
334 * Sets user data to @menu.
335 * This function can be used from a Web Process extension to set user data
336 * that can be retrieved from the UI Process using webkit_context_menu_get_user_data().
337 * If the @user_data #GVariant is floating, it is consumed.
338 *
339 * Since: 2.8
340 */
341void webkit_context_menu_set_user_data(WebKitContextMenu* menu, GVariant* userData)
342{
343 g_return_if_fail(WEBKIT_IS_CONTEXT_MENU(menu));
344 g_return_if_fail(userData);
345
346 menu->priv->userData = userData;
347}
348
349/**
350 * webkit_context_menu_get_user_data:
351 * @menu: a #WebKitContextMenu
352 *
353 * Gets the user data of @menu.
354 * This function can be used from the UI Process to get user data previously set
355 * from the Web Process with webkit_context_menu_set_user_data().
356 *
357 * Returns: (transfer none): the user data of @menu, or %NULL if @menu doesn't have user data
358 *
359 * Since: 2.8
360 */
361GVariant* webkit_context_menu_get_user_data(WebKitContextMenu* menu)
362{
363 g_return_val_if_fail(WEBKIT_IS_CONTEXT_MENU(menu), nullptr);
364
365 return menu->priv->userData.get();
366}
367