diff options
-rw-r--r-- | src/local.mk | 1 | ||||
-rw-r--r-- | src/uri-entry.c | 305 | ||||
-rw-r--r-- | src/uri-entry.h | 54 |
3 files changed, 360 insertions, 0 deletions
diff --git a/src/local.mk b/src/local.mk index f21f2d0..172be5c 100644 --- a/src/local.mk +++ b/src/local.mk @@ -7,6 +7,7 @@ marquee_SOURCES += \ %reldir%/tab.c \ %reldir%/tab-chrome.c \ %reldir%/back-forward-button-box.c \ + %reldir%/uri-entry.c \ %reldir%/main-menu.c \ %reldir%/find-toolbar.c \ %reldir%/web-view.c \ diff --git a/src/uri-entry.c b/src/uri-entry.c new file mode 100644 index 0000000..a36434c --- /dev/null +++ b/src/uri-entry.c @@ -0,0 +1,305 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <gtk/gtk.h> +#include <webkit2/webkit2.h> + +#include "uri-entry.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; + } +} + +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: <Esc> 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 *paramspec, + 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 *paramspec, + 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); +} + +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::rewritten-uri", + G_CALLBACK(uri_cb), uri_entry); +} + +static void +get_property(GObject *object, guint property_id, GValue *value, + GParamSpec *pspec) +{ + 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, + pspec); + break; + } +} + +static void +set_property(GObject *object, guint property_id, const GValue *value, + GParamSpec *pspec) +{ + 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, + pspec); + 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", + "URI", + "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", + "MqWebView", + "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-x-generic"); + gtk_entry_set_progress_fraction(GTK_ENTRY(uri_entry->uri_entry), 0.0); + 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); +} diff --git a/src/uri-entry.h b/src/uri-entry.h new file mode 100644 index 0000000..8ec4b9f --- /dev/null +++ b/src/uri-entry.h @@ -0,0 +1,54 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +typedef struct _MqUriEntry MqUriEntry; +typedef struct _MqUriEntryClass MqUriEntryClass; + +#ifndef MQ_URI_ENTRY_H +#define MQ_URI_ENTRY_H + +#include <gtk/gtk.h> + +#include "web-view.h" + +G_BEGIN_DECLS + +#define MQ_TYPE_URI_ENTRY (mq_uri_entry_get_type()) +#define MQ_URI_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + MQ_TYPE_URI_ENTRY, MqUriEntry)) +#define MQ_IS_URI_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \ + MQ_TYPE_URI_ENTRY)) +#define MQ_URI_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \ + MQ_TYPE_URI_ENTRY, MqUriEntryClass)) +#define MQ_IS_URI_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE( (klass), \ + MQ_TYPE_URI_ENTRY)) +#define MQ_URI_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ + MQ_TYPE_URI_ENTRY, MqUriEntryClass)) + +GType +mq_uri_entry_get_type(void); + +GtkToolItem * +mq_uri_entry_new(MqWebView *web_view, const gchar *uri); + +G_END_DECLS + +#endif /* MQ_URI_ENTRY_H */ |