diff options
author | Patrick McDermott <pj@pehjota.net> | 2017-10-22 19:09:22 (EDT) |
---|---|---|
committer | Patrick McDermott <pj@pehjota.net> | 2017-10-22 19:09:22 (EDT) |
commit | 8fc032f2f64f4b7eb022adb0b1255a5a68df109f (patch) | |
tree | 31094e678eba9fc4a828ddb8a2948d105ff898bc /src/web-view.c | |
parent | 927c4ea37ca967e61831164bb4b7ea33e28e0d9d (diff) | |
parent | a0b9288a5e6fa943a6f2b7f418a12343649ed454 (diff) | |
download | marquee-8fc032f2f64f4b7eb022adb0b1255a5a68df109f.zip marquee-8fc032f2f64f4b7eb022adb0b1255a5a68df109f.tar.gz marquee-8fc032f2f64f4b7eb022adb0b1255a5a68df109f.tar.bz2 |
Merge branch 'gobjectification'
Diffstat (limited to 'src/web-view.c')
-rw-r--r-- | src/web-view.c | 782 |
1 files changed, 782 insertions, 0 deletions
diff --git a/src/web-view.c b/src/web-view.c new file mode 100644 index 0000000..2f6d765 --- /dev/null +++ b/src/web-view.c @@ -0,0 +1,782 @@ +/* + * Web view + * + * 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 "web-view.h" + +#include <stdlib.h> +#include <string.h> + +#include <glib.h> +#include <gtk/gtk.h> +#include <webkit2/webkit2.h> + +#include "config.h" +#include "tab-page.h" + +struct _MqWebView { + WebKitWebView parent_instance; + MqTabPage *tab_page; + gchar *uri; + MqConfig *config; + WebKitHitTestResult *hit_test_result; + WebKitHitTestResult *mouse_target_hit_test_result; +}; + +enum { + PROP_TAB_PAGE = 1, + PROP_REWRITTEN_URI, + N_PROPERTIES +}; + +static GParamSpec *obj_properties[N_PROPERTIES] = {NULL,}; + +struct _MqWebViewClass { + WebKitWebViewClass parent_class; +}; + +G_DEFINE_TYPE(MqWebView, mq_web_view, WEBKIT_TYPE_WEB_VIEW) + +#define WKCMA(ACTION) \ + WEBKIT_CONTEXT_MENU_ACTION_##ACTION + +static void +menu_open_link_activate_cb(GtkAction G_GNUC_UNUSED *action, MqWebView *web_view) +{ + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(web_view), + webkit_hit_test_result_get_link_uri( + web_view->hit_test_result)); +} + +static void +menu_open_link_tab_activate_cb(GtkAction G_GNUC_UNUSED *action, + MqWebView *web_view) +{ + mq_tab_page_new_relative( + webkit_hit_test_result_get_link_uri(web_view->hit_test_result), + web_view->tab_page); +} + +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(web_view->hit_test_result), + NULL + }; + mq_application_add_window( + mq_tab_page_get_application(web_view->tab_page), uris); +} + +static void +menu_open_image_activate_cb(GtkAction G_GNUC_UNUSED *action, + MqWebView *web_view) +{ + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(web_view), + webkit_hit_test_result_get_image_uri( + web_view->hit_test_result)); +} + +static void +menu_open_image_tab_activate_cb(GtkAction G_GNUC_UNUSED *action, + MqWebView *web_view) +{ + mq_tab_page_new_relative( + webkit_hit_test_result_get_image_uri(web_view->hit_test_result), + web_view->tab_page); +} + +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(web_view->hit_test_result), + NULL + }; + mq_application_add_window( + mq_tab_page_get_application(web_view->tab_page), uris); +} + +static void +menu_open_video_activate_cb(GtkAction G_GNUC_UNUSED *action, + MqWebView *web_view) +{ + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(web_view), + webkit_hit_test_result_get_media_uri( + web_view->hit_test_result)); +} + +static void +menu_open_video_tab_activate_cb(GtkAction G_GNUC_UNUSED *action, + MqWebView *web_view) +{ + mq_tab_page_new_relative( + webkit_hit_test_result_get_media_uri(web_view->hit_test_result), + web_view->tab_page); +} + +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(web_view->hit_test_result), + NULL + }; + mq_application_add_window( + mq_tab_page_get_application(web_view->tab_page), uris); +} + +static void +menu_open_audio_activate_cb(GtkAction G_GNUC_UNUSED *action, + MqWebView *web_view) +{ + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(web_view), + webkit_hit_test_result_get_media_uri( + web_view->hit_test_result)); +} + +static void +menu_open_audio_tab_activate_cb(GtkAction G_GNUC_UNUSED *action, + MqWebView *web_view) +{ + mq_tab_page_new_relative( + webkit_hit_test_result_get_media_uri(web_view->hit_test_result), + web_view->tab_page); +} + +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(web_view->hit_test_result), + NULL + }; + mq_application_add_window( + mq_tab_page_get_application(web_view->tab_page), uris); +} + +#define ITEM_DECLS \ + GtkAction *action; \ + WebKitContextMenuItem *menu_item; +#define ITEM_DECLS_NO_CUSTOM \ + WebKitContextMenuItem *menu_item; +#define NEW_CUSTOM_ITEM(NAME, LABEL) \ + do { \ + /* 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); \ + } while (0) +#define NEW_STOCK_ITEM(STOCK) \ + do { \ + menu_item = webkit_context_menu_item_new_from_stock_action( \ + WEBKIT_CONTEXT_MENU_ACTION_##STOCK); \ + webkit_context_menu_append(context_menu, menu_item); \ + } while (0) +#define NEW_SEPARATOR_ITEM() \ + do { \ + webkit_context_menu_append(context_menu, \ + webkit_context_menu_item_new_separator()); \ + } while (0) +#define RESTORE_ITEMS(ITEMS) \ + do { \ + for (; ITEMS; ITEMS = ITEMS->next) { \ + webkit_context_menu_append(context_menu, ITEMS->data); \ + g_object_unref(ITEMS->data); \ + } \ + } while (0) + +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) \ + do { \ + g_object_ref(items->data); \ + ITEMS = g_list_prepend(ITEMS, items->data); \ + } while (0) + +static gboolean +context_menu_cb(WebKitWebView *wk_web_view, WebKitContextMenu *context_menu, + GdkEvent G_GNUC_UNUSED *event, WebKitHitTestResult *hit_test_result) +{ + MqWebView *web_view; + 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; + + web_view = MQ_WEB_VIEW(wk_web_view); + + /* 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 (web_view->hit_test_result) { + g_object_unref(web_view->hit_test_result); + } + web_view->hit_test_result = hit_test_result; + g_object_ref(web_view->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 +uri_cb(WebKitWebView *wk_web_view, GParamSpec G_GNUC_UNUSED *param_spec) +{ + const gchar *uri; + MqWebView *web_view; + + web_view = MQ_WEB_VIEW(wk_web_view); + + if (web_view->uri) { + g_free(web_view->uri); + } + + uri = webkit_web_view_get_uri(wk_web_view); + + if (g_str_has_prefix(uri, "mq-about:")) { + web_view->uri = g_strconcat("about:", uri + strlen("mq-about:"), + NULL); + } else { + web_view->uri = g_strdup(uri); + } + + g_object_notify_by_pspec(G_OBJECT(web_view), + obj_properties[PROP_REWRITTEN_URI]); +} + +static void +clipboard_text_recv_cb(GtkClipboard G_GNUC_UNUSED *clipboard, + const gchar *text, MqWebView *web_view) +{ + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(web_view), text); +} + +/* This callback is a hack to determine on middle mouse click whether to open a + * link in a new tab or to load a URI from the primary clipboard. The WebKit1 + * API provided webkit_web_view_get_hit_test_result() which would have been + * easier. */ +static void +mouse_target_changed_cb(WebKitWebView *wk_web_view, + WebKitHitTestResult *hit_test_result, guint G_GNUC_UNUSED modifiers) +{ + MqWebView *web_view; + + web_view = MQ_WEB_VIEW(wk_web_view); + + web_view->mouse_target_hit_test_result = hit_test_result; + g_object_ref(web_view->mouse_target_hit_test_result); +} + +static gboolean +button_press_cb(GtkWidget *widget, GdkEventButton *event) +{ + MqWebView *web_view; + WebKitHitTestResult *hit_test_result; + GtkClipboard *clipboard; + + web_view = MQ_WEB_VIEW(widget); + + /* Make sure this is a middle mouse button press event. */ + if (event->button != 2) { + return FALSE; + } + + hit_test_result = web_view->mouse_target_hit_test_result; + + if (webkit_hit_test_result_context_is_link(hit_test_result)) { + mq_tab_page_new_relative( + webkit_hit_test_result_get_link_uri(hit_test_result), + web_view->tab_page); + } else if (webkit_hit_test_result_context_is_editable(hit_test_result)){ + /* Let WebKit handle pasting from the primary clipboard into an + * editable element. */ + g_object_unref(hit_test_result); + return FALSE; + } else { + clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY); + gtk_clipboard_request_text(clipboard, + (GtkClipboardTextReceivedFunc) clipboard_text_recv_cb, + web_view); + } + + g_object_unref(hit_test_result); + + return TRUE; +} + +static void +constructed(GObject *object) +{ + MqWebView *web_view; + gchar *rw_uri; + gchar *new_tab_page; + + if (G_OBJECT_CLASS(mq_web_view_parent_class)->constructed) { + G_OBJECT_CLASS(mq_web_view_parent_class)->constructed(object); + } + + web_view = MQ_WEB_VIEW(object); + + web_view->config = mq_application_get_config( + mq_tab_page_get_application(web_view->tab_page)); + + webkit_web_view_set_settings(WEBKIT_WEB_VIEW(web_view), + mq_application_get_webkit_settings( + mq_tab_page_get_application(web_view->tab_page))); + + if (web_view->uri) { + if (g_str_has_prefix(web_view->uri, "about:")) { + rw_uri = g_strconcat("mq-about:", + web_view->uri + strlen("about:"), NULL); + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(web_view), + rw_uri); + g_free(rw_uri); + } else { + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(web_view), + web_view->uri); + } + } else { + new_tab_page = mq_config_get_string(web_view->config, + "tabs.new"); + if (g_strcmp0(new_tab_page, "home") == 0) { + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(web_view), + mq_config_get_string(web_view->config, + "tabs.home")); + } else if (g_strcmp0(new_tab_page, "blank") == 0) { + /* Don't load any URI. */ + } else { + g_assert_not_reached(); + } + } + + mq_web_view_zoom_reset(web_view); + + gtk_widget_set_vexpand(GTK_WIDGET(web_view), TRUE); + /* FIXME: This doesn't seem to be working. */ + gtk_widget_grab_focus(GTK_WIDGET(web_view)); +} + +static void +finalize(GObject *object) +{ + MqWebView *web_view; + + web_view = MQ_WEB_VIEW(object); + + if (web_view->uri) { + g_free(web_view->uri); + } + + G_OBJECT_CLASS(mq_web_view_parent_class)->finalize(object); +} + +static void +get_property(GObject *object, guint property_id, GValue *value, + GParamSpec *param_spec) +{ + MqWebView *web_view; + + web_view = MQ_WEB_VIEW(object); + + switch (property_id) { + case PROP_TAB_PAGE: + g_value_set_object(value, web_view->tab_page); + break; + case PROP_REWRITTEN_URI: + g_value_set_string(value, web_view->uri); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, + param_spec); + break; + } +} + +static void +set_property(GObject *object, guint property_id, const GValue *value, + GParamSpec *param_spec) +{ + MqWebView *web_view; + + web_view = MQ_WEB_VIEW(object); + + switch (property_id) { + case PROP_TAB_PAGE: + web_view->tab_page = g_value_get_object(value); + break; + case PROP_REWRITTEN_URI: + mq_web_view_load_uri(web_view, + g_value_get_string(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, + param_spec); + break; + } +} + +static void +mq_web_view_class_init(MqWebViewClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->constructed = constructed; + object_class->finalize = finalize; + object_class->get_property = get_property; + object_class->set_property = set_property; + + obj_properties[PROP_TAB_PAGE] = g_param_spec_object( + "tab-page", + "MqTabPage", + "The parent MqTabPage instance", + MQ_TYPE_TAB_PAGE, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); + obj_properties[PROP_REWRITTEN_URI] = g_param_spec_string( + "rewritten-uri", + "URI", + "The current active URI of the Web view, " + "with \"mq-about:\" rewritten to \"about:\"", + "", + G_PARAM_READWRITE | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); + g_object_class_install_properties(object_class, N_PROPERTIES, + obj_properties); +} + +static void +mq_web_view_init(MqWebView *web_view) +{ + web_view->hit_test_result = NULL; + + g_signal_connect(web_view, "context-menu", + G_CALLBACK(context_menu_cb), NULL); + g_signal_connect(web_view, "notify::uri", + G_CALLBACK(uri_cb), NULL); + g_signal_connect(web_view, "mouse-target-changed", + G_CALLBACK(mouse_target_changed_cb), NULL); + g_signal_connect(web_view, "button-press-event", + G_CALLBACK(button_press_cb), NULL); +} + +MqWebView * +mq_web_view_new(MqTabPage *tab_page, const gchar *uri) +{ + return g_object_new(MQ_TYPE_WEB_VIEW, + "tab-page", tab_page, /* TODO: Use gtk_widget_get_parent()? */ + "rewritten-uri", uri, + "web-context", webkit_web_context_get_default(), + NULL); +} + +const gchar * +mq_web_view_get_uri(MqWebView *web_view) +{ + return web_view->uri; +} + +void +mq_web_view_load_uri(MqWebView *web_view, const gchar *uri) +{ + gchar *rw_uri; + + if (!uri) { + /* Happens during object construction. */ + return; + } + + if (web_view->uri) { + g_free(web_view->uri); + } + web_view->uri = g_strdup(uri); + + if (g_str_has_prefix(uri, "about:")) { + rw_uri = g_strconcat("mq-about:", uri + strlen("about:"), NULL); + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(web_view), rw_uri); + g_free(rw_uri); + } else { + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(web_view), uri); + } + + g_object_notify_by_pspec(G_OBJECT(web_view), + obj_properties[PROP_REWRITTEN_URI]); +} + +void +mq_web_view_zoom_in(MqWebView *web_view) +{ + gdouble zoom_level; + + zoom_level = webkit_web_view_get_zoom_level(WEBKIT_WEB_VIEW(web_view)); + zoom_level += 0.1; + if (zoom_level < 0) { + zoom_level = G_MAXDOUBLE; + } + webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(web_view), zoom_level); +} + +void +mq_web_view_zoom_out(MqWebView *web_view) +{ + gdouble zoom_level; + + zoom_level = webkit_web_view_get_zoom_level(WEBKIT_WEB_VIEW(web_view)); + zoom_level -= 0.1; + if (zoom_level < 0) { + zoom_level = 0; + } + webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(web_view), zoom_level); +} + +void +mq_web_view_zoom_reset(MqWebView *web_view) +{ + gdouble zoom_level; + + zoom_level = mq_config_get_double(web_view->config, "zoom.default"); + webkit_web_view_set_zoom_level(WEBKIT_WEB_VIEW(web_view), zoom_level); +} |