1 | /* |
2 | * Copyright (C) 2018 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 "WebKitScriptDialogImpl.h" |
22 | |
23 | #include "WebKitScriptDialogPrivate.h" |
24 | #include <glib/gi18n-lib.h> |
25 | #include <wtf/glib/WTFGType.h> |
26 | #include <wtf/text/CString.h> |
27 | |
28 | struct _WebKitScriptDialogImplPrivate { |
29 | WebKitScriptDialog* dialog; |
30 | GtkWidget* vbox; |
31 | GtkWidget* swindow; |
32 | GtkWidget* title; |
33 | GtkWidget* label; |
34 | GtkWidget* entry; |
35 | GtkWidget* actionArea; |
36 | GtkWidget* defaultButton; |
37 | }; |
38 | |
39 | WEBKIT_DEFINE_TYPE(WebKitScriptDialogImpl, webkit_script_dialog_impl, WEBKIT_TYPE_WEB_VIEW_DIALOG) |
40 | |
41 | static void webkitScriptDialogImplClose(WebKitScriptDialogImpl* dialog) |
42 | { |
43 | webkit_script_dialog_close(dialog->priv->dialog); |
44 | gtk_widget_destroy(GTK_WIDGET(dialog)); |
45 | } |
46 | |
47 | static gboolean webkitScriptDialogImplKeyPressEvent(GtkWidget* widget, GdkEventKey* keyEvent) |
48 | { |
49 | if (keyEvent->keyval == GDK_KEY_Escape) { |
50 | webkitScriptDialogImplClose(WEBKIT_SCRIPT_DIALOG_IMPL(widget)); |
51 | return GDK_EVENT_STOP; |
52 | } |
53 | |
54 | return GDK_EVENT_PROPAGATE; |
55 | } |
56 | |
57 | static void webkitScriptDialogImplMap(GtkWidget* widget) |
58 | { |
59 | WebKitScriptDialogImplPrivate* priv = WEBKIT_SCRIPT_DIALOG_IMPL(widget)->priv; |
60 | gtk_widget_grab_default(priv->defaultButton); |
61 | |
62 | switch (priv->dialog->type) { |
63 | case WEBKIT_SCRIPT_DIALOG_ALERT: |
64 | case WEBKIT_SCRIPT_DIALOG_CONFIRM: |
65 | case WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM: |
66 | gtk_widget_grab_focus(priv->defaultButton); |
67 | break; |
68 | case WEBKIT_SCRIPT_DIALOG_PROMPT: |
69 | gtk_widget_grab_focus(priv->entry); |
70 | break; |
71 | } |
72 | |
73 | GTK_WIDGET_CLASS(webkit_script_dialog_impl_parent_class)->map(widget); |
74 | } |
75 | |
76 | static void webkitScriptDialogImplConstructed(GObject* object) |
77 | { |
78 | G_OBJECT_CLASS(webkit_script_dialog_impl_parent_class)->constructed(object); |
79 | |
80 | auto* dialog = WEBKIT_SCRIPT_DIALOG_IMPL(object); |
81 | WebKitScriptDialogImplPrivate* priv = dialog->priv; |
82 | |
83 | priv->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 20); |
84 | gtk_container_set_border_width(GTK_CONTAINER(priv->vbox), 0); |
85 | gtk_container_add(GTK_CONTAINER(dialog), priv->vbox); |
86 | gtk_widget_show(priv->vbox); |
87 | |
88 | GtkWidget* box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); |
89 | gtk_style_context_add_class(gtk_widget_get_style_context(box), GTK_STYLE_CLASS_TITLEBAR); |
90 | gtk_widget_set_size_request(box, -1, 16); |
91 | priv->title = gtk_label_new(nullptr); |
92 | gtk_label_set_ellipsize(GTK_LABEL(priv->title), PANGO_ELLIPSIZE_END); |
93 | gtk_widget_set_margin_top(priv->title, 6); |
94 | gtk_widget_set_margin_bottom(priv->title, 6); |
95 | gtk_style_context_add_class(gtk_widget_get_style_context(priv->title), GTK_STYLE_CLASS_TITLE); |
96 | gtk_box_set_center_widget(GTK_BOX(box), priv->title); |
97 | gtk_widget_show(priv->title); |
98 | gtk_box_pack_start(GTK_BOX(priv->vbox), box, TRUE, FALSE, 0); |
99 | gtk_widget_show(box); |
100 | |
101 | box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 30); |
102 | gtk_widget_set_margin_start(box, 30); |
103 | gtk_widget_set_margin_end(box, 30); |
104 | gtk_box_pack_start(GTK_BOX(priv->vbox), box, TRUE, FALSE, 0); |
105 | gtk_widget_show(box); |
106 | |
107 | GtkWidget* messageArea = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); |
108 | gtk_box_pack_start(GTK_BOX(box), messageArea, TRUE, TRUE, 0); |
109 | gtk_widget_show(messageArea); |
110 | |
111 | priv->swindow = gtk_scrolled_window_new(nullptr, nullptr); |
112 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(priv->swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); |
113 | gtk_box_pack_start(GTK_BOX(messageArea), priv->swindow, TRUE, TRUE, 0); |
114 | gtk_widget_show(priv->swindow); |
115 | |
116 | priv->label = gtk_label_new(nullptr); |
117 | gtk_widget_set_halign(priv->label, GTK_ALIGN_CENTER); |
118 | gtk_widget_set_valign(priv->label, GTK_ALIGN_START); |
119 | gtk_label_set_line_wrap(GTK_LABEL(priv->label), TRUE); |
120 | gtk_label_set_max_width_chars(GTK_LABEL(priv->label), 60); |
121 | gtk_container_add(GTK_CONTAINER(priv->swindow), priv->label); |
122 | gtk_widget_show(priv->label); |
123 | |
124 | GtkWidget* actionBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); |
125 | gtk_style_context_add_class(gtk_widget_get_style_context(actionBox), "dialog-action-box" ); |
126 | gtk_box_pack_end(GTK_BOX(priv->vbox), actionBox, FALSE, TRUE, 0); |
127 | gtk_widget_show(actionBox); |
128 | |
129 | priv->actionArea = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL); |
130 | gtk_button_box_set_layout(GTK_BUTTON_BOX(priv->actionArea), GTK_BUTTONBOX_EXPAND); |
131 | gtk_widget_set_hexpand(priv->actionArea, TRUE); |
132 | gtk_style_context_add_class(gtk_widget_get_style_context(priv->actionArea), "dialog-action-area" ); |
133 | gtk_box_pack_end(GTK_BOX(actionBox), priv->actionArea, FALSE, TRUE, 0); |
134 | gtk_widget_show(priv->actionArea); |
135 | } |
136 | |
137 | static void webkitScriptDialogImplDispose(GObject* object) |
138 | { |
139 | auto* dialog = WEBKIT_SCRIPT_DIALOG_IMPL(object); |
140 | if (dialog->priv->dialog) { |
141 | dialog->priv->dialog->nativeDialog = nullptr; |
142 | webkit_script_dialog_unref(dialog->priv->dialog); |
143 | dialog->priv->dialog = nullptr; |
144 | } |
145 | |
146 | G_OBJECT_CLASS(webkit_script_dialog_impl_parent_class)->dispose(object); |
147 | } |
148 | |
149 | static void webkit_script_dialog_impl_class_init(WebKitScriptDialogImplClass* klass) |
150 | { |
151 | GObjectClass* objectClass = G_OBJECT_CLASS(klass); |
152 | objectClass->constructed = webkitScriptDialogImplConstructed; |
153 | objectClass->dispose = webkitScriptDialogImplDispose; |
154 | |
155 | GtkWidgetClass* widgetClass = GTK_WIDGET_CLASS(klass); |
156 | widgetClass->key_press_event = webkitScriptDialogImplKeyPressEvent; |
157 | widgetClass->map = webkitScriptDialogImplMap; |
158 | gtk_widget_class_set_accessible_role(widgetClass, ATK_ROLE_ALERT); |
159 | } |
160 | |
161 | static void webkitScriptDialogImplSetText(WebKitScriptDialogImpl* dialog, const char* text, GtkRequisition* maxSize) |
162 | { |
163 | WebKitScriptDialogImplPrivate* priv = dialog->priv; |
164 | gtk_label_set_text(GTK_LABEL(priv->label), text); |
165 | GtkRequisition naturalRequisition; |
166 | gtk_widget_get_preferred_size(priv->label, nullptr, &naturalRequisition); |
167 | gtk_widget_set_size_request(priv->swindow, std::min(naturalRequisition.width, maxSize->width), std::min(maxSize->height, naturalRequisition.height)); |
168 | } |
169 | |
170 | static GtkWidget* webkitScriptDialogImplAddButton(WebKitScriptDialogImpl* dialog, const char* text) |
171 | { |
172 | WebKitScriptDialogImplPrivate* priv = dialog->priv; |
173 | GtkWidget* button = gtk_button_new_with_label(text); |
174 | gtk_button_set_use_underline(GTK_BUTTON(button), TRUE); |
175 | gtk_style_context_add_class(gtk_widget_get_style_context(button), "text-button" ); |
176 | gtk_widget_set_can_default(button, TRUE); |
177 | |
178 | gtk_widget_set_valign(button, GTK_ALIGN_BASELINE); |
179 | gtk_container_add(GTK_CONTAINER(priv->actionArea), button); |
180 | gtk_widget_show(button); |
181 | |
182 | return button; |
183 | } |
184 | |
185 | GtkWidget* webkitScriptDialogImplNew(WebKitScriptDialog* scriptDialog, const char* title, GtkRequisition* maxSize) |
186 | { |
187 | auto* dialog = WEBKIT_SCRIPT_DIALOG_IMPL(g_object_new(WEBKIT_TYPE_SCRIPT_DIALOG_IMPL, nullptr)); |
188 | dialog->priv->dialog = webkit_script_dialog_ref(scriptDialog); |
189 | dialog->priv->dialog->nativeDialog = GTK_WIDGET(dialog); |
190 | |
191 | switch (scriptDialog->type) { |
192 | case WEBKIT_SCRIPT_DIALOG_ALERT: { |
193 | gtk_label_set_text(GTK_LABEL(dialog->priv->title), title); |
194 | |
195 | GtkWidget* button = webkitScriptDialogImplAddButton(dialog, _("_Close" )); |
196 | dialog->priv->defaultButton = button; |
197 | g_signal_connect_swapped(button, "clicked" , G_CALLBACK(webkitScriptDialogImplCancel), dialog); |
198 | webkitScriptDialogImplSetText(dialog, scriptDialog->message.data(), maxSize); |
199 | break; |
200 | } |
201 | case WEBKIT_SCRIPT_DIALOG_PROMPT: |
202 | dialog->priv->entry = gtk_entry_new(); |
203 | gtk_entry_set_text(GTK_ENTRY(dialog->priv->entry), scriptDialog->defaultText.data()); |
204 | gtk_container_add(GTK_CONTAINER(dialog->priv->vbox), dialog->priv->entry); |
205 | gtk_entry_set_activates_default(GTK_ENTRY(dialog->priv->entry), TRUE); |
206 | gtk_widget_show(dialog->priv->entry); |
207 | |
208 | FALLTHROUGH; |
209 | case WEBKIT_SCRIPT_DIALOG_CONFIRM: { |
210 | gtk_label_set_text(GTK_LABEL(dialog->priv->title), title); |
211 | |
212 | GtkWidget* button = webkitScriptDialogImplAddButton(dialog, _("_Cancel" )); |
213 | g_signal_connect_swapped(button, "clicked" , G_CALLBACK(webkitScriptDialogImplCancel), dialog); |
214 | button = webkitScriptDialogImplAddButton(dialog, _("_OK" )); |
215 | dialog->priv->defaultButton = button; |
216 | g_signal_connect_swapped(button, "clicked" , G_CALLBACK(webkitScriptDialogImplConfirm), dialog); |
217 | webkitScriptDialogImplSetText(dialog, scriptDialog->message.data(), maxSize); |
218 | break; |
219 | } |
220 | case WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM: { |
221 | gtk_label_set_text(GTK_LABEL(dialog->priv->title), _("Are you sure you want to leave this page?" )); |
222 | |
223 | GtkWidget* button = webkitScriptDialogImplAddButton(dialog, _("Stay on Page" )); |
224 | g_signal_connect_swapped(button, "clicked" , G_CALLBACK(webkitScriptDialogImplCancel), dialog); |
225 | button = webkitScriptDialogImplAddButton(dialog, _("Leave Page" )); |
226 | dialog->priv->defaultButton = button; |
227 | g_signal_connect_swapped(button, "clicked" , G_CALLBACK(webkitScriptDialogImplConfirm), dialog); |
228 | webkitScriptDialogImplSetText(dialog, scriptDialog->message.data(), maxSize); |
229 | break; |
230 | } |
231 | } |
232 | |
233 | return GTK_WIDGET(dialog); |
234 | } |
235 | |
236 | void webkitScriptDialogImplCancel(WebKitScriptDialogImpl* dialog) |
237 | { |
238 | switch (dialog->priv->dialog->type) { |
239 | case WEBKIT_SCRIPT_DIALOG_ALERT: |
240 | case WEBKIT_SCRIPT_DIALOG_PROMPT: |
241 | break; |
242 | case WEBKIT_SCRIPT_DIALOG_CONFIRM: |
243 | case WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM: |
244 | dialog->priv->dialog->confirmed = false; |
245 | break; |
246 | } |
247 | webkitScriptDialogImplClose(dialog); |
248 | } |
249 | |
250 | void webkitScriptDialogImplConfirm(WebKitScriptDialogImpl* dialog) |
251 | { |
252 | switch (dialog->priv->dialog->type) { |
253 | case WEBKIT_SCRIPT_DIALOG_ALERT: |
254 | break; |
255 | case WEBKIT_SCRIPT_DIALOG_PROMPT: |
256 | dialog->priv->dialog->text = gtk_entry_get_text(GTK_ENTRY(dialog->priv->entry)); |
257 | break; |
258 | case WEBKIT_SCRIPT_DIALOG_CONFIRM: |
259 | case WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM: |
260 | dialog->priv->dialog->confirmed = true; |
261 | break; |
262 | } |
263 | webkitScriptDialogImplClose(dialog); |
264 | } |
265 | |
266 | void webkitScriptDialogImplSetEntryText(WebKitScriptDialogImpl* dialog, const String& text) |
267 | { |
268 | if (dialog->priv->dialog->type != WEBKIT_SCRIPT_DIALOG_PROMPT) |
269 | return; |
270 | |
271 | gtk_entry_set_text(GTK_ENTRY(dialog->priv->entry), text.utf8().data()); |
272 | } |
273 | |