summaryrefslogtreecommitdiffstats
path: root/src/toolbars
diff options
context:
space:
mode:
authorPatrick McDermott <pj@pehjota.net>2017-10-13 15:58:41 (EDT)
committer Patrick McDermott <pj@pehjota.net>2017-10-13 15:58:41 (EDT)
commit69bc9ea5a935f03e3be9c97686120af7adc0e176 (patch)
tree9767b3836437810e9e9bf9df5d3ee9a4b6f676d8 /src/toolbars
parent3fe94fa38e0529969285ab119bad2b7ebe920ab6 (diff)
downloadmarquee-69bc9ea5a935f03e3be9c97686120af7adc0e176.zip
marquee-69bc9ea5a935f03e3be9c97686120af7adc0e176.tar.gz
marquee-69bc9ea5a935f03e3be9c97686120af7adc0e176.tar.bz2
Move toolbar widget source files to src/toolbars/
And move navigation toolbar item widget source files to src/toolbars/navigation/.
Diffstat (limited to 'src/toolbars')
-rw-r--r--src/toolbars/find-toolbar.c325
-rw-r--r--src/toolbars/find-toolbar.h61
-rw-r--r--src/toolbars/local.mk5
-rw-r--r--src/toolbars/navigation-toolbar.c254
-rw-r--r--src/toolbars/navigation-toolbar.h62
-rw-r--r--src/toolbars/navigation/back-forward-button-box.c443
-rw-r--r--src/toolbars/navigation/back-forward-button-box.h59
-rw-r--r--src/toolbars/navigation/home-button.c159
-rw-r--r--src/toolbars/navigation/home-button.h56
-rw-r--r--src/toolbars/navigation/local.mk6
-rw-r--r--src/toolbars/navigation/main-menu.c297
-rw-r--r--src/toolbars/navigation/main-menu.h57
-rw-r--r--src/toolbars/navigation/stop-reload-button.c196
-rw-r--r--src/toolbars/navigation/stop-reload-button.h59
-rw-r--r--src/toolbars/navigation/uri-entry.c306
-rw-r--r--src/toolbars/navigation/uri-entry.h55
16 files changed, 2400 insertions, 0 deletions
diff --git a/src/toolbars/find-toolbar.c b/src/toolbars/find-toolbar.c
new file mode 100644
index 0000000..0b16ea1
--- /dev/null
+++ b/src/toolbars/find-toolbar.c
@@ -0,0 +1,325 @@
+/*
+ * Revealable find toolbar
+ *
+ * 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 "find-toolbar.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+#include "../web-view.h"
+
+struct _MqFindToolbar {
+ GtkRevealer parent_instance;
+ GtkWidget *search_entry;
+ GtkWidget *matches_label;
+ gboolean match_case;
+ gboolean searching;
+ WebKitFindController *find_controller;
+};
+
+enum {
+ PROP_WEB_VIEW = 1,
+ N_PROPERTIES
+};
+
+static GParamSpec *obj_properties[N_PROPERTIES] = {NULL,};
+
+struct _MqFindToolbarClass {
+ GtkRevealerClass parent_class;
+};
+
+G_DEFINE_TYPE(MqFindToolbar, mq_find_toolbar, GTK_TYPE_REVEALER)
+
+static void
+search(MqFindToolbar *find_toolbar, gboolean forward)
+{
+ guint32 find_options;
+
+ find_options = WEBKIT_FIND_OPTIONS_WRAP_AROUND;
+ if (!find_toolbar->match_case) {
+ find_options |= WEBKIT_FIND_OPTIONS_CASE_INSENSITIVE;
+ }
+ if (!forward) {
+ find_options |= WEBKIT_FIND_OPTIONS_BACKWARDS;
+ }
+ webkit_find_controller_search(find_toolbar->find_controller,
+ gtk_entry_get_text(GTK_ENTRY(find_toolbar->search_entry)),
+ find_options, G_MAXUINT);
+ find_toolbar->searching = TRUE;
+}
+
+static void
+search_finished(MqFindToolbar *find_toolbar)
+{
+ find_toolbar->searching = FALSE;
+ webkit_find_controller_search_finish(find_toolbar->find_controller);
+}
+
+static void
+hide(MqFindToolbar *find_toolbar)
+{
+ gtk_revealer_set_reveal_child(GTK_REVEALER(find_toolbar),
+ FALSE);
+ gtk_label_set_text(GTK_LABEL(find_toolbar->matches_label), NULL);
+ search_finished(find_toolbar);
+}
+
+static void
+search_changed_cb(GtkSearchEntry G_GNUC_UNUSED *entry,
+ MqFindToolbar *find_toolbar)
+{
+ search(find_toolbar, TRUE);
+}
+
+static gboolean
+search_key_press_event_cb(GtkSearchEntry G_GNUC_UNUSED *entry,
+ GdkEventKey *event, MqFindToolbar *find_toolbar)
+{
+ switch (event->keyval) {
+ case GDK_KEY_Escape:
+ hide(find_toolbar);
+ return TRUE;
+ case GDK_KEY_Return:
+ case GDK_KEY_KP_Enter:
+ case GDK_KEY_ISO_Enter:
+ search(find_toolbar,
+ !(event->state & GDK_SHIFT_MASK));
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static void
+prev_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqFindToolbar *find_toolbar)
+{
+ /* Calling this method before webkit_find_controller_search() or
+ * webkit_find_controller_count_matches() is a programming error. */
+ if (find_toolbar->searching) {
+ webkit_find_controller_search_previous(
+ find_toolbar->find_controller);
+ } else {
+ search(find_toolbar, FALSE);
+ }
+}
+
+static void
+next_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqFindToolbar *find_toolbar)
+{
+ /* Calling this method before webkit_find_controller_search() or
+ * webkit_find_controller_count_matches() is a programming error. */
+ if (find_toolbar->searching) {
+ webkit_find_controller_search_next(
+ find_toolbar->find_controller);
+ } else {
+ search(find_toolbar, TRUE);
+ }
+}
+
+static void
+match_case_toggled_cb(GtkToggleButton *toggle_button,
+ MqFindToolbar *find_toolbar)
+{
+ find_toolbar->match_case = gtk_toggle_button_get_active(toggle_button);
+ search(find_toolbar, TRUE);
+}
+
+static void
+close_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqFindToolbar *find_toolbar)
+{
+ hide(find_toolbar);
+}
+
+static void
+found_text_cb(WebKitFindController G_GNUC_UNUSED *find_controller,
+ guint match_count, MqFindToolbar *find_toolbar)
+{
+ gchar *text;
+
+ if (match_count == 1) {
+ text = g_strdup("1 match");
+ } else {
+ text = g_strdup_printf("%u matches", match_count);
+ }
+ gtk_label_set_text(GTK_LABEL(find_toolbar->matches_label), text);
+ g_free(text);
+
+}
+
+static void
+failed_to_find_text_cb(WebKitFindController G_GNUC_UNUSED *find_controller,
+ MqFindToolbar *find_toolbar)
+{
+ gtk_label_set_text(GTK_LABEL(find_toolbar->matches_label),
+ "No matches");
+}
+
+static void
+set_web_view(MqFindToolbar *find_toolbar, MqWebView *web_view)
+{
+ find_toolbar->find_controller = webkit_web_view_get_find_controller(
+ WEBKIT_WEB_VIEW(web_view));
+ g_signal_connect(find_toolbar->find_controller, "found-text",
+ G_CALLBACK(found_text_cb), find_toolbar);
+ g_signal_connect(find_toolbar->find_controller, "failed-to-find-text",
+ G_CALLBACK(failed_to_find_text_cb), find_toolbar);
+}
+
+static void
+get_property(GObject *object, guint property_id, GValue *value,
+ GParamSpec *param_spec)
+{
+ switch (property_id) {
+ 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)
+{
+ MqFindToolbar *find_toolbar;
+
+ find_toolbar = MQ_FIND_TOOLBAR(object);
+
+ switch (property_id) {
+ case PROP_WEB_VIEW:
+ set_web_view(find_toolbar, g_value_get_object(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id,
+ param_spec);
+ break;
+ }
+}
+
+static void
+mq_find_toolbar_class_init(MqFindToolbarClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ 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_find_toolbar_init(MqFindToolbar *find_toolbar)
+{
+ GtkWidget *close_button;
+ GtkWidget *prev_button;
+ GtkWidget *next_button;
+ GtkWidget *match_case_button;
+ GtkWidget *box;
+
+ /* Search entry */
+ find_toolbar->search_entry = gtk_search_entry_new();
+ g_signal_connect(find_toolbar->search_entry, "search-changed",
+ G_CALLBACK(search_changed_cb), find_toolbar);
+ g_signal_connect(find_toolbar->search_entry, "key-press-event",
+ G_CALLBACK(search_key_press_event_cb), find_toolbar);
+
+ /* Previous button */
+ prev_button = gtk_button_new_from_icon_name("go-up",
+ GTK_ICON_SIZE_BUTTON);
+ gtk_widget_set_tooltip_text(prev_button, "Find previous occurrence");
+ gtk_widget_set_can_focus(prev_button, FALSE);
+ g_signal_connect(prev_button, "clicked",
+ G_CALLBACK(prev_clicked_cb), find_toolbar);
+
+ /* Next button */
+ next_button = gtk_button_new_from_icon_name("go-down",
+ GTK_ICON_SIZE_BUTTON);
+ gtk_widget_set_tooltip_text(next_button, "Find next occurrence");
+ gtk_widget_set_can_focus(next_button, FALSE);
+ g_signal_connect(next_button, "clicked",
+ G_CALLBACK(next_clicked_cb), find_toolbar);
+
+ /* Case sensitivity button */
+ match_case_button = gtk_toggle_button_new_with_label("Match case");
+ gtk_widget_set_tooltip_text(match_case_button,
+ "Search with case sensitivity");
+ gtk_widget_set_can_focus(match_case_button, FALSE);
+ g_signal_connect(match_case_button, "toggled",
+ G_CALLBACK(match_case_toggled_cb), find_toolbar);
+
+ /* Matches label */
+ find_toolbar->matches_label = gtk_label_new(NULL);
+
+ /* Close button */
+ close_button = gtk_button_new_from_icon_name("window-close",
+ GTK_ICON_SIZE_BUTTON);
+ gtk_button_set_relief(GTK_BUTTON(close_button), GTK_RELIEF_NONE);
+ gtk_widget_set_tooltip_text(close_button, "Close find bar");
+ gtk_widget_set_can_focus(close_button, FALSE);
+ g_signal_connect(close_button, "clicked",
+ G_CALLBACK(close_clicked_cb), find_toolbar);
+
+ /* Box */
+ box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start(GTK_BOX(box), find_toolbar->search_entry,
+ FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(box), prev_button, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(box), next_button, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(box), match_case_button, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(box), find_toolbar->matches_label,
+ FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(box), close_button, FALSE, FALSE, 0);
+
+ /* Revealer */
+ gtk_revealer_set_transition_type(GTK_REVEALER(find_toolbar),
+ GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN);
+ gtk_container_add(GTK_CONTAINER(find_toolbar), box);
+
+ find_toolbar->match_case = FALSE;
+ find_toolbar->searching = FALSE;
+}
+
+GtkWidget *
+mq_find_toolbar_new(MqWebView *web_view)
+{
+ return g_object_new(MQ_TYPE_FIND_TOOLBAR,
+ "web-view", web_view,
+ NULL);
+}
+
+void
+mq_find_toolbar_reveal(MqFindToolbar *find_toolbar)
+{
+ gtk_revealer_set_reveal_child(GTK_REVEALER(find_toolbar), TRUE);
+ gtk_widget_grab_focus(find_toolbar->search_entry);
+}
diff --git a/src/toolbars/find-toolbar.h b/src/toolbars/find-toolbar.h
new file mode 100644
index 0000000..386cab4
--- /dev/null
+++ b/src/toolbars/find-toolbar.h
@@ -0,0 +1,61 @@
+/*
+ * Revealable find toolbar
+ *
+ * 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 _MqFindToolbar MqFindToolbar;
+typedef struct _MqFindToolbarClass MqFindToolbarClass;
+
+#ifndef MQ_TOOLBARS_FIND_TOOLBAR_H
+#define MQ_TOOLBARS_FIND_TOOLBAR_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+#include "../web-view.h"
+
+G_BEGIN_DECLS
+
+#define MQ_TYPE_FIND_TOOLBAR (mq_find_toolbar_get_type())
+#define MQ_FIND_TOOLBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+ MQ_TYPE_FIND_TOOLBAR, MqFindToolbar))
+#define MQ_IS_FIND_TOOLBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+ MQ_TYPE_FIND_TOOLBAR))
+#define MQ_FIND_TOOLBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
+ MQ_TYPE_FIND_TOOLBAR, \
+ MqFindToolbarClass))
+#define MQ_IS_FIND_TOOLBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), \
+ MQ_TYPE_FIND_TOOLBAR))
+#define MQ_FIND_TOOLBAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
+ MQ_TYPE_FIND_TOOLBAR, \
+ MqFindToolbarClass))
+
+GType
+mq_find_toolbar_get_type(void);
+
+GtkWidget *
+mq_find_toolbar_new(MqWebView *web_view);
+
+void
+mq_find_toolbar_reveal(MqFindToolbar *find_toolbar);
+
+G_END_DECLS
+
+#endif /* MQ_TOOLBARS_FIND_TOOLBAR_H */
diff --git a/src/toolbars/local.mk b/src/toolbars/local.mk
new file mode 100644
index 0000000..ccfb75a
--- /dev/null
+++ b/src/toolbars/local.mk
@@ -0,0 +1,5 @@
+marquee_SOURCES += \
+ %reldir%/navigation-toolbar.c \
+ %reldir%/find-toolbar.c
+
+include %reldir%/navigation/local.mk
diff --git a/src/toolbars/navigation-toolbar.c b/src/toolbars/navigation-toolbar.c
new file mode 100644
index 0000000..8a6ecfe
--- /dev/null
+++ b/src/toolbars/navigation-toolbar.c
@@ -0,0 +1,254 @@
+/*
+ * Navigation toolbar
+ *
+ * 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 "navigation-toolbar.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+#include "../config.h"
+#include "../tab.h"
+#include "../web-view.h"
+#include "find-toolbar.h"
+#include "navigation/back-forward-button-box.h"
+#include "navigation/home-button.h"
+#include "navigation/main-menu.h"
+#include "navigation/stop-reload-button.h"
+#include "navigation/uri-entry.h"
+
+struct _MqNavigationToolbar {
+ GtkToolbar parent_instance;
+ MqConfig *config;
+ MqTab *tab;
+ MqFindToolbar *find_toolbar;
+ MqWebView *web_view;
+ gchar *uri;
+};
+
+enum {
+ PROP_CONFIG = 1,
+ PROP_TAB,
+ PROP_FIND_TOOLBAR,
+ PROP_WEB_VIEW,
+ PROP_URI,
+ N_PROPERTIES
+};
+
+static GParamSpec *obj_properties[N_PROPERTIES] = {NULL,};
+
+struct _MqNavigationToolbarClass {
+ GtkToolbarClass parent_class;
+};
+
+G_DEFINE_TYPE(MqNavigationToolbar, mq_navigation_toolbar, GTK_TYPE_TOOLBAR)
+
+static void
+constructed(GObject *object)
+{
+ MqNavigationToolbar *navigation_toolbar;
+ GtkToolItem *back_forward_button_box;
+ GtkToolItem *stop_reload_button;
+ GtkToolItem *uri_entry;
+ GtkToolItem *home_button;
+ GtkToolItem *menu_button;
+
+ if (G_OBJECT_CLASS(mq_navigation_toolbar_parent_class)->constructed) {
+ G_OBJECT_CLASS(mq_navigation_toolbar_parent_class)->constructed(
+ object);
+ }
+
+ navigation_toolbar = MQ_NAVIGATION_TOOLBAR(object);
+
+ /* Back/forward button box */
+ back_forward_button_box = mq_back_forward_button_box_new(
+ navigation_toolbar->web_view);
+
+ /* Stop/reload button */
+ stop_reload_button = mq_stop_reload_button_new(
+ navigation_toolbar->web_view);
+
+ /* URI entry */
+ uri_entry = mq_uri_entry_new(navigation_toolbar->web_view,
+ navigation_toolbar->uri);
+
+ /* Home button */
+ home_button = mq_home_button_new(navigation_toolbar->config,
+ navigation_toolbar->web_view);
+
+ /* Menu button */
+ menu_button = mq_main_menu_new(navigation_toolbar->tab,
+ navigation_toolbar->find_toolbar, navigation_toolbar->web_view);
+
+ /* Navigation toolbar */
+ gtk_toolbar_insert(GTK_TOOLBAR(navigation_toolbar),
+ back_forward_button_box, -1);
+ gtk_toolbar_insert(GTK_TOOLBAR(navigation_toolbar),
+ stop_reload_button, -1);
+ gtk_toolbar_insert(GTK_TOOLBAR(navigation_toolbar), uri_entry, -1);
+ gtk_toolbar_insert(GTK_TOOLBAR(navigation_toolbar), home_button, -1);
+ gtk_toolbar_insert(GTK_TOOLBAR(navigation_toolbar), menu_button, -1);
+
+ gtk_widget_set_hexpand(GTK_WIDGET(navigation_toolbar), TRUE);
+}
+
+static void
+finalize(GObject *object)
+{
+ MqNavigationToolbar *navigation_toolbar;
+
+ navigation_toolbar = MQ_NAVIGATION_TOOLBAR(object);
+
+ if (navigation_toolbar->uri) {
+ g_free(navigation_toolbar->uri);
+ }
+}
+
+static void
+get_property(GObject *object, guint property_id, GValue *value,
+ GParamSpec *param_spec)
+{
+ MqNavigationToolbar *navigation_toolbar;
+
+ navigation_toolbar = MQ_NAVIGATION_TOOLBAR(object);
+
+ switch (property_id) {
+ case PROP_CONFIG:
+ g_value_set_pointer(value, navigation_toolbar->config);
+ break;
+ case PROP_TAB:
+ g_value_set_pointer(value, navigation_toolbar->tab);
+ break;
+ case PROP_FIND_TOOLBAR:
+ g_value_set_object(value,
+ navigation_toolbar->find_toolbar);
+ case PROP_WEB_VIEW:
+ g_value_set_object(value, navigation_toolbar->web_view);
+ break;
+ case PROP_URI:
+ g_value_set_string(value, navigation_toolbar->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)
+{
+ MqNavigationToolbar *navigation_toolbar;
+
+ navigation_toolbar = MQ_NAVIGATION_TOOLBAR(object);
+
+ switch (property_id) {
+ case PROP_CONFIG:
+ navigation_toolbar->config = g_value_get_pointer(value);
+ break;
+ case PROP_TAB:
+ navigation_toolbar->tab = g_value_get_pointer(value);
+ break;
+ case PROP_FIND_TOOLBAR:
+ navigation_toolbar->find_toolbar =
+ g_value_get_object(value);
+ break;
+ case PROP_WEB_VIEW:
+ navigation_toolbar->web_view =
+ g_value_get_object(value);
+ break;
+ case PROP_URI:
+ navigation_toolbar->uri = g_strdup(g_value_get_string(
+ value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id,
+ param_spec);
+ break;
+ }
+}
+
+static void
+mq_navigation_toolbar_class_init(MqNavigationToolbarClass *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_CONFIG] = g_param_spec_pointer(
+ "config",
+ "MqConfig",
+ "The application's MqConfig instance",
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+ obj_properties[PROP_TAB] = g_param_spec_pointer(
+ "tab",
+ "MqTab",
+ "The ancestral MqTab instance",
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+ obj_properties[PROP_FIND_TOOLBAR] = g_param_spec_object(
+ "find-toolbar",
+ "MqFindToolbar",
+ "The associated MqFindToolbar instance",
+ MQ_TYPE_FIND_TOOLBAR,
+ 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);
+ obj_properties[PROP_URI] = g_param_spec_string(
+ "uri",
+ "URI",
+ "The URI to load",
+ "",
+ 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_navigation_toolbar_init(
+ MqNavigationToolbar G_GNUC_UNUSED *navigation_toolbar)
+{
+}
+
+GtkWidget *
+mq_navigation_toolbar_new(MqConfig *config, MqTab *tab,
+ MqFindToolbar *find_toolbar, MqWebView *web_view, const gchar *uri)
+{
+ return g_object_new(MQ_TYPE_NAVIGATION_TOOLBAR,
+ "config", config,
+ "tab", tab,
+ "find-toolbar", find_toolbar,
+ "web-view", web_view,
+ "uri", uri,
+ NULL);
+}
diff --git a/src/toolbars/navigation-toolbar.h b/src/toolbars/navigation-toolbar.h
new file mode 100644
index 0000000..efaf2f6
--- /dev/null
+++ b/src/toolbars/navigation-toolbar.h
@@ -0,0 +1,62 @@
+/*
+ * Navigation toolbar
+ *
+ * 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 _MqNavigationToolbar MqNavigationToolbar;
+typedef struct _MqNavigationToolbarClass MqNavigationToolbarClass;
+
+#ifndef MQ_TOOLBARS_NAVIGATION_TOOLBAR_H
+#define MQ_TOOLBARS_NAVIGATION_TOOLBAR_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "../tab.h"
+#include "../web-view.h"
+#include "find-toolbar.h"
+
+G_BEGIN_DECLS
+
+#define MQ_TYPE_NAVIGATION_TOOLBAR \
+ (mq_navigation_toolbar_get_type())
+#define MQ_NAVIGATION_TOOLBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), MQ_TYPE_NAVIGATION_TOOLBAR, \
+ MqNavigationToolbar))
+#define MQ_IS_NAVIGATION_TOOLBAR(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), MQ_TYPE_NAVIGATION_TOOLBAR))
+#define MQ_NAVIGATION_TOOLBAR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), MQ_TYPE_NAVIGATION_TOOLBAR, \
+ MqNavigationToolbarClass))
+#define MQ_IS_NAVIGATION_TOOLBAR_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), MQ_TYPE_NAVIGATION_TOOLBAR))
+#define MQ_NAVIGATION_TOOLBAR_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), MQ_TYPE_NAVIGATION_TOOLBAR, \
+ MqNavigationToolbarClass))
+
+GType
+mq_navigation_toolbar_get_type(void);
+
+GtkWidget *
+mq_navigation_toolbar_new(MqConfig *config, MqTab *tab,
+ MqFindToolbar *find_toolbar, MqWebView *web_view, const gchar *uri);
+
+G_END_DECLS
+
+#endif /* MQ_TOOLBARS_NAVIGATION_TOOLBAR_H */
diff --git a/src/toolbars/navigation/back-forward-button-box.c b/src/toolbars/navigation/back-forward-button-box.c
new file mode 100644
index 0000000..491b939
--- /dev/null
+++ b/src/toolbars/navigation/back-forward-button-box.c
@@ -0,0 +1,443 @@
+/*
+ * Back/forward button box
+ *
+ * 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 "back-forward-button-box.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+#include "../../web-view.h"
+
+struct _MqBackForwardButtonBox {
+ GtkToolItem parent_instance;
+ MqWebView *web_view;
+ GtkWidget *back_button;
+ GtkWidget *forward_button;
+ gint back_items;
+ GtkWidget *back_forward_popover;
+};
+
+enum {
+ PROP_WEB_VIEW = 1,
+ N_PROPERTIES
+};
+
+static GParamSpec *obj_properties[N_PROPERTIES] = {NULL,};
+
+struct _MqBackForwardButtonBoxClass {
+ GtkToolItemClass parent_class;
+};
+
+G_DEFINE_TYPE(MqBackForwardButtonBox, mq_back_forward_button_box,
+ GTK_TYPE_TOOL_ITEM)
+
+static void
+back_forward_list_changed_cb(
+ WebKitBackForwardList G_GNUC_UNUSED *back_forward_list,
+ WebKitBackForwardListItem G_GNUC_UNUSED *item_added,
+ gpointer G_GNUC_UNUSED items_removed,
+ MqBackForwardButtonBox *back_forward_button_box)
+{
+ gtk_widget_set_sensitive(back_forward_button_box->back_button,
+ webkit_web_view_can_go_back(
+ WEBKIT_WEB_VIEW(back_forward_button_box->web_view)));
+ gtk_widget_set_sensitive(back_forward_button_box->forward_button,
+ webkit_web_view_can_go_forward(
+ WEBKIT_WEB_VIEW(back_forward_button_box->web_view)));
+}
+
+static void
+back_clicked_cb(GtkButton G_GNUC_UNUSED *toolbutton,
+ MqBackForwardButtonBox *back_forward_button_box)
+{
+ webkit_web_view_go_back(
+ WEBKIT_WEB_VIEW(back_forward_button_box->web_view));
+}
+
+static void
+forward_clicked_cb(GtkButton G_GNUC_UNUSED *toolbutton,
+ MqBackForwardButtonBox *back_forward_button_box)
+{
+ webkit_web_view_go_forward(
+ WEBKIT_WEB_VIEW(back_forward_button_box->web_view));
+}
+
+static GtkWidget *
+list_item_new(WebKitBackForwardListItem *list_item, gint type)
+{
+ GtkWidget *label;
+ GtkWidget *icon_stack;
+ GtkWidget *icon;
+ GtkWidget *box;
+
+ label = gtk_label_new(webkit_back_forward_list_item_get_title(
+ list_item));
+ gtk_widget_set_halign(label, GTK_ALIGN_START);
+
+ icon_stack = gtk_stack_new();
+ switch (type) {
+ case 0:
+ icon = gtk_radio_button_new(NULL);
+ gtk_stack_add_named(GTK_STACK(icon_stack), icon,
+ "current");
+ gtk_widget_set_can_focus(icon, FALSE);
+ gtk_stack_add_named(GTK_STACK(icon_stack),
+ gtk_image_new_from_icon_name("go-previous",
+ GTK_ICON_SIZE_BUTTON), "back");
+ break;
+ case -1:
+ gtk_stack_add_named(GTK_STACK(icon_stack),
+ gtk_image_new_from_icon_name("go-previous",
+ GTK_ICON_SIZE_BUTTON), "back");
+ gtk_stack_add_named(GTK_STACK(icon_stack),
+ gtk_radio_button_new(NULL), "current");
+ break;
+ case 1:
+ gtk_stack_add_named(GTK_STACK(icon_stack),
+ gtk_image_new_from_icon_name("go-next",
+ GTK_ICON_SIZE_BUTTON), "forward");
+ gtk_stack_add_named(GTK_STACK(icon_stack),
+ gtk_radio_button_new(NULL), "current");
+ break;
+ }
+
+ box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start(GTK_BOX(box), icon_stack, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0);
+ gtk_widget_set_tooltip_text(box,
+ webkit_back_forward_list_item_get_uri(list_item));
+
+ return box;
+}
+
+static void
+list_box_row_activated_cb(GtkListBox G_GNUC_UNUSED *box,
+ GtkListBoxRow *row, MqBackForwardButtonBox *back_forward_button_box)
+{
+ webkit_web_view_go_to_back_forward_list_item(
+ WEBKIT_WEB_VIEW(back_forward_button_box->web_view),
+ webkit_back_forward_list_get_nth_item(
+ webkit_web_view_get_back_forward_list(WEBKIT_WEB_VIEW(
+ back_forward_button_box->web_view)),
+ gtk_list_box_row_get_index(row) -
+ back_forward_button_box->back_items));
+
+ gtk_widget_hide(back_forward_button_box->back_forward_popover);
+}
+
+static void
+text_button_toggled_cb(GtkToggleButton *toggle_button,
+ GtkStack *stack)
+{
+ /* Use gtk_widget_show() and gtk_widget_hide() instead of
+ * gtk_stack_set_visible_child() so that the stack fits the size of only
+ * the visible child. */
+ gtk_widget_show_all(gtk_stack_get_child_by_name(stack,
+ gtk_toggle_button_get_active(toggle_button) ?
+ "text" : "list"));
+ gtk_widget_hide(gtk_stack_get_child_by_name(stack,
+ gtk_toggle_button_get_active(toggle_button) ?
+ "list" : "text"));
+}
+
+static gboolean
+event_box_button_press_cb(GtkWidget *widget,
+ GdkEventButton G_GNUC_UNUSED *event,
+ MqBackForwardButtonBox *back_forward_button_box)
+{
+ WebKitBackForwardList *back_forward_list;
+ GtkWidget *list_box;
+ GtkWidget *text_view;
+ GtkTextBuffer *text_buffer;
+ GList *list_item;
+ GtkTextIter text_iter;
+ gchar *str;
+ GtkTextTag *text_tag;
+ GtkWidget *list_scrolled_window;
+ GtkWidget *text_scrolled_window;
+ GtkWidget *stack;
+ GtkWidget *toggle_button;
+ GtkWidget *box;
+
+ /* Get the back/forward list for the Web view. */
+ back_forward_list = webkit_web_view_get_back_forward_list(
+ WEBKIT_WEB_VIEW(back_forward_button_box->web_view));
+
+ /* Set up the list box. */
+ list_box = gtk_list_box_new();
+ gtk_list_box_set_selection_mode(GTK_LIST_BOX(list_box),
+ GTK_SELECTION_BROWSE);
+ gtk_list_box_set_activate_on_single_click(GTK_LIST_BOX(list_box), TRUE);
+ g_signal_connect(list_box, "row-activated",
+ G_CALLBACK(list_box_row_activated_cb),
+ back_forward_button_box);
+
+ /* Set up the text view. */
+ text_view = gtk_text_view_new();
+ text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE);
+
+ /* Insert forward list items. */
+ /* The forward list is backwards, so we need to prepend each item to our
+ * list box and text buffer. */
+ list_item = webkit_back_forward_list_get_forward_list(
+ back_forward_list);
+ for (; list_item; list_item = list_item->next) {
+ gtk_list_box_prepend(GTK_LIST_BOX(list_box),
+ list_item_new(list_item->data, 1));
+ gtk_text_buffer_get_start_iter(text_buffer, &text_iter);
+ str = g_strdup_printf("\n%s",
+ webkit_back_forward_list_item_get_uri(list_item->data));
+ gtk_text_buffer_insert(text_buffer, &text_iter, str, -1);
+ g_free(str);
+ }
+
+ /* Insert the current item. */
+ gtk_list_box_prepend(GTK_LIST_BOX(list_box), list_item_new(
+ webkit_back_forward_list_get_current_item(
+ back_forward_list), 0));
+ gtk_list_box_select_row(GTK_LIST_BOX(list_box),
+ gtk_list_box_get_row_at_index(GTK_LIST_BOX(list_box), 0));
+ gtk_text_buffer_get_start_iter(text_buffer, &text_iter);
+ text_tag = gtk_text_buffer_create_tag(text_buffer, NULL, "weight",
+ PANGO_WEIGHT_BOLD, NULL);
+ gtk_text_buffer_insert_with_tags(text_buffer, &text_iter,
+ webkit_back_forward_list_item_get_uri(
+ webkit_back_forward_list_get_current_item(
+ back_forward_list)), -1, text_tag, NULL);
+
+ /* Insert back list items. */
+ list_item = webkit_back_forward_list_get_back_list(
+ back_forward_list);
+ back_forward_button_box->back_items = 0;
+ for (; list_item; list_item = list_item->next) {
+ gtk_list_box_prepend(GTK_LIST_BOX(list_box),
+ list_item_new(list_item->data, -1));
+ gtk_text_buffer_get_start_iter(text_buffer, &text_iter);
+ str = g_strdup_printf("%s\n",
+ webkit_back_forward_list_item_get_uri(list_item->data));
+ gtk_text_buffer_insert(text_buffer, &text_iter, str, -1);
+ g_free(str);
+ ++back_forward_button_box->back_items;
+ }
+
+ /*
+ * The following GtkScrolledWindow widgets have hardcoded minimum sizes,
+ * because there seems to be (in GTK+ versions before 3.22) no way to
+ * set the natural size of GtkScrolledWindow and its GtkViewport.
+ *
+ * I tried:
+ *
+ * - Setting size requests of the sizes of child widgets plus
+ * scrollbars (commit 2205669)
+ * - Putting GtkScrolledWindow widgets in stacks with non-scrolled
+ * versions of children, to allocate more area for the
+ * GtkScrolledWindow widgets to fill (commits 922cdef and 90686fa)
+ * - Setting policies to disable scrollbars upon GtkScrolledWindow
+ * instantiation, then enabling scrollbars on the GtkScrolledWindow
+ * "size-allocate" signal (commits c534c7e and 42ca783)
+ *
+ * Attempts to match the size of a GtkScrolledWindow's GtkViewport to
+ * its child's size don't allow the GtkScrolledWindow and GtkViewport to
+ * shrink when necessary to fit within the window. Therefore, such
+ * methods are equivalent to simply not using GtkScrolledWindow at all.
+ * And there appears to be no easy way to calculate the available size
+ * (the size of the GtkWindow minus all of the surrounding widgets) to
+ * manually manage a GtkViewport's size.
+ *
+ * GtkScrolledWindow or GtkViewport ignores the child's preferred
+ * (minimum and natural) sizes and apparently just sets a hardcoded size
+ * of about 64x64 px. I considered subclassing GtkScrolledWindow or
+ * GtkViewport, but I'm not sure where the hardcoded size is set.
+ *
+ * The functions gtk_scrolled_window_set_propagate_natural_width() and
+ * gtk_scrolled_window_set_propagate_natural_height() were introduced in
+ * GTK+ 3.22 and probably do what I want. However, I want to maintain
+ * compatibility with at least GTK+ 3.12 or 3.14.
+ */
+
+ /* Set up the list scrolled window. */
+ list_scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_min_content_width(
+ GTK_SCROLLED_WINDOW(list_scrolled_window), 400);
+ gtk_scrolled_window_set_min_content_height(
+ GTK_SCROLLED_WINDOW(list_scrolled_window), 200);
+ gtk_container_add(GTK_CONTAINER(list_scrolled_window), list_box);
+
+ /* Set up the text scrolled window. */
+ text_scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_min_content_width(
+ GTK_SCROLLED_WINDOW(text_scrolled_window), 400);
+ gtk_scrolled_window_set_min_content_height(
+ GTK_SCROLLED_WINDOW(text_scrolled_window), 200);
+ gtk_container_add(GTK_CONTAINER(text_scrolled_window), text_view);
+
+ /* Set up the stack. */
+ stack = gtk_stack_new();
+ gtk_stack_add_named(GTK_STACK(stack), list_scrolled_window, "list");
+ gtk_stack_add_named(GTK_STACK(stack), text_scrolled_window, "text");
+
+ /* Set up the toggle button. */
+ toggle_button = gtk_toggle_button_new();
+ gtk_button_set_image(GTK_BUTTON(toggle_button),
+ gtk_image_new_from_icon_name("edit-select-all",
+ GTK_ICON_SIZE_SMALL_TOOLBAR));
+ gtk_widget_set_halign(toggle_button, GTK_ALIGN_START);
+ g_signal_connect(toggle_button, "toggled",
+ G_CALLBACK(text_button_toggled_cb), GTK_STACK(stack));
+
+ /* Set up the containing box. */
+ box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+ gtk_box_pack_start(GTK_BOX(box), toggle_button, TRUE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(box), stack, TRUE, FALSE, 0);
+
+ /* Set up the popover. */
+ back_forward_button_box->back_forward_popover = gtk_popover_new(widget);
+ gtk_container_add(
+ GTK_CONTAINER(back_forward_button_box->back_forward_popover),
+ box);
+
+ /* NB: gtk_popover_popup() is new in GTK+ 3.22. */
+ gtk_widget_show_all(back_forward_button_box->back_forward_popover);
+ gtk_widget_hide(text_view);
+
+ return FALSE;
+}
+
+static void
+set_web_view(MqBackForwardButtonBox *back_forward_button_box,
+ MqWebView *web_view)
+{
+ back_forward_button_box->web_view = web_view;
+
+ g_signal_connect(webkit_web_view_get_back_forward_list(
+ WEBKIT_WEB_VIEW(web_view)),
+ "changed", G_CALLBACK(back_forward_list_changed_cb),
+ back_forward_button_box);
+}
+
+static void
+get_property(GObject *object, guint property_id, GValue *value,
+ GParamSpec *param_spec)
+{
+ MqBackForwardButtonBox *back_forward_button_box;
+
+ back_forward_button_box = MQ_BACK_FORWARD_BUTTON_BOX(object);
+
+ switch (property_id) {
+ case PROP_WEB_VIEW:
+ g_value_set_object(value,
+ back_forward_button_box->web_view);
+ 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)
+{
+ MqBackForwardButtonBox *back_forward_button_box;
+
+ back_forward_button_box = MQ_BACK_FORWARD_BUTTON_BOX(object);
+
+ switch (property_id) {
+ case PROP_WEB_VIEW:
+ set_web_view(back_forward_button_box,
+ g_value_get_object(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id,
+ param_spec);
+ break;
+ }
+}
+
+static void
+mq_back_forward_button_box_class_init(MqBackForwardButtonBoxClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ 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_back_forward_button_box_init(MqBackForwardButtonBox *back_forward_button_box)
+{
+ GtkWidget *box;
+ GtkWidget *event_box;
+
+ /* Back button */
+ back_forward_button_box->back_button = gtk_button_new_from_icon_name(
+ "go-previous", GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_widget_set_tooltip_text(back_forward_button_box->back_button,
+ "Go back one page");
+ g_signal_connect(back_forward_button_box->back_button, "clicked",
+ G_CALLBACK(back_clicked_cb), back_forward_button_box);
+
+ /* Forward button */
+ back_forward_button_box->forward_button = gtk_button_new_from_icon_name(
+ "go-next", GTK_ICON_SIZE_SMALL_TOOLBAR);
+ gtk_widget_set_tooltip_text(back_forward_button_box->forward_button,
+ "Go forward one page");
+ g_signal_connect(back_forward_button_box->forward_button, "clicked",
+ G_CALLBACK(forward_clicked_cb), back_forward_button_box);
+
+ /* Box */
+ box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start(GTK_BOX(box),
+ back_forward_button_box->back_button, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(box),
+ back_forward_button_box->forward_button, FALSE, FALSE, 0);
+ gtk_style_context_add_class(gtk_widget_get_style_context(box),
+ "linked");
+
+ /* Event box (MqBackForwardButtonBox) */
+ event_box = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(event_box), box);
+ g_signal_connect(event_box, "button-press-event",
+ G_CALLBACK(event_box_button_press_cb), back_forward_button_box);
+
+ gtk_container_add(GTK_CONTAINER(back_forward_button_box), event_box);
+}
+
+GtkToolItem *
+mq_back_forward_button_box_new(MqWebView *web_view)
+{
+ return g_object_new(MQ_TYPE_BACK_FORWARD_BUTTON_BOX,
+ "web-view", web_view,
+ NULL);
+}
diff --git a/src/toolbars/navigation/back-forward-button-box.h b/src/toolbars/navigation/back-forward-button-box.h
new file mode 100644
index 0000000..a11c4de
--- /dev/null
+++ b/src/toolbars/navigation/back-forward-button-box.h
@@ -0,0 +1,59 @@
+/*
+ * Back/forward button box
+ *
+ * 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 _MqBackForwardButtonBox MqBackForwardButtonBox;
+typedef struct _MqBackForwardButtonBoxClass MqBackForwardButtonBoxClass;
+
+#ifndef MQ_TOOLBARS_NAVIGATION_BACK_FORWARD_BUTTON_BOX_H
+#define MQ_TOOLBARS_NAVIGATION_BACK_FORWARD_BUTTON_BOX_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "../../web-view.h"
+
+G_BEGIN_DECLS
+
+#define MQ_TYPE_BACK_FORWARD_BUTTON_BOX \
+ (mq_back_forward_button_box_get_type())
+#define MQ_BACK_FORWARD_BUTTON_BOX(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), MQ_TYPE_BACK_FORWARD_BUTTON_BOX, \
+ MqBackForwardButtonBox))
+#define MQ_IS_BACK_FORWARD_BUTTON_BOX(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), MQ_TYPE_BACK_FORWARD_BUTTON_BOX))
+#define MQ_BACK_FORWARD_BUTTON_BOX_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), MQ_TYPE_BACK_FORWARD_BUTTON_BOX, \
+ MqBackForwardButtonBoxClass))
+#define MQ_IS_BACK_FORWARD_BUTTON_BOX_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), MQ_TYPE_BACK_FORWARD_BUTTON_BOX))
+#define MQ_BACK_FORWARD_BUTTON_BOX_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), MQ_TYPE_BACK_FORWARD_BUTTON_BOX, \
+ MqBackForwardButtonBoxClass))
+
+GType
+mq_back_forward_button_box_get_type(void);
+
+GtkToolItem *
+mq_back_forward_button_box_new(MqWebView *web_view);
+
+G_END_DECLS
+
+#endif /* MQ_TOOLBARS_NAVIGATION_BACK_FORWARD_BUTTON_BOX_H */
diff --git a/src/toolbars/navigation/home-button.c b/src/toolbars/navigation/home-button.c
new file mode 100644
index 0000000..a9d5f75
--- /dev/null
+++ b/src/toolbars/navigation/home-button.c
@@ -0,0 +1,159 @@
+/*
+ * Home button
+ *
+ * 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 "home-button.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+#include "../../config.h"
+#include "../../web-view.h"
+
+struct _MqHomeButton {
+ GtkToolButton parent_instance;
+ MqConfig *config;
+ MqWebView *web_view;
+ GtkWidget *stop_icon;
+ GtkWidget *reload_icon;
+};
+
+enum {
+ PROP_CONFIG = 1,
+ PROP_WEB_VIEW,
+ N_PROPERTIES
+};
+
+static GParamSpec *obj_properties[N_PROPERTIES] = {NULL,};
+
+struct _MqHomeButtonClass {
+ GtkToolButtonClass parent_class;
+};
+
+G_DEFINE_TYPE(MqHomeButton, mq_home_button, GTK_TYPE_TOOL_BUTTON)
+
+static void
+clicked_cb(MqHomeButton *home_button)
+{
+ const gchar *uri;
+
+ uri = mq_config_get_string(home_button->config, "tabs.home");
+
+ mq_web_view_load_uri(home_button->web_view, uri);
+}
+
+static void
+set_config(MqHomeButton *home_button, MqConfig *config)
+{
+ home_button->config = config;
+}
+
+static void
+set_web_view(MqHomeButton *home_button, MqWebView *web_view)
+{
+ home_button->web_view = web_view;
+}
+
+static void
+get_property(GObject *object, guint property_id, GValue *value,
+ GParamSpec *param_spec)
+{
+ switch (property_id) {
+ case PROP_CONFIG:
+ g_value_set_pointer(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)
+{
+ MqHomeButton *home_button;
+
+ home_button = MQ_HOME_BUTTON(object);
+
+ switch (property_id) {
+ case PROP_CONFIG:
+ set_config(home_button, g_value_get_pointer(value));
+ break;
+ case PROP_WEB_VIEW:
+ set_web_view(home_button, g_value_get_object(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id,
+ param_spec);
+ break;
+ }
+}
+
+static void
+mq_home_button_class_init(MqHomeButtonClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ obj_properties[PROP_CONFIG] = g_param_spec_pointer(
+ "config",
+ "MqConfig",
+ "The application's MqConfig instance",
+ 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_home_button_init(MqHomeButton *home_button)
+{
+ gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(home_button),
+ gtk_image_new_from_icon_name("go-home",
+ GTK_ICON_SIZE_SMALL_TOOLBAR));
+ gtk_tool_button_set_label(GTK_TOOL_BUTTON(home_button), "Home");
+ gtk_widget_set_tooltip_text(GTK_WIDGET(home_button),
+ "Load the home page");
+ g_signal_connect(home_button, "clicked", G_CALLBACK(clicked_cb), NULL);
+}
+
+GtkToolItem *
+mq_home_button_new(MqConfig *config, MqWebView *web_view)
+{
+ return g_object_new(MQ_TYPE_HOME_BUTTON,
+ "config", config,
+ "web-view", web_view,
+ NULL);
+}
diff --git a/src/toolbars/navigation/home-button.h b/src/toolbars/navigation/home-button.h
new file mode 100644
index 0000000..0eae927
--- /dev/null
+++ b/src/toolbars/navigation/home-button.h
@@ -0,0 +1,56 @@
+/*
+ * Home button
+ *
+ * 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 _MqHomeButton MqHomeButton;
+typedef struct _MqHomeButtonClass MqHomeButtonClass;
+
+#ifndef MQ_TOOLBARS_NAVIGATION_HOME_BUTTON_H
+#define MQ_TOOLBARS_NAVIGATION_HOME_BUTTON_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "../../config.h"
+#include "../../web-view.h"
+
+G_BEGIN_DECLS
+
+#define MQ_TYPE_HOME_BUTTON (mq_home_button_get_type())
+#define MQ_HOME_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+ MQ_TYPE_HOME_BUTTON, MqHomeButton))
+#define MQ_IS_HOME_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+ MQ_TYPE_HOME_BUTTON))
+#define MQ_HOME_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
+ MQ_TYPE_HOME_BUTTON, MqHomeButtonClass))
+#define MQ_IS_HOME_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), \
+ MQ_TYPE_HOME_BUTTON))
+#define MQ_HOME_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
+ MQ_TYPE_HOME_BUTTON, MqHomeButtonClass))
+
+GType
+mq_home_button_get_type(void);
+
+GtkToolItem *
+mq_home_button_new(MqConfig *config, MqWebView *web_view);
+
+G_END_DECLS
+
+#endif /* MQ_TOOLBARS_NAVIGATION_HOME_BUTTON_H */
diff --git a/src/toolbars/navigation/local.mk b/src/toolbars/navigation/local.mk
new file mode 100644
index 0000000..38f3213
--- /dev/null
+++ b/src/toolbars/navigation/local.mk
@@ -0,0 +1,6 @@
+marquee_SOURCES += \
+ %reldir%/back-forward-button-box.c \
+ %reldir%/stop-reload-button.c \
+ %reldir%/uri-entry.c \
+ %reldir%/home-button.c \
+ %reldir%/main-menu.c
diff --git a/src/toolbars/navigation/main-menu.c b/src/toolbars/navigation/main-menu.c
new file mode 100644
index 0000000..ad6809b
--- /dev/null
+++ b/src/toolbars/navigation/main-menu.c
@@ -0,0 +1,297 @@
+/*
+ * Main menu and button
+ *
+ * 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 "main-menu.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+#include "../../tab.h"
+#include "../../web-view.h"
+#include "../find-toolbar.h"
+
+struct _MqMainMenu {
+ GtkToolButton parent_instance;
+ MqTab *tab;
+ MqFindToolbar *find_toolbar;
+ MqWebView *web_view;
+ GtkWidget *popover;
+};
+
+enum {
+ PROP_TAB = 1,
+ PROP_FIND_TOOLBAR,
+ PROP_WEB_VIEW,
+ N_PROPERTIES
+};
+
+static GParamSpec *obj_properties[N_PROPERTIES] = {NULL,};
+
+struct _MqMainMenuClass {
+ GtkToolButtonClass parent_class;
+};
+
+G_DEFINE_TYPE(MqMainMenu, mq_main_menu, GTK_TYPE_TOOL_BUTTON)
+
+static void
+zoom_out_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu)
+{
+ mq_web_view_zoom_out(main_menu->web_view);
+}
+
+static void
+zoom_reset_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu)
+{
+ mq_web_view_zoom_reset(main_menu->web_view);
+}
+
+static void
+zoom_in_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu)
+{
+ mq_web_view_zoom_in(main_menu->web_view);
+}
+
+static void
+find_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu)
+{
+ gtk_widget_hide(main_menu->popover);
+ mq_find_toolbar_reveal(main_menu->find_toolbar);
+}
+
+static void
+fullscreen_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu)
+{
+ gtk_widget_hide(main_menu->popover);
+ mq_window_toggle_fullscreen(mq_tab_get_window(main_menu->tab));
+}
+
+static void
+developer_tools_clicked_cb(GtkButton G_GNUC_UNUSED *button,
+ MqMainMenu *main_menu)
+{
+ gtk_widget_hide(main_menu->popover);
+ webkit_web_inspector_show(webkit_web_view_get_inspector(
+ WEBKIT_WEB_VIEW(main_menu->web_view)));
+}
+
+static void
+preferences_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu)
+{
+ gtk_widget_hide(main_menu->popover);
+ mq_tab_new("about:preferences", main_menu->tab);
+ /* TODO: Hack: */
+ gtk_notebook_next_page(GTK_NOTEBOOK(main_menu->tab->window->notebook));
+}
+
+static void
+about_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu)
+{
+ gtk_widget_hide(main_menu->popover);
+ mq_tab_new("about:", main_menu->tab);
+ /* TODO: Hack: */
+ gtk_notebook_next_page(GTK_NOTEBOOK(main_menu->tab->window->notebook));
+}
+
+static void
+quit_clicked_cb(GtkButton G_GNUC_UNUSED *button, MqMainMenu *main_menu)
+{
+ /* mq_tab_quit() just calls mq_window_quit(), which just calls
+ * mq_application_quit(), which is asynchronous. So close the menu. */
+ gtk_widget_hide(main_menu->popover);
+ mq_tab_quit(main_menu->tab);
+}
+
+#define BUTTON_ROWS 6
+#define BUTTON_COLS 3
+#define NEW_BUTTON(Y, X, ICON, TOOLTIP) \
+ do { \
+ buttons[Y * BUTTON_COLS + X] = gtk_button_new_from_icon_name(\
+ ICON, GTK_ICON_SIZE_BUTTON); \
+ gtk_widget_set_tooltip_text(buttons[Y * BUTTON_COLS + X], \
+ TOOLTIP); \
+ gtk_grid_attach(GTK_GRID(grid), buttons[Y * BUTTON_COLS + X], \
+ X, Y, 1, 1); \
+ } while (0)
+#define CLICKED_CB(Y, X, CB) \
+ g_signal_connect(buttons[Y * BUTTON_COLS + X], "clicked", \
+ G_CALLBACK(CB), main_menu)
+
+static void
+menu_clicked_cb(MqMainMenu *main_menu)
+{
+ GtkWidget *grid;
+ GtkWidget *buttons[BUTTON_ROWS * BUTTON_COLS];
+
+ /* Set up the grid. */
+ grid = gtk_grid_new();
+
+ NEW_BUTTON(0, 0, "zoom-out", "Zoom out");
+ NEW_BUTTON(0, 1, "zoom-original", "Reset zoom");
+ NEW_BUTTON(0, 2, "zoom-in", "Zoom in");
+ NEW_BUTTON(1, 0, "document-open", "Open file");
+ NEW_BUTTON(1, 1, "document-save-as", "Save page");
+ NEW_BUTTON(1, 2, "mail-message-new", "E-mail link");
+ NEW_BUTTON(2, 0, "edit-find", "Find");
+ NEW_BUTTON(2, 1, "document-print-preview", "Print preview");
+ NEW_BUTTON(2, 2, "document-print", "Print");
+ NEW_BUTTON(3, 0, "bookmark-new", "Bookmarks");
+ NEW_BUTTON(3, 1, "document-open-recent", "History");
+ NEW_BUTTON(3, 2, "document-save", "Downloads");
+ NEW_BUTTON(4, 0, "view-fullscreen", "Full screen");
+ NEW_BUTTON(4, 1, "document-properties", "Developer tools");
+ NEW_BUTTON(4, 2, "system-run", "Preferences");
+ NEW_BUTTON(5, 0, "help-about", "About Marquee");
+ NEW_BUTTON(5, 2, "application-exit", "Quit");
+
+ CLICKED_CB(0, 0, zoom_out_clicked_cb);
+ CLICKED_CB(0, 1, zoom_reset_clicked_cb);
+ CLICKED_CB(0, 2, zoom_in_clicked_cb);
+ /* TODO: 1, 0: Open file */
+ /* TODO: 1, 1: Save page */
+ /* TODO: 1, 2: E-mail link */
+ CLICKED_CB(2, 0, find_clicked_cb);
+ /* TODO: 2, 1: Print preview */
+ /* TODO: 2, 2: Print */
+ /* TODO: 3, 0: Bookmarks */
+ /* TODO: 3, 1: History */
+ /* TODO: 3, 2: Downloads */
+ CLICKED_CB(4, 0, fullscreen_clicked_cb);
+ CLICKED_CB(4, 1, developer_tools_clicked_cb);
+ CLICKED_CB(4, 2, preferences_clicked_cb);
+ CLICKED_CB(5, 0, about_clicked_cb);
+ CLICKED_CB(5, 2, quit_clicked_cb);
+
+ /* Set up the popover. */
+ main_menu->popover = gtk_popover_new(GTK_WIDGET(main_menu));
+ gtk_container_add(GTK_CONTAINER(main_menu->popover), grid);
+
+ /* NB: gtk_popover_popup() is new in GTK+ 3.22. */
+ gtk_widget_show_all(main_menu->popover);
+}
+
+#undef BUTTON_ROWS
+#undef BUTTON_COLS
+#undef NEW_BUTTON
+
+static void
+get_property(GObject *object, guint property_id, GValue *value,
+ GParamSpec *param_spec)
+{
+ MqMainMenu *main_menu;
+
+ main_menu = MQ_MAIN_MENU(object);
+
+ switch (property_id) {
+ case PROP_TAB:
+ g_value_set_pointer(value, main_menu->tab);
+ break;
+ case PROP_FIND_TOOLBAR:
+ g_value_set_object(value, main_menu->find_toolbar);
+ break;
+ case PROP_WEB_VIEW:
+ g_value_set_object(value, main_menu->web_view);
+ 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)
+{
+ MqMainMenu *main_menu;
+
+ main_menu = MQ_MAIN_MENU(object);
+
+ switch (property_id) {
+ case PROP_TAB:
+ main_menu->tab = g_value_get_pointer(value);
+ break;
+ case PROP_FIND_TOOLBAR:
+ main_menu->find_toolbar = g_value_get_object(value);
+ break;
+ case PROP_WEB_VIEW:
+ main_menu->web_view = g_value_get_object(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id,
+ param_spec);
+ break;
+ }
+}
+
+static void
+mq_main_menu_class_init(MqMainMenuClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ obj_properties[PROP_TAB] = g_param_spec_pointer(
+ "tab",
+ "MqTab",
+ "The ancestral MqTab instance",
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+ obj_properties[PROP_FIND_TOOLBAR] = g_param_spec_object(
+ "find-toolbar",
+ "MqFindToolbar",
+ "The associated MqFindToolbar instance",
+ MQ_TYPE_FIND_TOOLBAR,
+ 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_main_menu_init(MqMainMenu *main_menu)
+{
+ gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(main_menu),
+ gtk_image_new_from_icon_name("open-menu-symbolic",
+ GTK_ICON_SIZE_SMALL_TOOLBAR));
+ gtk_tool_button_set_label(GTK_TOOL_BUTTON(main_menu), "Menu");
+ gtk_widget_set_tooltip_text(GTK_WIDGET(main_menu), "Open menu");
+ g_signal_connect(main_menu, "clicked",
+ G_CALLBACK(menu_clicked_cb), NULL);
+}
+
+GtkToolItem *
+mq_main_menu_new(MqTab *tab, MqFindToolbar *find_toolbar, MqWebView *web_view)
+{
+ return g_object_new(MQ_TYPE_MAIN_MENU,
+ "tab", tab,
+ "find-toolbar", find_toolbar,
+ "web-view", web_view,
+ NULL);
+}
diff --git a/src/toolbars/navigation/main-menu.h b/src/toolbars/navigation/main-menu.h
new file mode 100644
index 0000000..e1e85da
--- /dev/null
+++ b/src/toolbars/navigation/main-menu.h
@@ -0,0 +1,57 @@
+/*
+ * Main menu and button
+ *
+ * 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 _MqMainMenu MqMainMenu;
+typedef struct _MqMainMenuClass MqMainMenuClass;
+
+#ifndef MQ_TOOLBARS_NAVIGATION_MAIN_MENU_H
+#define MQ_TOOLBARS_NAVIGATION_MAIN_MENU_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "../../tab.h"
+#include "../../web-view.h"
+#include "../find-toolbar.h"
+
+G_BEGIN_DECLS
+
+#define MQ_TYPE_MAIN_MENU (mq_main_menu_get_type())
+#define MQ_MAIN_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
+ MQ_TYPE_MAIN_MENU, MqMainMenu))
+#define MQ_IS_MAIN_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), \
+ MQ_TYPE_MAIN_MENU))
+#define MQ_MAIN_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
+ MQ_TYPE_MAIN_MENU, MqMainMenuClass))
+#define MQ_IS_MAIN_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), \
+ MQ_TYPE_MAIN_MENU))
+#define MQ_MAIN_MENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \
+ MQ_TYPE_MAIN_MENU, MqMainMenuClass))
+
+GType
+mq_main_menu_get_type(void);
+
+GtkToolItem *
+mq_main_menu_new(MqTab *tab, MqFindToolbar *find_toolbar, MqWebView *web_view);
+
+G_END_DECLS
+
+#endif /* MQ_TOOLBARS_NAVIGATION_MAIN_MENU_H */
diff --git a/src/toolbars/navigation/stop-reload-button.c b/src/toolbars/navigation/stop-reload-button.c
new file mode 100644
index 0000000..1adaeeb
--- /dev/null
+++ b/src/toolbars/navigation/stop-reload-button.c
@@ -0,0 +1,196 @@
+/*
+ * Stop/reload button
+ *
+ * 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 "stop-reload-button.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+#include "../../web-view.h"
+
+struct _MqStopReloadButton {
+ GtkToolButton parent_instance;
+ GtkWidget *stop_icon;
+ GtkWidget *reload_icon;
+ MqWebView *web_view;
+};
+
+enum {
+ PROP_WEB_VIEW = 1,
+ N_PROPERTIES
+};
+
+static GParamSpec *obj_properties[N_PROPERTIES] = {NULL,};
+
+struct _MqStopReloadButtonClass {
+ GtkToolButtonClass parent_class;
+};
+
+G_DEFINE_TYPE(MqStopReloadButton, mq_stop_reload_button, GTK_TYPE_TOOL_BUTTON)
+
+static void
+loading_cb(MqWebView *web_view, GParamSpec G_GNUC_UNUSED *param_spec,
+ MqStopReloadButton *stop_reload_button)
+{
+ if (webkit_web_view_is_loading(WEBKIT_WEB_VIEW(web_view))) {
+ gtk_tool_button_set_icon_widget(
+ GTK_TOOL_BUTTON(stop_reload_button),
+ stop_reload_button->stop_icon);
+ gtk_tool_button_set_label(
+ GTK_TOOL_BUTTON(stop_reload_button), "Stop");
+ gtk_widget_set_tooltip_text(
+ GTK_WIDGET(stop_reload_button),
+ "Stop loading the current page");
+ } else {
+ gtk_tool_button_set_icon_widget(
+ GTK_TOOL_BUTTON(stop_reload_button),
+ stop_reload_button->reload_icon);
+ gtk_tool_button_set_label(
+ GTK_TOOL_BUTTON(stop_reload_button), "Reload");
+ gtk_widget_set_tooltip_text(
+ GTK_WIDGET(stop_reload_button),
+ "Reload the current page");
+ }
+ gtk_widget_show_all(GTK_WIDGET(stop_reload_button));
+}
+
+static void
+clicked_cb(MqStopReloadButton *stop_reload_button)
+{
+ WebKitWebView *web_view;
+
+ web_view = WEBKIT_WEB_VIEW(stop_reload_button->web_view);
+
+ if (webkit_web_view_is_loading(web_view)) {
+ webkit_web_view_stop_loading(web_view);
+ } else {
+ webkit_web_view_reload(web_view);
+ }
+}
+
+static void
+set_web_view(MqStopReloadButton *stop_reload_button, MqWebView *web_view)
+{
+ stop_reload_button->web_view = web_view;
+
+ g_signal_connect(web_view, "notify::is-loading",
+ G_CALLBACK(loading_cb), stop_reload_button);
+}
+
+static void
+finalize(GObject *object)
+{
+ MqStopReloadButton *stop_reload_button;
+
+ stop_reload_button = MQ_STOP_RELOAD_BUTTON(object);
+
+ g_object_unref(stop_reload_button->stop_icon);
+ g_object_unref(stop_reload_button->reload_icon);
+
+ G_OBJECT_CLASS(mq_stop_reload_button_parent_class)->finalize(object);
+}
+
+static void
+get_property(GObject *object, guint property_id, GValue *value,
+ GParamSpec *param_spec)
+{
+ switch (property_id) {
+ 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)
+{
+ MqStopReloadButton *stop_reload_button;
+
+ stop_reload_button = MQ_STOP_RELOAD_BUTTON(object);
+
+ switch (property_id) {
+ case PROP_WEB_VIEW:
+ set_web_view(stop_reload_button,
+ g_value_get_object(value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id,
+ param_spec);
+ break;
+ }
+}
+
+static void
+mq_stop_reload_button_class_init(MqStopReloadButtonClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->finalize = finalize;
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ 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_stop_reload_button_init(MqStopReloadButton *stop_reload_button)
+{
+ /* Stop icon */
+ stop_reload_button->stop_icon = gtk_image_new_from_icon_name(
+ "process-stop", GTK_ICON_SIZE_SMALL_TOOLBAR);
+ g_object_ref_sink(stop_reload_button->stop_icon);
+
+ /* Reload icon */
+ stop_reload_button->reload_icon = gtk_image_new_from_icon_name(
+ "view-refresh", GTK_ICON_SIZE_SMALL_TOOLBAR);
+ g_object_ref_sink(stop_reload_button->reload_icon);
+
+ /* Button */
+ gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(stop_reload_button),
+ stop_reload_button->stop_icon);
+ gtk_tool_button_set_label(GTK_TOOL_BUTTON(stop_reload_button), "Stop");
+ gtk_widget_set_tooltip_text(GTK_WIDGET(stop_reload_button),
+ "Stop loading the current page");
+ g_signal_connect(stop_reload_button, "clicked",
+ G_CALLBACK(clicked_cb), NULL);
+}
+
+GtkToolItem *
+mq_stop_reload_button_new(MqWebView *web_view)
+{
+ return g_object_new(MQ_TYPE_STOP_RELOAD_BUTTON,
+ "web-view", web_view,
+ NULL);
+}
diff --git a/src/toolbars/navigation/stop-reload-button.h b/src/toolbars/navigation/stop-reload-button.h
new file mode 100644
index 0000000..2b85109
--- /dev/null
+++ b/src/toolbars/navigation/stop-reload-button.h
@@ -0,0 +1,59 @@
+/*
+ * Stop/reload button
+ *
+ * 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 _MqStopReloadButton MqStopReloadButton;
+typedef struct _MqStopReloadButtonClass MqStopReloadButtonClass;
+
+#ifndef MQ_TOOLBARS_NAVIGATION_STOP_RELOAD_BUTTON_H
+#define MQ_TOOLBARS_NAVIGATION_STOP_RELOAD_BUTTON_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "../../web-view.h"
+
+G_BEGIN_DECLS
+
+#define MQ_TYPE_STOP_RELOAD_BUTTON \
+ (mq_stop_reload_button_get_type())
+#define MQ_STOP_RELOAD_BUTTON(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), MQ_TYPE_STOP_RELOAD_BUTTON, \
+ MqStopReloadButton))
+#define MQ_IS_STOP_RELOAD_BUTTON(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), MQ_TYPE_STOP_RELOAD_BUTTON))
+#define MQ_STOP_RELOAD_BUTTON_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), MQ_TYPE_STOP_RELOAD_BUTTON, \
+ MqStopReloadButtonClass))
+#define MQ_IS_STOP_RELOAD_BUTTON_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), MQ_TYPE_STOP_RELOAD_BUTTON))
+#define MQ_STOP_RELOAD_BUTTON_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS((obj), MQ_TYPE_STOP_RELOAD_BUTTON, \
+ MqStopReloadButtonClass))
+
+GType
+mq_stop_reload_button_get_type(void);
+
+GtkToolItem *
+mq_stop_reload_button_new(MqWebView *web_view);
+
+G_END_DECLS
+
+#endif /* MQ_TOOLBARS_NAVIGATION_STOP_RELOAD_BUTTON_H */
diff --git a/src/toolbars/navigation/uri-entry.c b/src/toolbars/navigation/uri-entry.c
new file mode 100644
index 0000000..8ae818a
--- /dev/null
+++ b/src/toolbars/navigation/uri-entry.c
@@ -0,0 +1,306 @@
+/*
+ * 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 "uri-entry.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.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 *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);
+}
+
+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 *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",
+ "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/toolbars/navigation/uri-entry.h b/src/toolbars/navigation/uri-entry.h
new file mode 100644
index 0000000..248ba22
--- /dev/null
+++ b/src/toolbars/navigation/uri-entry.h
@@ -0,0 +1,55 @@
+/*
+ * 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_TOOLBARS_NAVIGATION_URI_ENTRY_H
+#define MQ_TOOLBARS_NAVIGATION_URI_ENTRY_H
+
+#include <glib.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_TOOLBARS_NAVIGATION_URI_ENTRY_H */