1/*
2 * Copyright (C) 2015 Igalia S.L.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "WebContextMenuItemGlib.h"
28
29#include "APIObject.h"
30#include <gio/gio.h>
31
32#if PLATFORM(GTK)
33#include <gtk/gtk.h>
34#endif
35
36namespace WebKit {
37using namespace WebCore;
38
39#if PLATFORM(GTK)
40static const char* gtkStockIDFromContextMenuAction(ContextMenuAction action)
41{
42 switch (action) {
43 case ContextMenuItemTagCopyLinkToClipboard:
44 case ContextMenuItemTagCopyImageToClipboard:
45 case ContextMenuItemTagCopyMediaLinkToClipboard:
46 case ContextMenuItemTagCopy:
47 return GTK_STOCK_COPY;
48 case ContextMenuItemTagOpenLinkInNewWindow:
49 case ContextMenuItemTagOpenImageInNewWindow:
50 case ContextMenuItemTagOpenFrameInNewWindow:
51 case ContextMenuItemTagOpenMediaInNewWindow:
52 return GTK_STOCK_OPEN;
53 case ContextMenuItemTagDownloadLinkToDisk:
54 case ContextMenuItemTagDownloadImageToDisk:
55 return GTK_STOCK_SAVE;
56 case ContextMenuItemTagGoBack:
57 return GTK_STOCK_GO_BACK;
58 case ContextMenuItemTagGoForward:
59 return GTK_STOCK_GO_FORWARD;
60 case ContextMenuItemTagStop:
61 return GTK_STOCK_STOP;
62 case ContextMenuItemTagReload:
63 return GTK_STOCK_REFRESH;
64 case ContextMenuItemTagCut:
65 return GTK_STOCK_CUT;
66 case ContextMenuItemTagPaste:
67 return GTK_STOCK_PASTE;
68 case ContextMenuItemTagDelete:
69 return GTK_STOCK_DELETE;
70 case ContextMenuItemTagSelectAll:
71 return GTK_STOCK_SELECT_ALL;
72 case ContextMenuItemTagSpellingGuess:
73 return nullptr;
74 case ContextMenuItemTagIgnoreSpelling:
75 return GTK_STOCK_NO;
76 case ContextMenuItemTagLearnSpelling:
77 return GTK_STOCK_OK;
78 case ContextMenuItemTagOther:
79 return GTK_STOCK_MISSING_IMAGE;
80 case ContextMenuItemTagSearchInSpotlight:
81 return GTK_STOCK_FIND;
82 case ContextMenuItemTagSearchWeb:
83 return GTK_STOCK_FIND;
84 case ContextMenuItemTagOpenWithDefaultApplication:
85 return GTK_STOCK_OPEN;
86 case ContextMenuItemPDFZoomIn:
87 return GTK_STOCK_ZOOM_IN;
88 case ContextMenuItemPDFZoomOut:
89 return GTK_STOCK_ZOOM_OUT;
90 case ContextMenuItemPDFAutoSize:
91 return GTK_STOCK_ZOOM_FIT;
92 case ContextMenuItemPDFNextPage:
93 return GTK_STOCK_GO_FORWARD;
94 case ContextMenuItemPDFPreviousPage:
95 return GTK_STOCK_GO_BACK;
96 // New tags, not part of API
97 case ContextMenuItemTagOpenLink:
98 return GTK_STOCK_OPEN;
99 case ContextMenuItemTagCheckSpelling:
100 return GTK_STOCK_SPELL_CHECK;
101 case ContextMenuItemTagFontMenu:
102 return GTK_STOCK_SELECT_FONT;
103 case ContextMenuItemTagShowFonts:
104 return GTK_STOCK_SELECT_FONT;
105 case ContextMenuItemTagBold:
106 return GTK_STOCK_BOLD;
107 case ContextMenuItemTagItalic:
108 return GTK_STOCK_ITALIC;
109 case ContextMenuItemTagUnderline:
110 return GTK_STOCK_UNDERLINE;
111 case ContextMenuItemTagShowColors:
112 return GTK_STOCK_SELECT_COLOR;
113 case ContextMenuItemTagToggleMediaControls:
114 case ContextMenuItemTagToggleMediaLoop:
115 case ContextMenuItemTagCopyImageUrlToClipboard:
116 // No icon for this.
117 return nullptr;
118 case ContextMenuItemTagEnterVideoFullscreen:
119 return GTK_STOCK_FULLSCREEN;
120 default:
121 return nullptr;
122 }
123}
124#endif // PLATFORM(GTK)
125
126WebContextMenuItemGlib::WebContextMenuItemGlib(ContextMenuItemType type, ContextMenuAction action, const String& title, bool enabled, bool checked)
127 : WebContextMenuItemData(type, action, title, enabled, checked)
128{
129 ASSERT(type != SubmenuType);
130 createActionIfNeeded();
131}
132
133WebContextMenuItemGlib::WebContextMenuItemGlib(const WebContextMenuItemData& data)
134 : WebContextMenuItemData(data.type() == SubmenuType ? ActionType : data.type(), data.action(), data.title(), data.enabled(), data.checked())
135{
136 createActionIfNeeded();
137}
138
139WebContextMenuItemGlib::WebContextMenuItemGlib(const WebContextMenuItemGlib& data, Vector<WebContextMenuItemGlib>&& submenu)
140 : WebContextMenuItemData(ActionType, data.action(), data.title(), data.enabled(), false)
141{
142 m_gAction = data.gAction();
143 m_submenuItems = WTFMove(submenu);
144#if PLATFORM(GTK)
145 m_gtkAction = data.gtkAction();
146#endif
147}
148
149static bool isGActionChecked(GAction* action)
150{
151 if (!g_action_get_state_type(action))
152 return false;
153
154 ASSERT(g_variant_type_equal(g_action_get_state_type(action), G_VARIANT_TYPE_BOOLEAN));
155 GRefPtr<GVariant> state = adoptGRef(g_action_get_state(action));
156 return g_variant_get_boolean(state.get());
157}
158
159WebContextMenuItemGlib::WebContextMenuItemGlib(GAction* action, const String& title, GVariant* target)
160 : WebContextMenuItemData(g_action_get_state_type(action) ? CheckableActionType : ActionType, ContextMenuItemBaseApplicationTag, title, g_action_get_enabled(action), isGActionChecked(action))
161 , m_gAction(action)
162 , m_gActionTarget(target)
163{
164 createActionIfNeeded();
165}
166
167#if PLATFORM(GTK)
168WebContextMenuItemGlib::WebContextMenuItemGlib(GtkAction* action)
169 : WebContextMenuItemData(GTK_IS_TOGGLE_ACTION(action) ? CheckableActionType : ActionType, ContextMenuItemBaseApplicationTag, String::fromUTF8(gtk_action_get_label(action)), gtk_action_get_sensitive(action), GTK_IS_TOGGLE_ACTION(action) ? gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)) : false)
170{
171 m_gtkAction = action;
172 createActionIfNeeded();
173 g_object_set_data_full(G_OBJECT(m_gAction.get()), "webkit-gtk-action", g_object_ref(m_gtkAction), g_object_unref);
174}
175#endif
176
177WebContextMenuItemGlib::~WebContextMenuItemGlib()
178{
179}
180
181GUniquePtr<char> WebContextMenuItemGlib::buildActionName() const
182{
183#if PLATFORM(GTK)
184 if (m_gtkAction)
185 return GUniquePtr<char>(g_strdup(gtk_action_get_name(m_gtkAction)));
186#endif
187
188 static uint64_t actionID = 0;
189 return GUniquePtr<char>(g_strdup_printf("action-%" PRIu64, ++actionID));
190}
191
192void WebContextMenuItemGlib::createActionIfNeeded()
193{
194 if (type() == SeparatorType)
195 return;
196
197 if (!m_gAction) {
198 auto actionName = buildActionName();
199 if (type() == CheckableActionType)
200 m_gAction = adoptGRef(G_ACTION(g_simple_action_new_stateful(actionName.get(), nullptr, g_variant_new_boolean(checked()))));
201 else
202 m_gAction = adoptGRef(G_ACTION(g_simple_action_new(actionName.get(), nullptr)));
203 g_simple_action_set_enabled(G_SIMPLE_ACTION(m_gAction.get()), enabled());
204 }
205
206#if PLATFORM(GTK)
207 // Create the GtkAction for backwards compatibility only.
208 if (!m_gtkAction) {
209 if (type() == CheckableActionType) {
210 m_gtkAction = GTK_ACTION(gtk_toggle_action_new(g_action_get_name(m_gAction.get()), title().utf8().data(), nullptr, gtkStockIDFromContextMenuAction(action())));
211 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(m_gtkAction), checked());
212 } else
213 m_gtkAction = gtk_action_new(g_action_get_name(m_gAction.get()), title().utf8().data(), 0, gtkStockIDFromContextMenuAction(action()));
214 gtk_action_set_sensitive(m_gtkAction, enabled());
215 g_object_set_data_full(G_OBJECT(m_gAction.get()), "webkit-gtk-action", m_gtkAction, g_object_unref);
216 }
217
218 g_signal_connect_object(m_gAction.get(), "activate", G_CALLBACK(gtk_action_activate), m_gtkAction, G_CONNECT_SWAPPED);
219#endif
220}
221
222} // namespace WebKit
223