summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/local.mk1
-rw-r--r--src/uri-entry.c305
-rw-r--r--src/uri-entry.h54
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 */