summaryrefslogtreecommitdiffstats
path: root/src/web-view-schemes
diff options
context:
space:
mode:
authorPatrick McDermott <pj@pehjota.net>2017-10-30 04:12:46 (EDT)
committer Patrick McDermott <pj@pehjota.net>2017-10-30 04:12:46 (EDT)
commit926ee678fd39c7ee2c355579794c4b2e4ce7bfac (patch)
tree92438415495e2ce3d7ea2d3e0830f336fd4306d9 /src/web-view-schemes
parent9e5ef3089bbda8faa1fbdea937b76b69e1fe05a6 (diff)
downloadmarquee-926ee678fd39c7ee2c355579794c4b2e4ce7bfac.zip
marquee-926ee678fd39c7ee2c355579794c4b2e4ce7bfac.tar.gz
marquee-926ee678fd39c7ee2c355579794c4b2e4ce7bfac.tar.bz2
mq_web_view_normal_scheme_methods: Implement
Diffstat (limited to 'src/web-view-schemes')
-rw-r--r--src/web-view-schemes/local.mk2
-rw-r--r--src/web-view-schemes/normal.c768
2 files changed, 770 insertions, 0 deletions
diff --git a/src/web-view-schemes/local.mk b/src/web-view-schemes/local.mk
new file mode 100644
index 0000000..879ee6f
--- /dev/null
+++ b/src/web-view-schemes/local.mk
@@ -0,0 +1,2 @@
+marquee_SOURCES += \
+ %reldir%/normal.c
diff --git a/src/web-view-schemes/normal.c b/src/web-view-schemes/normal.c
new file mode 100644
index 0000000..3a0ccf2
--- /dev/null
+++ b/src/web-view-schemes/normal.c
@@ -0,0 +1,768 @@
+/*
+ * Web view members and methods for normal schemes (http, https, file, about)
+ *
+ * Copyright (C) 2017 Patrick McDermott
+ *
+ * This file is part of Marquee.
+ *
+ * Marquee is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Marquee is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Marquee. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "schemes.h"
+
+#include <string.h>
+
+#include <glib.h>
+#include <webkit2/webkit2.h>
+
+#include "../application.h"
+#include "../config.h"
+#include "../notebook.h"
+#include "../tab-page.h"
+#include "../web-view.h"
+
+static gchar *
+rewrite_uri(MqWebViewScheme G_GNUC_UNUSED *scheme, const gchar *uri)
+{
+ if (g_str_has_prefix(uri, "about:")) {
+ return g_strconcat("mq-about:", uri + strlen("about:"), NULL);
+ } else {
+ return g_strdup(uri);
+ }
+}
+
+static gchar *
+display_uri(MqWebViewScheme G_GNUC_UNUSED *scheme, const gchar *uri)
+{
+ if (g_str_has_prefix(uri, "mq-about:")) {
+ return g_strconcat("about:", uri + strlen("mq-about:"), NULL);
+ } else {
+ return g_strdup(uri);
+ }
+}
+
+#define WKCMA(ACTION) \
+ WEBKIT_CONTEXT_MENU_ACTION_##ACTION
+
+static void
+menu_open_link_activate_cb(GtkAction G_GNUC_UNUSED *action, MqWebView *web_view)
+{
+ MqWebViewNormalScheme *scheme;
+
+ scheme = &mq_web_view_get_scheme(web_view)->normal;
+
+ mq_web_view_load_uri(web_view, webkit_hit_test_result_get_link_uri(
+ scheme->hit_test_result));
+}
+
+static void
+menu_open_link_tab_activate_cb(GtkAction G_GNUC_UNUSED *action,
+ MqWebView *web_view)
+{
+ MqWebViewNormalScheme *scheme;
+
+ scheme = &mq_web_view_get_scheme(web_view)->normal;
+
+ mq_notebook_insert_child(
+ MQ_NOTEBOOK(gtk_widget_get_parent(GTK_WIDGET(
+ mq_web_view_get_tab_page(web_view)))),
+ webkit_hit_test_result_get_link_uri(scheme->hit_test_result),
+ mq_web_view_get_tab_page(web_view),
+ !mq_config_get_boolean(mq_web_view_get_config(web_view),
+ "tabs.background"));
+}
+
+static void
+menu_open_link_win_activate_cb(GtkAction G_GNUC_UNUSED *action,
+ MqWebView *web_view)
+{
+ const gchar *uris[2] = {
+ webkit_hit_test_result_get_link_uri(mq_web_view_get_scheme(
+ web_view)->normal.hit_test_result),
+ NULL
+ };
+ mq_application_add_window(
+ mq_tab_page_get_application(mq_web_view_get_tab_page(web_view)),
+ uris);
+}
+
+static void
+menu_open_image_activate_cb(GtkAction G_GNUC_UNUSED *action,
+ MqWebView *web_view)
+{
+ MqWebViewNormalScheme *scheme;
+
+ scheme = &mq_web_view_get_scheme(web_view)->normal;
+
+ mq_web_view_load_uri(web_view, webkit_hit_test_result_get_image_uri(
+ scheme->hit_test_result));
+}
+
+static void
+menu_open_image_tab_activate_cb(GtkAction G_GNUC_UNUSED *action,
+ MqWebView *web_view)
+{
+ MqWebViewNormalScheme *scheme;
+
+ scheme = &mq_web_view_get_scheme(web_view)->normal;
+
+ mq_notebook_insert_child(
+ MQ_NOTEBOOK(gtk_widget_get_parent(GTK_WIDGET(
+ mq_web_view_get_tab_page(web_view)))),
+ webkit_hit_test_result_get_image_uri(scheme->hit_test_result),
+ mq_web_view_get_tab_page(web_view),
+ !mq_config_get_boolean(mq_web_view_get_config(web_view),
+ "tabs.background"));
+}
+
+static void
+menu_open_image_win_activate_cb(GtkAction G_GNUC_UNUSED *action,
+ MqWebView *web_view)
+{
+ const gchar *uris[2] = {
+ webkit_hit_test_result_get_image_uri(mq_web_view_get_scheme(
+ web_view)->normal.hit_test_result),
+ NULL
+ };
+ mq_application_add_window(
+ mq_tab_page_get_application(mq_web_view_get_tab_page(web_view)),
+ uris);
+}
+
+static void
+menu_open_video_activate_cb(GtkAction G_GNUC_UNUSED *action,
+ MqWebView *web_view)
+{
+ MqWebViewNormalScheme *scheme;
+
+ scheme = &mq_web_view_get_scheme(web_view)->normal;
+
+ mq_web_view_load_uri(web_view, webkit_hit_test_result_get_media_uri(
+ scheme->hit_test_result));
+}
+
+static void
+menu_open_video_tab_activate_cb(GtkAction G_GNUC_UNUSED *action,
+ MqWebView *web_view)
+{
+ MqWebViewNormalScheme *scheme;
+
+ scheme = &mq_web_view_get_scheme(web_view)->normal;
+
+ mq_notebook_insert_child(
+ MQ_NOTEBOOK(gtk_widget_get_parent(GTK_WIDGET(
+ mq_web_view_get_tab_page(web_view)))),
+ webkit_hit_test_result_get_media_uri(scheme->hit_test_result),
+ mq_web_view_get_tab_page(web_view),
+ !mq_config_get_boolean(mq_web_view_get_config(web_view),
+ "tabs.background"));
+}
+
+static void
+menu_open_video_win_activate_cb(GtkAction G_GNUC_UNUSED *action,
+ MqWebView *web_view)
+{
+ const gchar *uris[2] = {
+ webkit_hit_test_result_get_media_uri(mq_web_view_get_scheme(
+ web_view)->normal.hit_test_result),
+ NULL
+ };
+ mq_application_add_window(
+ mq_tab_page_get_application(mq_web_view_get_tab_page(web_view)),
+ uris);
+}
+
+static void
+menu_open_audio_activate_cb(GtkAction G_GNUC_UNUSED *action,
+ MqWebView *web_view)
+{
+ MqWebViewNormalScheme *scheme;
+
+ scheme = &mq_web_view_get_scheme(web_view)->normal;
+
+ mq_web_view_load_uri(web_view, webkit_hit_test_result_get_media_uri(
+ scheme->hit_test_result));
+}
+
+static void
+menu_open_audio_tab_activate_cb(GtkAction G_GNUC_UNUSED *action,
+ MqWebView *web_view)
+{
+ MqWebViewNormalScheme *scheme;
+
+ scheme = &mq_web_view_get_scheme(web_view)->normal;
+
+ mq_notebook_insert_child(
+ MQ_NOTEBOOK(gtk_widget_get_parent(GTK_WIDGET(
+ mq_web_view_get_tab_page(web_view)))),
+ webkit_hit_test_result_get_media_uri(scheme->hit_test_result),
+ mq_web_view_get_tab_page(web_view),
+ !mq_config_get_boolean(mq_web_view_get_config(web_view),
+ "tabs.background"));
+}
+
+static void
+menu_open_audio_win_activate_cb(GtkAction G_GNUC_UNUSED *action,
+ MqWebView *web_view)
+{
+ const gchar *uris[2] = {
+ webkit_hit_test_result_get_media_uri(mq_web_view_get_scheme(
+ web_view)->normal.hit_test_result),
+ NULL
+ };
+ mq_application_add_window(
+ mq_tab_page_get_application(mq_web_view_get_tab_page(web_view)),
+ uris);
+}
+
+#define ITEM_DECLS \
+ GtkAction *action; \
+ WebKitContextMenuItem *menu_item;
+#define ITEM_DECLS_NO_CUSTOM \
+ WebKitContextMenuItem *menu_item;
+#define NEW_CUSTOM_ITEM(NAME, LABEL) \
+ G_STMT_START { \
+ /* Don't blame me; blame WebKitGTK+ for using GtkAction. */ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ action = gtk_action_new(#NAME, (LABEL), NULL, NULL); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ g_signal_connect(action, "activate", \
+ G_CALLBACK(menu_##NAME##_activate_cb), web_view); \
+ menu_item = webkit_context_menu_item_new(action); \
+ webkit_context_menu_append(context_menu, menu_item); \
+ } G_STMT_END
+#define NEW_STOCK_ITEM(STOCK) \
+ G_STMT_START { \
+ menu_item = webkit_context_menu_item_new_from_stock_action( \
+ WEBKIT_CONTEXT_MENU_ACTION_##STOCK); \
+ webkit_context_menu_append(context_menu, menu_item); \
+ } G_STMT_END
+#define NEW_SEPARATOR_ITEM() \
+ G_STMT_START { \
+ webkit_context_menu_append(context_menu, \
+ webkit_context_menu_item_new_separator()); \
+ } G_STMT_END
+#define RESTORE_ITEMS(ITEMS) \
+ G_STMT_START { \
+ for (; ITEMS; ITEMS = ITEMS->next) { \
+ webkit_context_menu_append(context_menu, ITEMS->data); \
+ g_object_unref(ITEMS->data); \
+ } \
+ } G_STMT_END
+
+static void
+context_menu_link_cb(WebKitContextMenu *context_menu, MqWebView *web_view)
+{
+ ITEM_DECLS
+
+ NEW_CUSTOM_ITEM(open_link, "_Open Link");
+ NEW_CUSTOM_ITEM(open_link_tab, "Open Link in New _Tab");
+ NEW_CUSTOM_ITEM(open_link_win, "Open Link in New _Window");
+ NEW_SEPARATOR_ITEM(); /* --- */
+ NEW_STOCK_ITEM(DOWNLOAD_LINK_TO_DISK); /* _Download Linked File */
+ NEW_STOCK_ITEM(COPY_LINK_TO_CLIPBOARD); /* Copy Link Loc_ation */
+}
+
+static void
+context_menu_image_cb(WebKitContextMenu *context_menu, MqWebView *web_view)
+{
+ ITEM_DECLS
+
+ NEW_CUSTOM_ITEM(open_image, "Open _Image");
+ NEW_CUSTOM_ITEM(open_image_tab, "Open Image in New Tab");
+ NEW_CUSTOM_ITEM(open_image_win, "Open Image in New Window");
+ NEW_SEPARATOR_ITEM(); /* --- */
+ NEW_STOCK_ITEM(DOWNLOAD_IMAGE_TO_DISK); /* Sa_ve Image As */
+ NEW_STOCK_ITEM(COPY_IMAGE_TO_CLIPBOARD); /* Cop_y Image */
+ NEW_STOCK_ITEM(COPY_IMAGE_URL_TO_CLIPBOARD); /* Copy Image _Address */
+}
+
+static void
+context_menu_media_cb(WebKitContextMenu *context_menu, GList *media_ctrl_items,
+ GList *media_toggle_items, gboolean is_video, MqWebView *web_view)
+{
+ ITEM_DECLS
+
+ /* _Play/_Pause, _Mute */
+ RESTORE_ITEMS(media_ctrl_items);
+ /* _Toggle Media Controls, Toggle Media _Loop Playback, Switch Video to
+ * _Fullscreen */
+ RESTORE_ITEMS(media_toggle_items);
+ NEW_SEPARATOR_ITEM(); /* --- */
+ if (is_video) {
+ NEW_CUSTOM_ITEM(open_video, "_Open Video");
+ NEW_CUSTOM_ITEM(open_video_tab, "Open Video in New Tab");
+ NEW_CUSTOM_ITEM(open_video_win, "Open Video in New Window");
+ NEW_SEPARATOR_ITEM(); /* --- */
+ NEW_STOCK_ITEM(DOWNLOAD_VIDEO_TO_DISK); /* Download _Video */
+ /* Cop_y Video Link Location */
+ NEW_STOCK_ITEM(COPY_VIDEO_LINK_TO_CLIPBOARD);
+ } else {
+ NEW_CUSTOM_ITEM(open_audio, "_Open Audio");
+ NEW_CUSTOM_ITEM(open_audio_tab, "Open Audio in New Tab");
+ NEW_CUSTOM_ITEM(open_audio_win, "Open Audio in New Window");
+ NEW_SEPARATOR_ITEM(); /* --- */
+ NEW_STOCK_ITEM(DOWNLOAD_AUDIO_TO_DISK); /* Download _Audio */
+ /* Cop_y Audio Link Location */
+ NEW_STOCK_ITEM(COPY_AUDIO_LINK_TO_CLIPBOARD);
+ }
+}
+
+static void
+context_menu_editable_cb(WebKitContextMenu *context_menu,
+ GList *spell_repl_items, GList *spell_ctrl_items, GList *edit_items,
+ GList *input_items, MqWebView G_GNUC_UNUSED *web_view)
+{
+ ITEM_DECLS_NO_CUSTOM
+
+ RESTORE_ITEMS(spell_repl_items); /* Spelling suggestions */
+ NEW_SEPARATOR_ITEM(); /* --- */
+ RESTORE_ITEMS(spell_ctrl_items); /* _Ignore Spelling, _Learn Spelling */
+ NEW_SEPARATOR_ITEM(); /* --- */
+ RESTORE_ITEMS(edit_items); /* Cu_t, _Copy, _Paste, _Delete */
+ NEW_SEPARATOR_ITEM(); /* --- */
+ NEW_STOCK_ITEM(SELECT_ALL); /* Select _All */
+ NEW_SEPARATOR_ITEM(); /* --- */
+ RESTORE_ITEMS(input_items); /* _Insert Unicode Character */
+}
+static void
+context_menu_document_cb(WebKitContextMenu *context_menu, GList *nav_items,
+ MqWebView G_GNUC_UNUSED *web_view)
+{
+ ITEM_DECLS_NO_CUSTOM
+
+ RESTORE_ITEMS(nav_items); /* _Back, _Forward, _Stop, _Reload */
+ NEW_SEPARATOR_ITEM(); /* --- */
+ NEW_STOCK_ITEM(SELECT_ALL); /* Select _All */
+ NEW_SEPARATOR_ITEM(); /* --- */
+ /* View Page Source */
+}
+
+#define PRESERVE_ITEM(ITEMS) \
+ G_STMT_START { \
+ g_object_ref(items->data); \
+ ITEMS = g_list_prepend(ITEMS, items->data); \
+ } G_STMT_END
+
+static gboolean
+context_menu(MqWebView *web_view, MqWebViewScheme G_GNUC_UNUSED *scheme,
+ WebKitContextMenu *context_menu, GdkEvent G_GNUC_UNUSED *event,
+ WebKitHitTestResult *hit_test_result)
+{
+ GList *items;
+ GList *nav_items;
+ GList *edit_items;
+ GList *input_items;
+ GList *spell_repl_items;
+ GList *spell_ctrl_items;
+ GList *media_ctrl_items;
+ GList *media_toggle_items;
+ gboolean is_selection;
+ gboolean is_video;
+ WebKitContextMenuAction stock_action;
+ WebKitHitTestResultContext context;
+ gboolean context_handled;
+ WebKitContextMenuItem *menu_item;
+
+ /* Get more hints about the context, since WebKit doesn't describe
+ * context very well in hit test results. Also, preserve menu items
+ * that aren't easy to reproduce (e.g. the Unicode menu and spelling
+ * guesses). */
+ items = webkit_context_menu_get_items(context_menu);
+ nav_items = NULL;
+ edit_items = NULL;
+ input_items = NULL;
+ spell_repl_items = NULL;
+ spell_ctrl_items = NULL;
+ media_ctrl_items = NULL;
+ media_toggle_items = NULL;
+ is_selection = FALSE;
+ is_video = FALSE;
+ for (; items; items = items->next) {
+ stock_action = webkit_context_menu_item_get_stock_action(
+ items->data);
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wswitch"
+ switch (stock_action) {
+ case WKCMA(GO_BACK):
+ case WKCMA(GO_FORWARD):
+ case WKCMA(STOP):
+ case WKCMA(RELOAD):
+ PRESERVE_ITEM(nav_items);
+ break;
+ case WKCMA(COPY):
+ PRESERVE_ITEM(edit_items);
+ is_selection = TRUE;
+ break;
+ case WKCMA(CUT):
+ case WKCMA(PASTE):
+ case WKCMA(DELETE):
+ PRESERVE_ITEM(edit_items);
+ break;
+ case WKCMA(INPUT_METHODS):
+ case WKCMA(UNICODE):
+ PRESERVE_ITEM(input_items);
+ break;
+ case WKCMA(SPELLING_GUESS):
+ case WKCMA(NO_GUESSES_FOUND):
+ PRESERVE_ITEM(spell_repl_items);
+ break;
+ case WKCMA(IGNORE_SPELLING):
+ case WKCMA(LEARN_SPELLING):
+ case WKCMA(IGNORE_GRAMMAR):
+ PRESERVE_ITEM(spell_ctrl_items);
+ break;
+ case WKCMA(OPEN_VIDEO_IN_NEW_WINDOW):
+ case WKCMA(COPY_VIDEO_LINK_TO_CLIPBOARD):
+ case WKCMA(DOWNLOAD_VIDEO_TO_DISK):
+ is_video = TRUE;
+ break;
+ case WKCMA(OPEN_AUDIO_IN_NEW_WINDOW):
+ case WKCMA(COPY_AUDIO_LINK_TO_CLIPBOARD):
+ case WKCMA(DOWNLOAD_AUDIO_TO_DISK):
+ is_video = FALSE;
+ break;
+ case WKCMA(TOGGLE_MEDIA_CONTROLS):
+ case WKCMA(TOGGLE_MEDIA_LOOP):
+ case WKCMA(ENTER_VIDEO_FULLSCREEN):
+ PRESERVE_ITEM(media_toggle_items);
+ break;
+ case WKCMA(MEDIA_PLAY):
+ case WKCMA(MEDIA_PAUSE):
+ case WKCMA(MEDIA_MUTE):
+ PRESERVE_ITEM(media_ctrl_items);
+ break;
+ }
+#pragma GCC diagnostic pop
+ }
+ nav_items = g_list_reverse(nav_items);
+ edit_items = g_list_reverse(edit_items);
+ input_items = g_list_reverse(input_items);
+ spell_repl_items = g_list_reverse(spell_repl_items);
+ spell_ctrl_items = g_list_reverse(spell_ctrl_items);
+ media_ctrl_items = g_list_reverse(media_ctrl_items);
+ media_toggle_items = g_list_reverse(media_toggle_items);
+
+ /* Clear the menu. */
+ webkit_context_menu_remove_all(context_menu);
+
+ /* Get the reported context (which isn't very descriptive) and save the
+ * hit test result for use by action callbacks. */
+ context = webkit_hit_test_result_get_context(hit_test_result);
+ if (scheme->normal.hit_test_result) {
+ g_object_unref(scheme->normal.hit_test_result);
+ }
+ scheme->normal.hit_test_result = hit_test_result;
+ g_object_ref(scheme->normal.hit_test_result);
+ context_handled = FALSE;
+
+ /* Build the context menu. */
+ if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) {
+ context_menu_link_cb(context_menu, web_view);
+ context_handled = TRUE;
+ }
+ if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) {
+ if (context_handled) {
+ NEW_SEPARATOR_ITEM();
+ }
+ context_menu_image_cb(context_menu, web_view);
+ context_handled = TRUE;
+ }
+ if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_MEDIA) {
+ context_menu_media_cb(context_menu, media_ctrl_items,
+ media_toggle_items, is_video, web_view);
+ context_handled = TRUE;
+ }
+ if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_EDITABLE) {
+ context_menu_editable_cb(context_menu, spell_repl_items,
+ spell_ctrl_items, edit_items, input_items, web_view);
+ context_handled = TRUE;
+ }
+ if (!context_handled &&
+ context & WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT) {
+ if (is_selection) {
+ RESTORE_ITEMS(edit_items); /* _Copy */
+ context_handled = TRUE;
+ } else {
+ context_menu_document_cb(context_menu, nav_items,
+ web_view);
+ context_handled = TRUE;
+ }
+ }
+ if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT) {
+ if (context_handled) {
+ NEW_SEPARATOR_ITEM();
+ }
+ NEW_STOCK_ITEM(INSPECT_ELEMENT); /* Inspect Element */
+ }
+
+ /* Propagate the event further and show the context menu. */
+ return FALSE;
+}
+
+static void
+save_html_replace_cb(GFile *file, GAsyncResult *result, guchar *data)
+{
+ g_file_replace_contents_finish(file, result, NULL, NULL);
+ /* TODO: Error handling? */
+ g_free(data);
+}
+
+static void
+save_html_get_data_cb(WebKitWebResource *resource, GAsyncResult *result,
+ MqWebView *web_view)
+{
+ MqWebViewNormalScheme *scheme;
+ guchar *data;
+ gsize length;
+
+ scheme = &mq_web_view_get_scheme(web_view)->normal;
+
+ data = webkit_web_resource_get_data_finish(resource, result, &length,
+ NULL); /* TODO: Error handling? */
+ g_file_replace_contents_async(scheme->save_file,
+ (gchar *) data, length,
+ NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
+ (GAsyncReadyCallback) save_html_replace_cb, data);
+}
+
+static void
+save_mhtml_cb(WebKitWebView *web_view, GAsyncResult *result)
+{
+ webkit_web_view_save_to_file_finish(web_view, result, NULL);
+}
+
+static gchar *
+get_extension(gchar *filename)
+{
+ gchar *extension;
+
+ extension = strrchr(filename, '.');
+ if (extension) {
+ return extension;
+ } else {
+ return filename + strlen(filename);
+ }
+}
+
+static gboolean
+extension_is_html(const gchar *extension)
+{
+ return
+ extension[0] == '.' &&
+ extension[1] == 'h' &&
+ extension[2] == 't' &&
+ extension[3] == 'm' &&
+ (extension[4] == '\0' ||
+ (extension[4] == 'l' &&
+ extension[5] == '\0'));
+}
+
+static gboolean
+extension_is_mhtml(const gchar *extension)
+{
+ return
+ extension[0] == '.' &&
+ extension[1] == 'm' &&
+ extension[2] == 'h' &&
+ extension[3] == 't' &&
+ (extension[4] == '\0' ||
+ (extension[4] == 'm' &&
+ (extension[5] == '\0' ||
+ (extension[5] == 'l' &&
+ extension[6] == '\0'))));
+}
+
+static void
+save_html(MqWebView *web_view, GFile *file)
+{
+ mq_web_view_get_scheme(web_view)->normal.save_file = file;
+ webkit_web_resource_get_data(
+ webkit_web_view_get_main_resource(WEBKIT_WEB_VIEW(web_view)),
+ NULL, (GAsyncReadyCallback) save_html_get_data_cb, web_view);
+}
+
+static void
+save_mhtml(MqWebView *web_view, GFile *file)
+{
+ webkit_web_view_save_to_file(WEBKIT_WEB_VIEW(web_view), file,
+ WEBKIT_SAVE_MODE_MHTML, NULL,
+ (GAsyncReadyCallback) save_mhtml_cb, NULL);
+}
+
+static gchar *
+get_clean_title(MqWebView *web_view)
+{
+ const gchar *title;
+
+ title = webkit_web_view_get_title(WEBKIT_WEB_VIEW(web_view));
+ if (!title || title[0] == '\0') {
+ title = mq_web_view_get_uri(web_view);
+ }
+ if (!title || title[0] == '\0') {
+ title = "page";
+ }
+ return g_strdelimit(g_strdup(title),
+#ifdef G_OS_WIN32
+ "/\\:*\"?<>|",
+#else
+ "/",
+#endif
+ '_');
+}
+
+static void
+save_type_changed_cb(GtkComboBox *combo_box, GtkFileChooser *chooser)
+{
+ gchar *name;
+ gchar *extension;
+ const gchar *active_id;
+ gchar *new_name;
+
+ name = gtk_file_chooser_get_current_name(chooser);
+ extension = get_extension(name);
+
+ active_id = gtk_combo_box_get_active_id(combo_box);
+ if (g_strcmp0(active_id, "html") == 0) {
+ if (!extension_is_html(extension)) {
+ /* Extension is not "htm" or "html". */
+ extension[0] = '\0'; /* Remove extension. */
+ new_name = g_strconcat(name, ".html", NULL);
+ gtk_file_chooser_set_current_name(chooser, new_name);
+ g_free(new_name);
+ }
+ } else if (g_strcmp0(active_id, "mhtml") == 0) {
+ if (!extension_is_mhtml(extension)) {
+ /* Extension is not "mht", "mhtm", or "mhtml". */
+ extension[0] = '\0'; /* Remove extension. */
+ new_name = g_strconcat(name, ".mhtml", NULL);
+ gtk_file_chooser_set_current_name(chooser, new_name);
+ g_free(new_name);
+ }
+ }
+
+ g_free(name);
+}
+
+static void
+save_response_cb(GtkWidget *dialog, gint response_id, MqWebView *web_view)
+{
+ MqWebViewNormalScheme *scheme;
+ gchar *dir;
+ gchar *filename;
+ GFile *file;
+ gchar *extension;
+ const gchar *active_id;
+
+ scheme = &mq_web_view_get_scheme(web_view)->normal;
+
+ if (response_id == GTK_RESPONSE_ACCEPT) {
+ dir = gtk_file_chooser_get_current_folder(
+ GTK_FILE_CHOOSER(dialog));
+ if (dir) {
+ mq_config_set_string(mq_web_view_get_config(web_view),
+ "directories.downloads", dir);
+ g_free(dir);
+ mq_config_save(mq_web_view_get_config(web_view));
+ }
+
+ filename = gtk_file_chooser_get_filename(
+ GTK_FILE_CHOOSER(dialog));
+ file = g_file_new_for_path(filename);
+
+ active_id = gtk_combo_box_get_active_id(
+ scheme->save_type_combo_box);
+ if (g_strcmp0(active_id, "detect") == 0) {
+ extension = get_extension(filename);
+ if (extension_is_html(extension)) {
+ save_html(web_view, file);
+ } else {
+ save_mhtml(web_view, file);
+ }
+ } else if (g_strcmp0(active_id, "html") == 0) {
+ save_html(web_view, file);
+ } else if (g_strcmp0(active_id, "mhtml") == 0) {
+ save_mhtml(web_view, file);
+ }
+
+ g_free(filename);
+ }
+
+ gtk_widget_destroy(dialog);
+}
+
+static void
+save_file(MqWebView *web_view, MqWebViewScheme *scheme)
+{
+ GtkWidget *dialog;
+ GtkFileChooser *chooser;
+ gchar *dir;
+ gchar *title;
+ gchar *filename;
+ GtkWidget *type_combo_box;
+ GtkWidget *type_box;
+
+ dialog = gtk_file_chooser_dialog_new("Save File",
+ GTK_WINDOW(mq_tab_page_get_window(
+ mq_web_view_get_tab_page(web_view))),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ "_Cancel", GTK_RESPONSE_CANCEL,
+ "_Save", GTK_RESPONSE_ACCEPT,
+ NULL);
+ chooser = GTK_FILE_CHOOSER(dialog);
+
+ gtk_file_chooser_set_do_overwrite_confirmation(chooser, TRUE);
+
+ dir = mq_config_get_string(mq_web_view_get_config(web_view),
+ "directories.downloads");
+ gtk_file_chooser_set_current_folder(chooser, dir);
+ g_free(dir);
+
+ title = get_clean_title(web_view);
+ filename = g_strconcat(title, ".mhtml", NULL);
+ g_free(title);
+ gtk_file_chooser_set_current_name(chooser, filename);
+ g_free(filename);
+
+ mq_web_view_add_html_mhtml_file_chooser_filters(chooser);
+
+ type_combo_box = gtk_combo_box_text_new();
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(type_combo_box),
+ "detect", "By extension");
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(type_combo_box),
+ "html", "HTML document only");
+ gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(type_combo_box),
+ "mhtml", "MHTML archive with associated resources");
+ gtk_combo_box_set_active(GTK_COMBO_BOX(type_combo_box), 0);
+ g_signal_connect(type_combo_box, "changed",
+ G_CALLBACK(save_type_changed_cb), chooser);
+ scheme->normal.save_type_combo_box = GTK_COMBO_BOX(type_combo_box);
+
+ type_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start(GTK_BOX(type_box),
+ gtk_label_new("Save as file type:"), FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(type_box), type_combo_box, FALSE, FALSE, 0);
+ gtk_file_chooser_set_extra_widget(chooser, type_box);
+
+ g_signal_connect(dialog, "response",
+ G_CALLBACK(save_response_cb), web_view);
+ gtk_widget_show_all(dialog);
+}
+
+MqWebViewSchemeMethods mq_web_view_normal_scheme_methods = {
+ .rewrite_uri = rewrite_uri,
+ .display_uri = display_uri,
+ .context_menu = context_menu,
+ .save_file = save_file,
+};