/* * URI entry * * 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 . */ #include "uri-entry.h" #include #include #include #include "../../i18n.h" #include "../../web-view.h" struct _MqUriEntry { GtkToolItem parent_instance; MqWebView *web_view; GtkWidget *uri_entry; gchar *hovered_link_uri; PangoAttrList *hovered_link_style; gboolean load_failed; }; enum { PROP_WEB_VIEW = 1, PROP_URI, N_PROPERTIES }; static GParamSpec *obj_properties[N_PROPERTIES] = {NULL,}; struct _MqUriEntryClass { GtkToolItemClass parent_class; }; G_DEFINE_TYPE(MqUriEntry, mq_uri_entry, GTK_TYPE_TOOL_ITEM) static void load_changed_cb(MqWebView *web_view, WebKitLoadEvent load_event, MqUriEntry *uri_entry) { const gchar *uri; switch (load_event) { case WEBKIT_LOAD_STARTED: case WEBKIT_LOAD_REDIRECTED: case WEBKIT_LOAD_COMMITTED: uri = mq_web_view_get_uri(web_view); gtk_entry_set_text(GTK_ENTRY(uri_entry->uri_entry), uri); gtk_entry_set_attributes(GTK_ENTRY(uri_entry->uri_entry), NULL); break; case WEBKIT_LOAD_FINISHED: gtk_entry_set_progress_fraction( GTK_ENTRY(uri_entry->uri_entry), 0.0); break; default: g_assert_not_reached(); break; } } static gboolean load_failed_cb(WebKitWebView G_GNUC_UNUSED *web_view, WebKitLoadEvent G_GNUC_UNUSED load_event, gchar G_GNUC_UNUSED *failing_uri, GError G_GNUC_UNUSED *error, MqUriEntry *uri_entry) { uri_entry->load_failed = TRUE; return FALSE; } /* TODO: key in URI bar should reset to URI of current hovered link, if * any, even if different from uri_entry->hovered_link_uri. */ static void mouse_target_changed_cb(MqWebView G_GNUC_UNUSED *web_view, WebKitHitTestResult *hit_test_result, guint G_GNUC_UNUSED modifiers, MqUriEntry *uri_entry) { const gchar *uri; uri = mq_web_view_get_uri(web_view); if (webkit_hit_test_result_context_is_link(hit_test_result)) { if (gtk_widget_has_focus(uri_entry->uri_entry)) { } else if (uri_entry->hovered_link_uri && g_strcmp0(gtk_entry_get_text( GTK_ENTRY(uri_entry->uri_entry)), uri_entry->hovered_link_uri) != 0) { /* The user has edited the hovered link URI in the URI * bar. */ } else if (g_strcmp0(gtk_entry_get_text( GTK_ENTRY(uri_entry->uri_entry)), uri) == 0) { g_free(uri_entry->hovered_link_uri); uri_entry->hovered_link_uri = g_strdup( webkit_hit_test_result_get_link_uri( hit_test_result)); gtk_entry_set_text(GTK_ENTRY(uri_entry->uri_entry), uri_entry->hovered_link_uri); gtk_entry_set_attributes(GTK_ENTRY(uri_entry->uri_entry), uri_entry->hovered_link_style); } else if (gtk_entry_get_attributes( GTK_ENTRY(uri_entry->uri_entry)) == uri_entry->hovered_link_style) { /* The URI bar's text differs from the Web view's URI * because the mouse was already targeting a different * link. */ g_free(uri_entry->hovered_link_uri); uri_entry->hovered_link_uri = g_strdup( webkit_hit_test_result_get_link_uri( hit_test_result)); gtk_entry_set_text(GTK_ENTRY(uri_entry->uri_entry), uri_entry->hovered_link_uri); } } else if (gtk_entry_get_attributes(GTK_ENTRY(uri_entry->uri_entry)) == uri_entry->hovered_link_style) { if (gtk_widget_has_focus(uri_entry->uri_entry)) { } else if (g_strcmp0(gtk_entry_get_text( GTK_ENTRY(uri_entry->uri_entry)), uri_entry->hovered_link_uri) == 0) { /* The user hasn't edited the hovered link URI in the * URI bar. */ g_free(uri_entry->hovered_link_uri); uri_entry->hovered_link_uri = NULL; gtk_entry_set_text(GTK_ENTRY(uri_entry->uri_entry), uri); gtk_entry_set_attributes(GTK_ENTRY(uri_entry->uri_entry), NULL); } } } static void load_progress_cb(WebKitWebView *web_view, GParamSpec G_GNUC_UNUSED *param_spec, MqUriEntry *uri_entry) { /* * If loading fails, the WebKitWebView's "estimated-load-progress" is * set to 1.0 after signals like "load-changed" and "load-failed" are * emitted. So the only way to avoid leaving behind a full progress bar * after, for example, canceling a page load is to save a flag on a * failed load and only update the progress bar if the flag is unset. */ if (uri_entry->load_failed) { uri_entry->load_failed = FALSE; return; } gtk_entry_set_progress_fraction(GTK_ENTRY(uri_entry->uri_entry), webkit_web_view_get_estimated_load_progress(web_view)); } static void uri_cb(MqWebView *web_view, GParamSpec G_GNUC_UNUSED *param_spec, MqUriEntry *uri_entry) { const gchar *uri; uri = mq_web_view_get_uri(web_view); gtk_entry_set_text(GTK_ENTRY(uri_entry->uri_entry), uri); gtk_entry_set_attributes(GTK_ENTRY(uri_entry->uri_entry), NULL); } static void uri_activate_cb(GtkEntry *entry, MqUriEntry *uri_entry) { const gchar *uri; uri = gtk_entry_get_text(GTK_ENTRY(entry)); mq_web_view_load_uri(uri_entry->web_view, uri); mq_web_view_grab_focus(uri_entry->web_view); } static void set_uri(MqUriEntry *uri_entry, const gchar *uri) { if (uri) { gtk_entry_set_text(GTK_ENTRY(uri_entry->uri_entry), uri); } } static void set_web_view(MqUriEntry *uri_entry, MqWebView *web_view) { uri_entry->web_view = web_view; g_signal_connect(web_view, "load-changed", G_CALLBACK(load_changed_cb), uri_entry); g_signal_connect(web_view, "load-failed", G_CALLBACK(load_failed_cb), uri_entry); g_signal_connect(web_view, "mouse-target-changed", G_CALLBACK(mouse_target_changed_cb), uri_entry); g_signal_connect(web_view, "notify::estimated-load-progress", G_CALLBACK(load_progress_cb), uri_entry); g_signal_connect(web_view, "notify::display-uri", G_CALLBACK(uri_cb), uri_entry); } static void get_property(GObject *object, guint property_id, GValue *value, GParamSpec *param_spec) { switch (property_id) { case PROP_URI: g_value_set_string(value, NULL); break; case PROP_WEB_VIEW: g_value_set_object(value, NULL); 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) { MqUriEntry *uri_entry; uri_entry = MQ_URI_ENTRY(object); switch (property_id) { case PROP_URI: set_uri(uri_entry, g_value_get_string(value)); break; case PROP_WEB_VIEW: set_web_view(uri_entry, g_value_get_object(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, param_spec); break; } } static void mq_uri_entry_class_init(MqUriEntryClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->get_property = get_property; object_class->set_property = set_property; obj_properties[PROP_URI] = g_param_spec_string( "uri", P_("URI"), P_("The initial URI in the URI entry"), "", G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); obj_properties[PROP_WEB_VIEW] = g_param_spec_object( "web-view", P_("MqWebView"), P_("The associated MqWebView instance"), MQ_TYPE_WEB_VIEW, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_properties(object_class, N_PROPERTIES, obj_properties); } static void mq_uri_entry_init(MqUriEntry *uri_entry) { uri_entry->uri_entry = gtk_entry_new(); gtk_entry_set_placeholder_text(GTK_ENTRY(uri_entry->uri_entry), _("URI...")); gtk_entry_set_icon_from_icon_name(GTK_ENTRY(uri_entry->uri_entry), GTK_ENTRY_ICON_PRIMARY, "text-html"); gtk_entry_set_progress_fraction(GTK_ENTRY(uri_entry->uri_entry), 0.0); gtk_entry_set_input_purpose(GTK_ENTRY(uri_entry->uri_entry), GTK_INPUT_PURPOSE_URL); g_signal_connect(uri_entry->uri_entry, "activate", G_CALLBACK(uri_activate_cb), uri_entry); gtk_container_add(GTK_CONTAINER(uri_entry), uri_entry->uri_entry); gtk_tool_item_set_expand(GTK_TOOL_ITEM(uri_entry), TRUE); uri_entry->hovered_link_uri = NULL; /* URI bar hovered link style */ uri_entry->hovered_link_style = pango_attr_list_new(); pango_attr_list_insert(uri_entry->hovered_link_style, pango_attr_style_new(PANGO_STYLE_ITALIC)); uri_entry->load_failed = FALSE; } GtkToolItem * mq_uri_entry_new(MqWebView *web_view, const gchar *uri) { return g_object_new(MQ_TYPE_URI_ENTRY, "web-view", web_view, "uri", uri, NULL); } void mq_uri_entry_grab_focus(MqUriEntry *uri_entry) { gtk_widget_grab_focus(uri_entry->uri_entry); }