summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McDermott <pj@pehjota.net>2017-10-12 22:15:07 (EDT)
committer Patrick McDermott <pj@pehjota.net>2017-10-12 22:15:07 (EDT)
commit55aa5c787ec1c05a03f22a9f8f91da9a6c0360eb (patch)
treedacb44c1ad9ddc0775e76aaa9f5ea8040592fe4f
parent1de0210ea13fc18b8cd497ec3501789b7a7fd328 (diff)
downloadmarquee-55aa5c787ec1c05a03f22a9f8f91da9a6c0360eb.zip
marquee-55aa5c787ec1c05a03f22a9f8f91da9a6c0360eb.tar.gz
marquee-55aa5c787ec1c05a03f22a9f8f91da9a6c0360eb.tar.bz2
MqBackForwardButtonBox: New class
-rw-r--r--src/back-forward-button-box.c463
-rw-r--r--src/back-forward-button-box.h59
-rw-r--r--src/local.mk1
3 files changed, 523 insertions, 0 deletions
diff --git a/src/back-forward-button-box.c b/src/back-forward-button-box.c
new file mode 100644
index 0000000..5531f56
--- /dev/null
+++ b/src/back-forward-button-box.c
@@ -0,0 +1,463 @@
+/*
+ * 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 <glib.h>
+#include <gtk/gtk.h>
+#include <webkit2/webkit2.h>
+
+#include "back-forward-button-box.h"
+#include "web-view.h"
+
+struct _MqBackForwardButtonBox {
+ GtkEventBox parent_instance;
+ MqTab *tab;
+ MqWebView *web_view;
+ GtkWidget *back_button;
+ GtkWidget *forward_button;
+ gint back_items;
+ GtkWidget *back_forward_popover;
+};
+
+enum {
+ PROP_TAB = 1,
+ PROP_WEB_VIEW,
+ N_PROPERTIES
+};
+
+static GParamSpec *obj_properties[N_PROPERTIES] = {NULL,};
+
+struct _MqBackForwardButtonBoxClass {
+ GtkEventBoxClass parent_class;
+};
+
+G_DEFINE_TYPE(MqBackForwardButtonBox, mq_back_forward_button_box,
+ GTK_TYPE_EVENT_BOX)
+
+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), back_forward_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),
+ back_forward_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
+constructed(GObject *object)
+{
+ MqBackForwardButtonBox *back_forward_button_box;
+
+ if (G_OBJECT_CLASS(mq_back_forward_button_box_parent_class)->
+ constructed) {
+ G_OBJECT_CLASS(mq_back_forward_button_box_parent_class)->
+ constructed(object);
+ }
+
+ back_forward_button_box = MQ_BACK_FORWARD_BUTTON_BOX(object);
+
+ g_signal_connect(webkit_web_view_get_back_forward_list(
+ WEBKIT_WEB_VIEW(back_forward_button_box->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 *pspec)
+{
+ MqBackForwardButtonBox *back_forward_button_box;
+
+ back_forward_button_box = MQ_BACK_FORWARD_BUTTON_BOX(object);
+
+ switch (property_id) {
+ case PROP_TAB:
+ g_value_set_pointer(value,
+ back_forward_button_box->tab);
+ break;
+ 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,
+ pspec);
+ break;
+ }
+}
+
+static void
+set_property(GObject *object, guint property_id, const GValue *value,
+ GParamSpec *pspec)
+{
+ MqBackForwardButtonBox *back_forward_button_box;
+
+ back_forward_button_box = MQ_BACK_FORWARD_BUTTON_BOX(object);
+
+ switch (property_id) {
+ case PROP_TAB:
+ back_forward_button_box->tab =
+ g_value_get_pointer(value);
+ break;
+ case PROP_WEB_VIEW:
+ back_forward_button_box->web_view =
+ g_value_get_object(value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id,
+ pspec);
+ break;
+ }
+}
+
+static void
+mq_back_forward_button_box_class_init(MqBackForwardButtonBoxClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->constructed = constructed;
+ 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_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;
+
+ /* 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) */
+ gtk_container_add(GTK_CONTAINER(back_forward_button_box), box);
+ g_signal_connect(back_forward_button_box, "button-press-event",
+ G_CALLBACK(event_box_button_press_cb), back_forward_button_box);
+}
+
+GtkWidget *
+mq_back_forward_button_box_new(MqTab *tab, MqWebView *web_view)
+{
+ return g_object_new(MQ_TYPE_BACK_FORWARD_BUTTON_BOX,
+ "tab", tab,
+ "web-view", web_view,
+ NULL);
+}
diff --git a/src/back-forward-button-box.h b/src/back-forward-button-box.h
new file mode 100644
index 0000000..6a57a68
--- /dev/null
+++ b/src/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_BACK_FORWARD_BUTTON_BOX_H
+#define MQ_BACK_FORWARD_BUTTON_BOX_H
+
+#include <gtk/gtk.h>
+
+#include "tab.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);
+
+GtkWidget *
+mq_back_forward_button_box_new(MqTab *tab, MqWebView *web_view);
+
+G_END_DECLS
+
+#endif /* MQ_BACK_FORWARD_BUTTON_BOX_H */
diff --git a/src/local.mk b/src/local.mk
index b3efd88..3968277 100644
--- a/src/local.mk
+++ b/src/local.mk
@@ -6,6 +6,7 @@ marquee_SOURCES += \
%reldir%/window.c \
%reldir%/tab.c \
%reldir%/tab-chrome.c \
+ %reldir%/back-forward-button-box.c \
%reldir%/find-toolbar.c \
%reldir%/web-view.c \
%reldir%/html.c \